+7 495 008 8452 пн.-пт. 10:00 – 17:00
Если у вас возникли какие либо вопросы которые вы не смогли решить по нашим публикациям самостоятельно,
то ждем ваше обращение в нашей службе тех поддержки.


Щадящая кастомизация импорта из 1С

Вступление
Не знаю, как у вас, а у нас самым проблемным местом при разработке интернет-магазина на битриксе является… Так и хочется сказать «битрикс», но нет — это интеграция с 1С:-) Проблемы эти можно свести в одно предложение: «Нам на сайте нужно то-то, но мы можем выгрузить только так, а не как вы просите». Или «Да у нас отличный 1С-программист, конечно сможем!», а через неделю слышится уже что-то вроде «ну не шмогла я, не шмогла». И тогда нам приходится работать с тем, что дают, подстраивая стандартные алгоритмы интеграции с 1С под свои нужды (здесь и далее разговор идёт о CommerceML).

Много лет назад при появлении необходимости модификации процесса обработки xml-файла или импорта товаров мы просто брали класс CIBlockCMLImport, копировали его и накручивали свою логику. Быстро, просто, эффективно ©. До определённого времени — пока Битрикс не анонсирует какую-то новую фичу, которая станет клиенту необходима. Или не вылезет баг, унаследованный от оригинального класса. Методом недолгих проб и ошибок у нас появились решения, удовлетворяющие следующим требованиям:
  • На сайте клиента должны появляться все нововведения, пришедшие в обновлениях продукта.
  • Доработки должны быть максимально изолированы от процесса импорта. Чтобы можно было их отключить, подтвердить, что проблема не у нас на канале, и отправить баг-репорт в саппорт.
  • Работы по мержингу доработок с обновлённым сайтом должны либо отсутствовать, либо быть минимальными в особо критичных ситуациях.
Из этих требований вытекают условия — совсем не трогать класс 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ской — пишем тз, рисуем таблички, генерим примеры и изворачиваемся подобными способами в особых ситуациях:-)

Назад в раздел

Подписаться на новые материалы раздела:














CAPTCHA