Столкнулся с проблемой корректировки цеы товара через обработчики событий в 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С, и при работе через админку.
Всем успехов
