
[spoiler]
Ларчик открывается просто, достаточно схематично рассмотреть, как происходит сохранение элемента в форме редактирования.
Итак, Вы нажали кнопку "Сохранить" в форме. Данные отправились на сервер, выполняется проверка переданных данных, относящихся к элементу инфоблока, затем (если инфоблок является торговым каталогом) - проверка валидности цен и данных, относящихся к товару. Проверка прошла успешно, начинается сохранение (или обновление) данных:
- Вызывается CIBlockElement::Add или CIBlockElement::Update. Данные заносятся в базу, вызывается пользовательский обработчик событий, изменяет нужный тип цен.
- Вызывается CCatalogProduct::Add или CCatalogProduct::Update - сохранение свойств товара
- Сохраняются цены и удаляются те, которых не было в форме редактирования.
Какой же может быть выход? Если использование обработчика необходимо - его можно усложнить, встроив проверку адреса текущей страницы. Если это адрес формы редактирования - никаких действий не выполнять. А требуемый функционал в форме редактирования реализовать через функцию BXIBlockAfterSave. В настройках инфоблока есть поле "Файл для редактирования элемента, позволяющий модифицировать поля перед сохранением". Путь к файлу с функцией необходимо прописать в этом поле. Минус этого решения - в функцию передаются только поля и свойства элемента, но не цены. Выход - либо брать их из базы, либо работать с массивом $_POST.
UPD. Подобная же проблема может возникнуть в скриптах импорта (csv, 1C). В этом случае решение одно - использовать обработчики класса CPrice.
Решил задачу через жесткий костыль: OnAfterIBlockElementUpdate генерит файлик с id нужного элемента, после чего проверяем наличие данных в этом файле, и если есть то CPrice::SetBase... чистим файл.
Как-то так. Главное работает, как часы!
К примеру в Контент/Каталоги/Продукция/Товары/ товар -> Торговый каталог/Параметры поле "Доступное количество" изменяется поле "Количество товара" на вкладке Склады.
Нужно реализовать, чтобы также отрабатывало при импорте товаров. Тойсть я выгружаю CSV обновляется поле "Доступное количество", а дальше через функцию BXIBlockAfterSave обновляем значения поля "Количество товара".
Подскажите как это можно реализовать.
Как быть?
AddEventHandler("catalog", "OnProductUpdate", "FucOnProductUpdate");
function FucOnProductUpdate($ID, $arFields){
$arOrder = array("SORT"=>"ASC");
$arFilter = array("ID" => $ID);
$arGroupBy = false;
$arNavStartPs = false;
$arSelectFields = array("QUANTITY");
$arRes = CCatalogProduct::GetList($arOrder,$arFilter,$arGroupBy,$arNavStartPs,$arSelectFields)->fetch(); //текущие количество на складе
$arFieldsUpdate = Array("PRODUCT_ID" => $ID,"STORE_ID" => 1, "AMOUNT" => $arRes["QUANTITY");
$arOrderID = array("SORT"=>"ASC");
$arFilterID = array("PRODUCT_ID" => $ID, "STORE_ID" => 1);
$arGroupByID = false;
$arNStPsID = false;
$arSelectFieldsID = array("ID","PRODUCT_ID"); //ID - записи
$arResID = CCatalogStoreProduct::GetList($arOrderID,$arFilterID,$arGroupByID,$arNStPsID,$arSelectFieldsID)->fetch();
if($arResID == false){
$arFieldsADD = Array("PRODUCT_ID" => $ID, "STORE_ID" => 1, "AMOUNT" => $arRes["QUANTITY");
$ID_ADD = CCatalogStoreProduct::Add($arFieldsADD);
}else{
$arFieldsUpdate = Array("PRODUCT_ID" => $ID,"STORE_ID" => 1, "AMOUNT" => $arRes["QUANTITY");
$r = CCatalogStoreProduct::Update($arResID["ID"], $arFieldsUpdate);
}
}
Когда я изменяю поле "Доступное количество" у меня не обновляется поле "Количество товара" на вкладке Склад.
В чем может быть причина?
Вот мой файл BXIBlockAfterSave:
function BXIBlockAfterSave(&$arFields) { static $use_handler = true; if ($use_handler) { $use_handler = false; $PRODUCT_ID = $arFields['ID']; CPrice::DeleteByProduct($PRODUCT_ID); $arField = Array( "PRODUCT_ID" => $PRODUCT_ID, "CATALOG_GROUP_ID" => 1, "PRICE" => 1, // сюда цену. у всех товаров цена 1 руб. "CURRENCY" => "RUB" ); if(CCatalogProduct::Add(array('ID'=>$PRODUCT_ID))){ CPrice::SetBasePrice($arFields['ID'], 1, 'RUB'); $res = CPrice::GetList(array(),array("PRODUCT_ID" => $PRODUCT_ID, "CATALOG_GROUP_ID" => 1)); if ($arr = $res->Fetch()) { CPrice::Update($arr["ID"], $arField); } else { CPrice::Add($arField); } $use_handler = true; //CPrice::Update($arFields['ID'], $arField); } } }- работает отменно, цена обновляется после обновления страницы элемента товара!
А так выглядит мой обработчик событий init.php:
<? use Bitrix\Highloadblock\HighloadBlockTable; use Bitrix\Main\Loader; use \Bitrix\Iblock\PropertyIndex; Loader::includeModule('highloadblock'); Loader::includeModule('iblock'); AddEventHandler("iblock", "OnStartIBlockElementAdd ", "OnAfterIBlockElementAddHandler"); AddEventHandler("iblock", "OnStartIBlockElementUpdate", "OnAfterIBlockElementAddHandler"); //AddEventHandler('catalog', 'OnSuccessCatalogImport1C', array('MyClass','customCatalogImportStep')); //AddEventHandler("iblock", "OnBeforeIBlockAdd", "AddElementOrSectionCode"); //AddEventHandler("iblock", "OnBeforeIBlockUpdate", "AddElementOrSectionCode"); AddEventHandler("iblock", "OnBeforeIBlockElementAdd", "AddElementOrSectionCode"); AddEventHandler("iblock", "OnBeforeIBlockElementAdd", "OnAfterIBlockElementAddHandler"); AddEventHandler("iblock", "OnBeforeIBlockElementUpdate", "AddElementOrSectionCode"); AddEventHandler("iblock", "OnBeforeIBlockElementUpdate", "OnAfterIBlockElementAddHandler"); AddEventHandler("iblock", "OnBeforeIBlockSectionAdd", "AddElementOrSectionCode"); AddEventHandler("iblock", "OnBeforeIBlockSectionUpdate", "AddElementOrSectionCode"); AddEventHandler("sale", "OnOrderNewSendEmail", "bxModifySaleMails"); AddEventHandler("sale", "OnOrderAdd", "OnOrderAdd_mail"); AddEventHandler("catalog", "OnStoreProductUpdate", array("MyClass", "OnStoreProductUpdateHandler")); //изменение товара на складе AddEventHandler("catalog", "OnStoreProductAdd", array("MyClass", "OnStoreProductUpdateHandler")); //добавление товара на склад AddEventHandler("catalog", "OnProductUpdate", "notInStore"); //событие, для того, что бы вытащить данные по товару class MyClass { protected static $handlerDisallow = false; public static function OnStoreProductUpdateHandler($ID, $arFields) { CModule::IncludeModule("iblock"); CModule::IncludeModule("sale"); CModule::IncludeModule("catalog"); if (self::$handlerDisallow) return; self::$handlerDisallow = true; function notInStore($ID,$Fields){ $ar_res = CCatalogProduct::GetByIDEx($ID); //получаем данные товара по ID $PRODUCT_ID = $ar_res['ID']; // $ID = прихода товара на склад $price_2 = 1; //у всех товаров цена 1 руб. $PRICE_TYPE_ID = 1; // оставляем для первого типа товаров. $arFields = Array( "PRODUCT_ID" => $PRODUCT_ID, "CATALOG_GROUP_ID" => $PRICE_TYPE_ID, "PRICE" => $price_2, // сюда цену. "CURRENCY" => "RUB", "QUANTITY_FROM" => false, "QUANTITY_TO" => false ); $res = CPrice::GetList( array(), array( "PRODUCT_ID" => $PRODUCT_ID, "CATALOG_GROUP_ID" => $PRICE_TYPE_ID ) ); if ($arr = $res->Fetch()) { CPrice::Update($arr["ID"], $arFields); } else { CPrice::Add($arFields); } } } } function OnOrderAdd_mail($ID, $val) { addmessage2log($val); } function bxModifySaleMails($orderID, &$eventName, &$arFields) { if (empty($arFields['ORDER_LISTR'])) { return false; } } function AddElementOrSectionCode(&$arFields) { $params = array( "max_len" => "100", "change_case" => "L", "replace_space" => "_", "replace_other" => "_", "delete_repeat_replace" => "true", "use_google" => "false", ); if (strlen($arFields["NAME"])>0 && strlen($arFields["CODE"])<=0) { $arFields['CODE'] = CUtil::translit($arFields["NAME"].rand(), "ru", $params); } } function OnAfterIBlockElementAddHandler(&$arFields) { static $use_handler = true; if ($use_handler) { $use_handler = false; $PRODUCT_ID = $arFields['ID']; CPrice::DeleteByProduct($PRODUCT_ID); $arField = Array( "PRODUCT_ID" => $PRODUCT_ID, "CATALOG_GROUP_ID" => 1, "PRICE" => 1, // сюда цену закупочную. "CURRENCY" => "RUB" ); if(CCatalogProduct::Add(array('ID'=>$PRODUCT_ID))){ CPrice::SetBasePrice($PRODUCT_ID, 1, 'RUB'); $res = CPrice::GetList(array(),array("PRODUCT_ID" => $PRODUCT_ID, "CATALOG_GROUP_ID" => 1)); if ($arr = $res->Fetch()) { CPrice::Update($arr["ID"], $arField); } else { CPrice::Add($arField); } $use_handler = true; //CPrice::Update($arFields['ID'], $arField); } } } // регистрируем обработчик AddEventHandler("search", "BeforeIndex", "BeforeIndexHandler"); // создаем обработчик события "BeforeIndex" function BeforeIndexHandler($arFields) { if(!CModule::IncludeModule("iblock")) // подключаем модуль return $arFields; if($arFields["MODULE_ID"] == "iblock") { $db_props = CIBlockElement::GetProperty( // Запросим свойства индексируемого элемента $arFields["PARAM2"], // BLOCK_ID индексируемого свойства $arFields["ITEM_ID"], // ID индексируемого свойства array("sort" => "asc"), // Сортировка (можно упустить) Array("CODE"=>"CML2_ARTICLE")); // CODE свойства (в данном случае артикул) if($ar_props = $db_props->Fetch()) $arFields["TITLE"] .= " ".$ar_props["VALUE"]; // Добавим свойство в конец заголовка индексируемого элемента } return $arFields; // вернём изменения } AddEventHandler("iblock", "OnBeforeIBlockElementUpdate","SaveMySection"); function SaveMySection(&$arFields) { if (@$_REQUEST['mode']=='import')//импорт из 1с? { $ss = 0; $db_old_groups = CIBlockElement::GetElementGroups($arFields['ID'], true); while($ar_group = $db_old_groups->Fetch()) { if(!in_array($ar_group['ID'],$arFields['IBLOCK_SECTION'])) { $arFields['IBLOCK_SECTION'][$ss] = $ar_group['ID']; $ss++; } } sort($arFields ['IBLOCK_SECTION'], SORT_NUMERIC); $arFields ['IBLOCK_SECTION'] = array_pop($arFields['IBLOCK_SECTION']); //$el = new CIBlockElement; //$res1 = $el->Update($arFields['ID'], $arLoadProductArray); // CIBlockElement::SetElementSection($arFields['ID'], $arFields['IBLOCK_SECTION']); // $_AE[$arFields['ID']] = $arLoadProductArray; //unset($arLoadProductArray); //PropertyIndex\Manager::updateElementIndex($IBLOCK_ID, $arFields['ID']); } } ?>- обработчик не отрабатывает добавление цены.
Подскажите что надо исправить в init.php?
В Вашем коде для init.php нет обработчиков событий класса CPrice.
AddEventHandler("catalog", "OnPriceAdd", "OnAfterIBlockElementAddHandler";); AddEventHandler("catalog", "OnPriceUpdate", "OnAfterIBlockElementAddHandler";);Выяснилось, что цена добавилась только у ~ 12 000 элементов.
Скорей всего на сервере не хватает памяти. Будем увеличивать.
Вопрос: Как с помощью скрипта добавить цену остальным элементам?
Если делать через:
$res = CIBlockElement:: GetList и
while ($ob = $res->GetNextElement()) { с проверкой наличия уже проставленной цены.
то не повешу ли я БД.
Как сделать добавление цены большому количеству элементов, при этом наблюдать процесс работы скрипта, чтобы знать работает он ещё или уже закончил работу, или завис??
Я сделал так: в init.php создал статическую переменную $dontDelete, сохраняю в нее ID цены, добавленной с помощью CPrice::Add() (это происходит в обработчике событий OnPriceUpdate/OnPriceAdd). В обработчике события OnBeforeProductPriceDelete проверяю эту переменную, если она не содержится в массиве $arr, то добавляю ее туда и все проходит успешно – новая цена не удаляется.
AddEventHandler("catalog", "OnBeforeProductPriceDelete", array("MyClass", "OnBeforeProductPriceDeleteHandler")); class MyClass { protected static $dontDelete = 0; function OnBeforeProductPriceDeleteHandler($id, &$arr = null) { if(!in_array(self::$dontDelete, $arr)) { $arr[] = self::$dontDelete; } return true; } ... //где-то в обработчике OnPriceUpdate/OnPriceAdd создаем новую цену для товара self::$dontDelete = CPrice::Add($arNewPrice); ...