Комплекты товаров

введение

Часто в магазинах продаются составные товары: когда на скаладе или в магазине товар представлен составляющими, а продается он как одно ценлое. В CMS 1с-Битрикс для подобных ситуаций уже реализован функционал "комплекты", начиная с версии 12. Для этого предусмотрен особый тип элемента инфоблока - "комплект", для которого на отдельной вкладке можно указать товары, входящие в набор, их количество и цену в наборе(с помощью скидки от цены). Тем не менее их работа не соответствовала описанию: количество товаров определялось некорректно. А реализовать функционал для клиента требовалось уже сейчас. Таким образом было решено написать недостающую часть.

После продолжительного тестирования имеющегося функционала и определения недостатков, было решено сделать следующее:

  1. Написать обработчик - если мы сохраняем/обновляем товар-комплект, то кол-во товара должно прописаться автоматом и быть равно тому минимальному кол-ву комплектов, которое можно собрать из составляющих комплекта.
  2. Цена комплекта должна выставиться из цен составляющих с учетом скидки, прописанной в составе комплекта.
  3. Затем нужно привязаться к событию изменения количества товаров и если данные товары входят в комплект - проставить кол-во комплекта исходя из новых реалий.

Таким образом решено разработать функцию, выполняющую пересчет цены при создании или изменении элемента инфоблока типа "комплект". Соответственно решение задачи состоит из двух этапов. Для начала надо разработать саму функцию, а затем выбрать события к которым она будет привязана.

Разработка функции countParams($ID)

Итак, нам нужна функция, которая пересчитает количество и цену комплекта с идентификатором $ID. Очевидно, что у нас будет один входной параметр - ид товара-комплекта. Функция должна выполнять следующее: получить товары входящие в комплект и вычислить доспустимое количество и цену. Поэтому функция будет возваращать массив из этих двух величин или false если это не набор или произошла ошибка. Проблемой является то, что функции работы с комплектами пока не документированны. Но у нас уже есть функционал для получения товаров, входящих в комплект. Он реализован в административной части сайта при редактировании товара. Оттуда узнаем функцию $arSets = CCatalogProductSet::getAllSetsByProduct($intProductID, CCatalogProductSet::TYPE_SET); которая получает массив товаров.

Затем остается только получить массив элементов с помощью стандартного getlist, пробежаться по нему и определить необходимые параметры.

Подключение функционала

Эта часть немного сложнее. Нам нужно обработать следующие события: создание и изменение элемента, при этом если нам придется изменять количество товара, то событие изменения товара сработает еще раз. При изменении товара возможны 3 ситуации: изменился товар, изменился комплект, изменился товар, входящий в комплект. Если меняется простой товар, делать ничего не нужно. Если меняется комплект, то меняется и набор, поэтому можно подключитсься на событие "изменение комплекта".(Это событие также недокумментированно, но логика подсказывает, что оно должно быть, что и было успешно подтверждено с помощью модуля live.API). На найденное событие OnProductSetUpdate ставим обработчик, который пересчитает параметры(с помощью описанной раннее функции). ID комплекта передается в глобальной переменной. Это нужно для того, чтобы отличать изменение товара, от срабатывания события после функции update.

Если меняется товар, входящий в комплект, то сначала нужно определить ид комплекта. Затем пересчитать параметры комплекта в том же событии и изменить количество и цену комплекта этого товара.

Определение типа товара

Так получилось, что события и функции связанные с комплектами недокументированы. При поиске нужных функций очень помог модуль live.API. Одну из функций мы узнали из исходного кода административной части. Это послужило определенной подсказкой. Итак, инструментарий.

  1. функция $arSets = CCatalogProductSet::getAllSetsByProduct($intProductID, CCatalogProductSet::TYPE_SET); позволяет получать массив товаров, входящих в комплект.
  2. 2) CCatalogProductSet::isProductInSet($ID) определяет, является ли товар $ID комплектом или товаром с комплектом. Иными словами, возвращает false для обычного товара.
  3. 3) CCatalogProductSet::isPrInSet($intProductID, $intSetType = 0) определяет, что перед нами: товар с комплектом или комплект. По сути представляет собой запрос к БД комплектов, поэтому была модифицированна и стала возвращать номер комплекта и владельца комплекта вместо простой логической переменной.

Добавление элемента

При добавлении элемента возможна только одна ситуация которая нам интересна: добавление комплекта.(т.к. нельзя создать товар и сразу же добавить его в комплект). Поэтому можем подключаться к событию OnProductSetAdd. Нам нужно узнать ид элемента, к которому будет привязан комплект, а затем событие полностью аналогично изменению комплекта, поэтому мы просто можем вызвать уже имеющийся обработчик. А id товара-хозяина комплекта хранится в свойствах элементов

Ошибка при добавлением заказа

Ура! Функционал готов, тестируем... Получаем еще одну ошибку. При оформлении заказа пересчет (списание) количества товаров не происходит. После пройденного это кажется всего лишь пятиминутным затруднением. Предполагается, что эта ошибка связана с тем, что товары с комплектами обрабатываются отельно от других типов товаров и поэтому стандартные события не срабатывают. Решение - простое. Подключаемся к событию OnBasketOrder - событию привязки корзины к заказу - и получаем idшники и количество товаров. Затем смотрим какие из товаров входят в комплекты и для этих товаров выставляем верное количество. Затем запустятся стандарнтные события и количесво комплектов тоже пересчитается.