Столкнулся с проблемой корректировки цеы товара через обработчики событий в init.php. В итоге, путем длительных мучений, сообщаю наиболее супер-универсальное решение данной задачи.
Шаг №1. Создаем обработчик в init.php
Здесь важными являются следующие моменты:
Можно сказать, что наиболее нормальным набором является:
Шаг №3. Супер-секретная функция BXIBlockAfterSave
Без этого шага при сохранении товара через админку ничего не работало, цены обнулялись. Как объясняла ТП Битрикса, оказывается, события, которые я перечислил на шаге №2, не являются окончательными в процессе сохранения элемента... То есть, после них еще что-то происходит неведомое, что перечеркивает работу нашего обработчика... И единственным способом уже точно-точно выполнить обработчик в самом конце сохранения элемента является не особо документированная функция BXIBlockAfterSave... Если ее декларировать в файле обработчика элемента инфоблока перед сохранением (в настройках инфоблока есть такое поле - "Файл для редактирования элемента, позволяющий модифицировать поля перед сохранением"), то она выполнится в самом-самом конце процесса...
Таким образом, создаем файлик iblock_element_edit_before_save.php.
Пишем в него код:
Кладем файлик в /bitrix/php_interface/include и прописываем на него путь в настройках инфоблока ("Файл для редактирования элемента, позволяющий модифицировать поля перед сохранением"): /bitrix/php_interface/include/iblock_element_edit_before_save.php
ИТОГ
Данное решение обеспечит 100% установку нужной цены на товар, в том числе и при импорте из 1С, и при работе через админку.
Всем успехов
Шаг №1. Создаем обработчик в init.php
function IBlockElementAfterSaveHandler($arg1, $arg2 = false) { static $use_handler = true; if ($use_handler) { CModule::IncludeModule("iblock"); CModule::IncludeModule("catalog"); $ELEMENT_ID = false; if (is_array($arg2) && $arg2["PRODUCT_ID"] > 0) { $ELEMENT_ID = $arg2["PRODUCT_ID"]; } elseif (is_array($arg1) && $arg1["ID"] > 0) { $arElementFields = CIBlockElement::GetByID($arg1["ID"])->GetNext(); if(in_array($arElementFields["IBLOCK_ID"],array(2,3))) { $ELEMENT_ID = $arg1["ID"]; } } if($ELEMENT_ID) { $price = false; ... тут код, который формирует значение переменной $price ... if($price) { $use_handler = false; CPrice::DeleteByProduct($ELEMENT_ID); $arPriceFields = array( "PRODUCT_ID" => $ELEMENT_ID, "CATALOG_GROUP_ID" => 1, "PRICE" => intval($price), "CURRENCY" => 'RUB', "QUANTITY_FROM" => 1, // это опционально, если хочется выставлять диапазоны цен в зависимости от количества "QUANTITY_TO" => ... // это опционально, если хочется выставлять диапазоны цен в зависимости от количества ); CPrice::Add($arPriceFields); $use_handler = true; } } } } |
- Благодаря двум аргументам функции IBlockElementAfterSaveHandler($arg1, $arg2 = false) она будет хорошо отрабатывать входные данные от событий как модуля "Инфоблоки", так и модуля "Каталог". В первом случае если заполнен $arg1, а во-втором - если $arg2
- Если мы обрабатываем события модуля "Инфоблоки", я дополнительно выясняю ID инфоблока и его принадлежность к инфоблокам с товарами (в моем случае in_array(2,3) - то есть инфоблоки с ID 2 и 3 у меня могут быть каталогами). Важно, что ID инфоблока надо именно выцеплять через API, так как он не будет передаваться в массиве $arg1, если мы делаем вызов через BXIBlockAfterSave! (см. шаг №3)
- Статическая переменная $use_handler используется для предотвращения зацикливания обработчика, потому что он у нас будет вызываться на события обновления/добавления цен, а при этом сам делает эти же действия (CPrice::Add)
- Логика установки цены на товар может быть другой, в моем случае подходит упрощение, что мы изначально сносим все цены (CPrice:DeleteByProduct), а потом устанавливаем нужную новую цену или цены
Можно сказать, что наиболее нормальным набором является:
AddEventHandler("iblock", "OnAfterIBlockElementUpdate", "IBlockElementAfterSaveHandler"); AddEventHandler("iblock", "OnAfterIBlockElementAdd", "IBlockElementAfterSaveHandler"); AddEventHandler("catalog", "OnPriceAdd", "IBlockElementAfterSaveHandler"); AddEventHandler("catalog", "OnPriceUpdate", "IBlockElementAfterSaveHandler"); AddEventHandler("catalog", "OnProductUpdate", "IBlockElementAfterSaveHandler"); |
Без этого шага при сохранении товара через админку ничего не работало, цены обнулялись. Как объясняла ТП Битрикса, оказывается, события, которые я перечислил на шаге №2, не являются окончательными в процессе сохранения элемента... То есть, после них еще что-то происходит неведомое, что перечеркивает работу нашего обработчика... И единственным способом уже точно-точно выполнить обработчик в самом конце сохранения элемента является не особо документированная функция BXIBlockAfterSave... Если ее декларировать в файле обработчика элемента инфоблока перед сохранением (в настройках инфоблока есть такое поле - "Файл для редактирования элемента, позволяющий модифицировать поля перед сохранением"), то она выполнится в самом-самом конце процесса...
Таким образом, создаем файлик iblock_element_edit_before_save.php.
Пишем в него код:
<? function BXIBlockAfterSave($arFields) { IBlockElementAfterSaveHandler($arFields); } ?> |
ИТОГ
Данное решение обеспечит 100% установку нужной цены на товар, в том числе и при импорте из 1С, и при работе через админку.
Всем успехов