Yandex Metrika
sanches.free 2 просмотра

Дополнительные поля при импорте CommerceML из 1С: разбор b_xml_tree

Зачем выносить обработку в отдельный класс

Стандартный импорт «1С ↔ сайт» кладёт XML во временную структуру b_xml_tree. Пока идёт сессия, дерево сохраняет глубину, имена узлов и значения так же, как в исходном файле — это проще искать нужные элементы через SQL joins по LEFT_MARGIN и RIGHT_MARGIN, чем парсить сырой документ второй раз. Отдельный класс можно вызвать с последнего шага обработчика режима каталога, когда загружается файл похожий на offers.xml, и уже там обновить произвольные свойства торговых предложений через API инфоблока.

Клон компонента и точка входа

Копируют штатный catalog.import.1c, подключают кастомную версию вместо типовой, и перед переключением сессионного состояния на следующий индекс вставляют проверку имени входящего файла. Ниже — иллюстрация проверки: после финального сообщения об импорте для потока предложений выполняется собственная процедура (имена символических кодов свойств заменены на свой пример).

$exchangeDoneMessage = \Bitrix\Main\Localization\Loc::getMessage("CC_BSC1_DEA_DONE");
$exchangeSession["STEP"] = 9;
if (
    isset($_GET["filename"])
    && $_GET["filename"] === 'offers.xml'
) {
    OffersMarginSync::applyFromStagingTree();
}

Важная деталь: индекс девятого шага опирается на ту же машину состояний, что описывают материалы по обмену CommerceML для Битрикс; её стоит сверить на актуальной версии платформы, прежде чем выкладывать на боевой контур.

Чтение дерева и запись свойства по внешнему ключу

Метод сначала убеждается, что в корне блока оффера ожидаемый текст справочника («пакет предложений»), затем собирает пары «внешний идентификатор торгового предложения» — «цифровое значение поля учёта» из вложенных узлов через один запрос со соединением по дереву на глубину предложений. Дальше остаётся найти элемент в каталоге по XML_ID и передать строку в свойство с кодом, например WHOLESALE_MARGIN. Ниже — черновая схема на API ядра с соединением b_xml_tree; глубину уровней и имена узлов подставляют под фактический XML из своей конфигурации 1С.

declare(strict_types=1);

final class OffersMarginSync
{
    private const CATALOG_IBLOCK_ID = 10;

    public static function applyFromStagingTree(): void
    {
        $connection = \Bitrix\Main\Application::getConnection();
        $snapshot = $connection->query(
            "SELECT VALUE FROM b_xml_tree "
            . "WHERE DEPTH_LEVEL = 2 AND NAME = 'Наименование'"
        )->fetch();
        if (!$snapshot || $snapshot["VALUE"] !== 'Пакет предложений') {
            return;
        }

        $rows = $connection->query(
            "SELECT idNode.VALUE AS CRM_CODE, qtyNode.VALUE AS MARGIN_PCT"
            . " FROM b_xml_tree AS skuNode"
            . " INNER JOIN b_xml_tree AS qtyNode ON ("
            . "  qtyNode.LEFT_MARGIN BETWEEN skuNode.LEFT_MARGIN AND skuNode.RIGHT_MARGIN"
            . "  AND qtyNode.DEPTH_LEVEL = 6 AND qtyNode.NAME = 'Ставка'"
            . ")"
            . " INNER JOIN b_xml_tree AS idNode ON ("
            . "  idNode.LEFT_MARGIN BETWEEN skuNode.LEFT_MARGIN AND skuNode.RIGHT_MARGIN"
            . "  AND idNode.DEPTH_LEVEL = 4 AND idNode.NAME = 'Ид'"
            . ")"
            . " WHERE skuNode.DEPTH_LEVEL = 3 AND skuNode.NAME = 'Предложение'"
        );

        if (!\Bitrix\Main\Loader::includeModule("iblock")) {
            return;
        }

        $order = ["SORT" => "ASC"];
        $columns = ["ID", "XML_ID", "NAME"];
        $predicate = ["IBLOCK_ID" => self::CATALOG_IBLOCK_ID];

        while ($exchangeRow = $rows->fetch()) {
            $predicate["=XML_ID"] = $exchangeRow["CRM_CODE"];
            $catalogCursor = \CIBlockElement::GetList(
                $order,
                $predicate,
                false,
                false,
                $columns
            );
            if ((int)$catalogCursor->SelectedRowsCount() === 0) {
                continue;
            }
            $elementPayload = $catalogCursor->Fetch();
            \CIBlockElement::SetPropertyValues(
                (int)$elementPayload["ID"],
                self::CATALOG_IBLOCK_ID,
                $exchangeRow["MARGIN_PCT"],
                'WHOLESALE_MARGIN'
            );
        }
    }
}

На что посмотреть при отладке

  • Уточнять фильтры =XML_ID и код свойства перед выкладкой — несуществующее свойство даст предупреждение, но не остановит сценарий.
  • Если нужных строк вообще нет, проще временно сохранить копию проблемной выгрузки и дерево последнего успешного прогона, чтобы переснять имена узлов через выборку b_xml_tree уже на тестовом стенде.

Не хотите копаться сами?

Починю за 1-3 дня. Без предоплаты — оплата по результату.

15+ лет опыта с 1С-Битрикс · Без предоплаты · 7 дней гарантии