Часто в магазинах продаются составные товары: когда на скаладе или в магазине товар представлен составляющими, а продается он как одно ценлое. В CMS 1с-Битрикс для подобных ситуаций уже реализован функционал "комплекты", начиная с версии 12. Для этого предусмотрен особый тип элемента инфоблока - "комплект", для которого на отдельной вкладке можно указать товары, входящие в набор, их количество и цену в наборе(с помощью скидки от цены). Тем не менее их работа не соответствовала описанию: количество товаров определялось некорректно. А реализовать функционал для клиента требовалось уже сейчас. Таким образом было решено написать недостающую часть.
После продолжительного тестирования имеющегося функционала и определения недостатков, было решено сделать следующее:
Таким образом решено разработать функцию, выполняющую пересчет цены при создании или изменении элемента инфоблока типа "комплект". Соответственно решение задачи состоит из двух этапов. Для начала надо разработать саму функцию, а затем выбрать события к которым она будет привязана.
Итак, нам нужна функция, которая пересчитает количество и цену комплекта с идентификатором $ID. Очевидно, что у нас будет один входной параметр - ид товара-комплекта. Функция должна выполнять следующее: получить товары входящие в комплект и вычислить доспустимое количество и цену. Поэтому функция будет возваращать массив из этих двух величин или false если это не набор или произошла ошибка. Проблемой является то, что функции работы с комплектами пока не документированны. Но у нас уже есть функционал для получения товаров, входящих в комплект. Он реализован в административной части сайта при редактировании товара. Оттуда узнаем функцию $arSets = CCatalogProductSet::getAllSetsByProduct($intProductID, CCatalogProductSet::TYPE_SET); которая получает массив товаров.
Затем остается только получить массив элементов с помощью стандартного getlist, пробежаться по нему и определить необходимые параметры.
Эта часть немного сложнее. Нам нужно обработать следующие события: создание и изменение элемента, при этом если нам придется изменять количество товара, то событие изменения товара сработает еще раз. При изменении товара возможны 3 ситуации: изменился товар, изменился комплект, изменился товар, входящий в комплект. Если меняется простой товар, делать ничего не нужно. Если меняется комплект, то меняется и набор, поэтому можно подключитсься на событие "изменение комплекта".(Это событие также недокумментированно, но логика подсказывает, что оно должно быть, что и было успешно подтверждено с помощью модуля live.API). На найденное событие OnProductSetUpdate ставим обработчик, который пересчитает параметры(с помощью описанной раннее функции). ID комплекта передается в глобальной переменной. Это нужно для того, чтобы отличать изменение товара, от срабатывания события после функции update.
Если меняется товар, входящий в комплект, то сначала нужно определить ид комплекта. Затем пересчитать параметры комплекта в том же событии и изменить количество и цену комплекта этого товара.
Так получилось, что события и функции связанные с комплектами недокументированы. При поиске нужных функций очень помог модуль live.API. Одну из функций мы узнали из исходного кода административной части. Это послужило определенной подсказкой. Итак, инструментарий.
При добавлении элемента возможна только одна ситуация которая нам интересна: добавление комплекта.(т.к. нельзя создать товар и сразу же добавить его в комплект). Поэтому можем подключаться к событию OnProductSetAdd. Нам нужно узнать ид элемента, к которому будет привязан комплект, а затем событие полностью аналогично изменению комплекта, поэтому мы просто можем вызвать уже имеющийся обработчик. А id товара-хозяина комплекта хранится в свойствах элементов
Ура! Функционал готов, тестируем... Получаем еще одну ошибку. При оформлении заказа пересчет (списание) количества товаров не происходит. После пройденного это кажется всего лишь пятиминутным затруднением. Предполагается, что эта ошибка связана с тем, что товары с комплектами обрабатываются отельно от других типов товаров и поэтому стандартные события не срабатывают. Решение - простое. Подключаемся к событию OnBasketOrder - событию привязки корзины к заказу - и получаем idшники и количество товаров. Затем смотрим какие из товаров входят в комплекты и для этих товаров выставляем верное количество. Затем запустятся стандарнтные события и количесво комплектов тоже пересчитается.