На сайте клиента должны появляться все нововведения, пришедшие в обновлениях продукта.
Доработки должны быть максимально изолированы от процесса импорта. Чтобы можно было их отключить, подтвердить, что проблема не у нас на канале, и отправить баг-репорт в саппорт.
Работы по мержингу доработок с обновлённым сайтом должны либо отсутствовать, либо быть минимальными в особо критичных ситуациях.
Из этих требований вытекают условия — совсем не трогать класс CIBlockCMLImport и стараться не трогать catalog.import.1c. Практически во всех наших задачах этого можно достичь с помощью обработчиков.
iblock.OnStartIBlockElementAdd / iblock.OnStartIBlockElementUpdate Знакомые, наверное, почти каждому события. Вызываются перед добавлением/обновлением элемента инфоблока до валидации полей и свойств, первым аргументом передаётся &$arFields. С помощью них можно назначать обязательные, но отсутствующие значения характеристик товаров, давать уникальные символьные коды по своим алгоритмам и менять привязку к разделам.
iblock.OnAfterIBlockElementAdd / iblock.OnAfterIBlockElementUpdate События вызываются после создания/обновления элемента инфоблока. В обработчик первым аргументом передаётся &$arFields. При использовании OnAfterIBlockElementAdd стоит помнить, что оно вызывается в любом случае — хоть при успешно созданном элементе, хоть при ошибке. Поэтому надо обязательно проверять ключ RESULT и корректировать логику в зависимости от него. Через эти обработчики можно производить действия постфактум — создать вспомогательные записи в БД, что-то где-то обновить и т.п. Например, создать элемент в ИБ «Производители» и привязать к нему товар, если в выгрузке значение свойства — строка, а не ссылка на справочник. Или пронаследовать некоторые характеристики всех торговых предложений в основной товар для более быстрой фильтрации в каталоге.
catalog.OnSuccessCatalogImport1C Самое вкусное оставил напоследок. Данное событие появилось около года назад. Вызывается оно в компоненте catalog.import.1c в самом конце импорта — перед отдачей строки success, сигнализирующей 1Ске об успешном окончании обмена. С помощью этого события можно организовать пост-обработку импортированных товаров/ТП - перегенерировать неуправляемый кеш, вычислить диапазон цен для товара с торговыми предложениями, импортировать скидки как нативные битриксовые скидки. Стоит заметить, что на этом шаге весь xml-файл ещё доступен в таблице b_xml_tree, поэтому потенциал события огромен. А если прибавить к этому ещё и возможность реализации пошаговости, то можно сделать свой маленький изолированный импорт без каких-либо модификаций основного так, что 1Ска ничего и не заподозрит:-) Собственно, это событие и его обсуждение тут и сподвигло написать данный пост — показать, как можно на основе этого события реализовать пошаговость. Пример (с подсветкой):
AddEventHandler('catalog', 'OnSuccessCatalogImport1C', 'customCatalogImportStep');
function customCatalogImportStep()
{
$stepInterval = (int) COption::GetOptionString("catalog", "1C_INTERVAL", "-");
$startTime = time();
// Флаг импорта файла торговых предложений
$isOffers = strpos($_REQUEST['filename'], 'offers') !== false;
$NS = &$_SESSION["BX_CML2_IMPORT"]["NS"];
if (!isset($NS['custom']['lastId'])) {
// Последний отработанный элемент для пошаговости.
$NS['custom']['lastId'] = 0;
$NS['custom']['counter'] = 0;
}
// Условия выборки элементов для обработки
$arFilter = array(
'IBLOCK_ID' => 1024,
'ACTIVE' => 'Y',
);
$res = CIBlockElement::GetList(array('ID' => 'ASC'), array_merge($arFilter, array('>ID' => $NS['custom']['lastId'])));
$errorMessage = null;
while ($arItem = $res->Fetch()) {
/*
// Что-нибудь делаем
if (updateElement($arItem['ID']) === false) {
$error = true;
}
*/
if ($error === true) {
$errorMessage = 'Что-то случилось.';
break;
}
$NS['custom']['lastId'] = $arItem['ID'];
$NS['custom']['counter']++;
// Прерывание по времени шага
if ($stepInterval > 0 && (time() - $startTime) > $stepInterval) {
break;
}
}
if ($arItem != false) {
if ($errorMessage === null) {
print "progress\n";
print "Обработано " . $NS['custom']['counter'] . ' элементов, осталось ' . $res->SelectedRowsCount();
} else {
print "failure\n" . $errorMessage;
}
$contents = ob_get_contents();
ob_end_clean();
if (toUpper(LANG_CHARSET) != "WINDOWS-1251") {
$contents = $GLOBALS['APPLICATION']->ConvertCharset($contents, LANG_CHARSET, "windows-1251");
}
header("Content-Type: text/html; charset=windows-1251");
print $contents;
exit;
}
}
В примере происходит выборка из некоего инфоблока и совершение действий над элементами этого инфоблока. Если при обновлении произошла ошибка, 1Ске вернётся статус failure с текстом ошибки. Если всё успешно прошло, происходит выход из обработчика и компонент catalog.import.1c завершает работу (обнуляет сессию и возвращает success).
Заключение Вот как-то так мы и работаем с 1ской — пишем тз, рисуем таблички, генерим примеры и изворачиваемся подобными способами в особых ситуациях:-)
Нейман Андрей, Я создал функцию-обработчик на событие catalog.OnSuccessCatalogImport1C но не могли бы Вы написать как передать данные из b_xml_tree (да и вообще все характеристики и свойства) в функцию-обработчик. Дело в том, что у меня дополнительные картинки, хоть и выгружаются, но не добавляются в свойство MORE_PHOTO
Sadvakasov Dias, Посмотрите сам импорт (CIBlockCMLImport), в принципе, там всё довольно понятно - сначала создайте объект класса CIBlockXMLFile (метод Init()), а потом посмотрите в importMetaData(), как делаются выборки по тегам и иерархии.
Нейман Андрей, Прежде всего, хочу поблагодарить Вас! С помощью данного метода действительно можно получить доступ к моим картинкам. Это я убедился написав в обработчик:
AddEventHandler('catalog', 'OnSuccessCatalogImport1C', 'customCatalogImportStep');
function customCatalogImportStep(){
global $APPLICATION;
$CIBlockXMLFile = new CIBlockXMLFile();
$rs = $CIBlockXMLFile->GetList(array());
while($ar = $rs->Fetch()){
$unser = serialize($ar);
file_put_contents('importcat.txt', $unser, FILE_APPEND);
file_put_contents('importcat.txt', "\n\n", FILE_APPEND);
}
}
В файле с сериализованными массивами я нашел свои картинки, но как более удобно получить к ним доступ. Структуру класса CIBlockCMLImport перечитал раз 5 (4353 строк кода), но до конца не понял как «вытаскиваются» свойства и характеристики. Не в коем случае не прошу готовый код, пойму на словах.
Sadvakasov Dias, весь xml находится в таблице b_xml_tree, посмотрите её структуру и содержимое. Получить информацию по тегу (например, <Товар> можно с помощью гетлиста, указав название тега и/или айди его родителя, например:
Нейман Андрей, У меня все получилось, мой код обработчика http://mysticpaste.com/view/ydODr9dXf2?3 И все бы хорошо, если бы не одно «НО». Когда импортирую 5-7 товаров — то все ОК. Но при импорте целого каталога (раздела) 1С выдает:
Не удалось получить текущее состояние процесса обмена. Данные обмена отправлены, но не загружены.
Обмен не выполнен
Выгрузка товаров завершена с ошибками!!!
Заметил, что если закомментирую участок кода, который изменяет характеристики (с 72 по 83 строки кода) см. код. То все прекрасно! Я думаю может 1С просто не дожидается, пока весь код отработает. Можно ли как-то на шаги разбить, время выгрузки увеличить или причина ошибки в другом, как Вы думаете? Спасибо и с наступающим!
Sadvakasov Dias, потому что вы полностью убрали пошаговость - ключевой момент успешного выполнения своей обработки при импорте. Заготовка для этой пошаговости и приведена в моём посте.
подскажите, а как в OnSuccessCatalogImport1Cполучить id инфоблока товаров и торговых предложений? Цель, выбрать товары без торговых предложений и при нулевом количестве отключать. А товары где есть торговые предложения, смотреть количество в торговых предложениях и выключать соответственно торговые предложения а не товаы.
Добрый день, Нейман Андрей, если не сложно подскажи, пожалуйста, как должен выглядеть код, который при загрузке из 1с с помощью событий OnBeforeIBlockElementAdd и OnBeforeIBlockElementUpdateв файле init.php добавит в отдельный ИБ производителя и привяжет этого производителя к товару через свойство "привязка к элементам", интернет-магазин без торговых предложений. Стандартно при загрузке из 1с создается свойство производитель с типом "список" и с кодом CML2_MANUFACTURER. Спасибо!
Полезный код! Последнее время импорт с 1С претерпел значительные изменения. По сути обработка и дальше продолжается через компонент catalog.import.1c . Поэтому есть надежда, что даже при импорте в режиме Real-time событие будет отрабатывать... Правильно я думаю? Тогда наверное нужно отрезать обработку в событии при режиме Real-time (listen)? Иначе при каждом создании нового заказа будет запускатся тяжелый скрипт обработки. Хотя в режиме Real-time могут приходить новые изменения в каталог...
Подскажите, пожалуйста, не совсем понял, как в данном примере реализована пошаговость, точнее, где условие выхода из этой пошаговости, как устанавливается, что скрипту пора остановиться
Группы на сайте создаются не только сотрудниками «1С-Битрикс», но и партнерами компании. Поэтому мнения участников групп могут не совпадать с позицией компании «1С-Битрикс».