Реализация модуля импорта каталога OneCatalog Wiki API на платформе 1С-Битрикс (редакция «Малый бизнес»/«Бизнес», модуль
iblock+catalog). Эталон — WooCommerce (см.INTEGRATION-STANDARD.md). Внешние контракты (§2) и инварианты (§5) стандарта неизменны; меняется только слой «как это лечь в Битрикс».
Модуль: отдельный сторонний модуль vendor.onecatalog (НЕ правка ядра, НЕ
шаблон). Структура:
/bitrix/modules/vendor.onecatalog/
include.php — автозагрузка классов (Loader::registerAutoLoadClasses)
install/index.php — vendor_onecatalog (CModule): InstallDB/UnInstallDB,
InstallEvents, InstallFiles, регистрация агентов, права
install/version.php — массив с 'VERSION' (SemVer) и 'VERSION_DATE'
install/db/ (mysql) — таблицы трекинга медиа/очереди (если не HL-блок)
lib/
api.php — Api: base URL, token, lang, GET, обёртка {data,success}
units.php — Units: конвертер вес(г)/размеры(мм) → ед. каталога
media.php — Media: скачивание, MIME, обложка/галерея, качество, дедуп
taxonomies.php — Taxonomies: свойства-списки/HL, термины, разделы
collectionimporter.php
brandimporter.php
countryimporter.php
productimporter.php — оркестратор
queue.php — Queue: порции, агент-обработчик, лог
settings.php — Options-обёртка над COption + валидация
admin/
vendor_onecatalog_settings.php — страница настроек (options.php style)
vendor_onecatalog_mapping.php — ручной маппинг характеристик
vendor_onecatalog_import.php — кнопка/пикер + прогресс
menu.php — пункт в админ-меню
ajax/import.php — контроллер приёма списка public_id + статус (аналог REST)
lang/ru/, lang/en/ — языковые файлы
options.php / .settings.php — при необходимости
Зависимости направлены «вниз» (как в §4): ProductImporter оркестрирует
Api/Media/Taxonomies/*Importer; Queue вызывает ProductImporter; AJAX-контроллер
вызывает Queue. Циклов нет.
Создаём один торговый инфоблок ONECATALOG_CATALOG (тип 1c_catalog или
свой). Привязываем к каталогу: CCatalog::Add(['IBLOCK_ID' => $id]). Из стандарта
торговые предложения (SKU/вариации) НЕ требуются — у OneCatalog нет вариаций
товара, options[] это характеристики-фасеты, а не SKU. Поэтому простой каталог
без инфоблока ТП (если позже понадобятся ТП — это отдельная задача, см. блокеры).
| OneCatalog | Битрикс — сущность | Механизм / API |
|---|---|---|
| product | Элемент инфоблока «Каталог» | CIBlockElement::Add/Update |
public_id (OC.IND.1) |
XML_ID элемента |
поле XML_ID, идемпотентность по нему |
| article (артикул произв.) | Свойство ARTICLE (строка) + CCatalogProduct нет своего «SKU» |
свойство типа «строка» |
| options[] (характеристики) | Свойства инфоблока типа «список» (L) или привязка к HL | CIBlockProperty + CIBlockPropertyEnum |
| categories[] | Разделы инфоблока (CIBlockSection) |
многоуровневая привязка элемента к разделам |
| brand | Свойство-справочник (HL) или список или раздел — выбор в настройках | BRAND |
| country | Свойство-справочник (HL) или список — выбор в настройках | COUNTRY |
| collections[] | Отдельный инфоблок или раздел или свойство — выбор в настройках | CIBlockElement / CIBlockSection / property |
| tags[] | Множественное свойство-список TAGS (вкл/выкл) |
CIBlockProperty (MULTIPLE=Y) |
| images_urls / files | PREVIEW_PICTURE + DETAIL_PICTURE + множественное свойство-файл MORE_PHOTO |
CFile::MakeFileArray |
| measurement_unit, sizes, areas_per_package, product_shape, … | через событие OnAfterProductImported (сайтовый слой) |
событие модуля |
public_idпишем вXML_IDэлемента. Поиск перед импортом:CIBlockElement::GetList([], ['IBLOCK_ID'=>$id, 'XML_ID'=>$publicId], false, false, ['ID'])→ найден →Update, иначеAdd.XML_IDредактируем в админке (вкладка «Подробно»).⚠️ Важно:XML_IDв Битрикс НЕ уникален на уровне БД и НЕ уникален в рамках инфоблока по умолчанию. Уникальность надо обеспечивать самим модулем (всегда искать поXML_IDпередAdd) и желательно включить флаг инфоблока «Проверять уникальность XML_ID» (CIBlockfieldXML_IDunique — задаётся в свойствах инфоблока, но это контроль только в админ-форме, не в API). В коде — всегда find-first.- Альтернатива/дубль ключа: отдельное строковое свойство
OC_PUBLIC_ID(на случай еслиXML_IDуже занят интеграцией с 1С). По умолчанию —XML_ID, переопределяемо настройкой/событием. Рекомендация: использоватьXML_ID, но предусмотреть конфликт с обменом 1С (см. вопрос 3). - Легаси-фолбэк (§5.1): если ранее ключ лежал в другом поле — поиск по нему как
фолбэк + миграция в
XML_ID.
- В Битриксе нет родного поля «Артикул» у элемента инфоблока (в отличие от
WooCommerce SKU). Есть свойство-строка
ARTICLE(часто заводят в торговых каталогах) — пишемarticleтуда.public_idиarticle— разные поля. - Если
articleпуст — свойство не заполняем (не синтезировать). - Коллизия артикула: артикул в Битриксе по умолчанию НЕ уникален, но по стандарту
«не перезаписывать чужой» — перед записью можно проверить, нет ли другого
элемента с тем же
ARTICLEи инымXML_ID; при коллизии — лог + пропуск записи свойства (значение источника всё равно сохраняем в событие). См. вопрос 8.
Два режима (как в стандарте — ручной маппинг по specification_id ИЛИ по имени):
- Свойства типа «список» (PROPERTY_TYPE='L') — основной путь. Для каждой
характеристики (
specification_id+specification_label):- find-or-create свойство: ищем
CIBlockProperty::GetListпо инфоблоку сXML_IDсвойства =oc_spec_<specification_id>(стабильный ключ маппинга), не поCODE(код мог быть транслитерирован по-разному). Если нет — создаёмCIBlockProperty::Add(['PROPERTY_TYPE'=>'L', 'XML_ID'=>'oc_spec_<id>', ...]). - значения (термы):
CIBlockPropertyEnum— find-or-create поXML_IDenum =specification_option_id(стабильно), либо по человекочитаемомуVALUE(label) без учёта регистра, НЕ поCODE/слагу (§5.1 — иначе дубли при разных транслитах). Слаг/XML_ID— только для создания нового. - привязка значения к элементу:
CIBlockElement::SetPropertyValues($elId, $iblock, [$enumId], $propCode).
- find-or-create свойство: ищем
- HL-блок — альтернатива для характеристик-справочников с доп. полями (если
нужно хранить numeric/единицы). Свойство типа «привязка к элементам HL-блока»
(
UserType='directory'). Сложнее в установке (создание HL-блока, прав, таблицы). Делаем опциональным; по умолчанию — списки.
boolean-характеристики (§2.2, §5.6): specification_option_name = null,
значение в bool_option. Без явного правила «true/false → значение терма»
теряется. Решение: для boolean-свойства создаём два enum-значения (label из
настройки маппинга, по умолчанию «Да»/«Нет» — локализуемо). bool_option === true
→ терм «Да», иначе (null/false/отсутствует) → терм «Нет» (§5.6: false по
умолчанию). В ручном маппинге (строгий режим) вебмастер задаёт VALUE термов для
true/false.
numeric-характеристики: numeric_option. В «список» класть число как label
неудобно (фасеты разбухнут). Лучше — отдельное свойство типа «число» (N) с
XML_ID='oc_spec_<id>'. Делаем определение типа свойства по
specification_type (text→L, boolean→L с Да/Нет, numeric→N). Это отступление от
строки таблицы «всё в список» — обосновано (см. вопрос 5).
CIBlockSection: find-or-create поXML_IDраздела = id категории OneCatalog (стабильно), фолбэк-поиск поNAME(label) без учёта регистра (§5.1). Слаг — вCODEтолько при создании.- Иерархия: payload даёт плоский
categories[](id, menutitle, slug) без явного родителя в примере. Если иерархия не приходит — создаём плоско под корнем; если приходитparent— строим дерево (IBLOCK_SECTION_ID). См. вопрос 6. - Привязка элемента к разделам: основной раздел —
IBLOCK_SECTION_ID; множественная привязка — полеIBLOCK_SECTION(массив) вCIBlockElement::Add(требуетbWorkFlow=false,bUpdateSearchпо вкусу).
Настройка по каждой сущности: тип хранилища = свойство-список |
свойство-справочник (HL) | раздел (для бренда) + конкретная цель.
- Список (L): find-or-create enum по label →
SetPropertyValues. - HL-справочник: find-or-create записи HL-блока (
DataManager::addсгенерён. черезHighloadBlockTable+CompileEntity), find поUF_NAME(label) илиUF_XML_ID= id OneCatalog. Свойство товара — типdirectory. Доп. поля бренда (страна, год основания, сайт, лого) кладутся в поля HL-записи через событие. - Раздел (для бренда — «бренд как раздел/страница»):
CIBlockSectionв отдельном инфоблоке «Бренды».
Дефолты (адаптация §7): бренд — HL-блок-справочник (ближайший аналог «нативной таксономии брендов» WooCommerce; в Битриксе нативной сущности «бренд» нет), включён; страна — свойство-список, выключена.
Настройка «тип объекта + цель»:
- Отдельный инфоблок «Коллекции» (аналог WP CPT): элемент-коллекция,
идемпотентность по
XML_ID= id коллекции; поля (link_3d,link_official_site, лого) — свойства. Связь товар↔коллекция — свойство товара типа «привязка к элементам» (E) на инфоблок коллекций. - Раздел инфоблока каталога (коллекция как подраздел).
- Свойство-список (простой случай).
Дефолт: отдельный инфоблок (богаче полей, как CPT в эталоне). Поля коллекции
заполняются из эмбеда collections[] товара (стандарт §2.1: /collections/{slug}/
отдаёт 500 — НЕ дёргать, всё уже в payload товара).
Множественное свойство-список TAGS (PROPERTY_TYPE='L', MULTIPLE='Y'), вкл/выкл,
find-or-create enum по title (label), не по слагу. По умолчанию выкл.
images_urls.{min|middle|max}(обложка) →PREVIEW_PICTUREи/илиDETAIL_PICTURE.files[](галерея,category=images) → множественное свойство-файлMORE_PHOTO(типF, MULTIPLE='Y') — стандартное имя для «детальных картинок» в Битриксе.- URL без расширения (§2.3):
media_sideload-аналога нет; качаем вручную (HttpClient::getв temp-файл), определяем MIME по содержимому (CFile::GetContentType/finfo), формируем массивCFile::MakeFileArray($tmp, $mime)(с корректным расширением в имени, иначеCFileотвергнет/сохранит без типа). Передаём вDETAIL_PICTURE/SetPropertyValues. - Выбор размера: с токеном —
max, без —min(§2.3). Размер — из настройки/ события (фильтр «порядок размеров»). - Трекинг качества (§5.3): рядом с файлом хранить выбранный размер. У
CFileнет места под произвольную мету → ведём свою таблицу (или HL-блок)oc_media:content_key (hash(path#size)),size,FILE_ID, ссылки. На повторном импорте: если доступен размер лучше записанного — перекачать, заменить (CFile::Deleteстарого, если НЕ общий), обновить запись. - Дедуп по контент-ключу (§5.3): из base64-префикса URL извлекаем
path, ключ =hash(path#size). Перед скачиванием ищем вoc_mediaпо ключу → нашли → переиспользуемFILE_ID(без скачивания).⚠️ Общие файлы (один файл у многих товаров / лого бренда) НЕ удалять при апгрейде качества (счётчик ссылок или флаг shared вoc_media). - Галерея идемпотентна по имени файла (§5.3): подписи в URL меняются — по URL
не сравнивать; сравнивать по
images_data.name/files[].name+ контент-ключу. ⚠️ CIBlockElement::Updateсо старой картинкой требует явного['DETAIL_PICTURE' => $arr]; для удаления старого файла —['del'=>'Y']илиCFile::Delete. Множественные свойства-файлы при апдейте надо аккуратно диффать (удалить лишние enum поPROPERTY_*_VALUE_ID), иначе дубли.
- OneCatalog: вес — граммы, размеры — миллиметры (подписи локализованы:
«мм», «г»); единица из
sizes.*_unit, иначе мм/г. - Битрикс: вес товара — поле
WEIGHTвCCatalogProduct(в граммах — внутр. единица каталога!). Размеры —WIDTH/LENGTH/HEIGHTвCCatalogProduct(в миллиметрах). То есть внутренние единицы Битрикса СОВПАДАЮТ с OneCatalog (г/мм) — конверсия фактически тождественна, но конвертер всё равно обязателен:- нормализовать локализованную подпись единицы → коэффициент к базовой;
- привести к г/мм (на случай, если источник отдаст кг/см или другую подпись);
- таблица коэффициентов + нормализация подписей (как в §5.6), переопределяемо событием.
⚠️ Тонкость: единицы отображения в публичной части задаются отдельно (measurement_unitтовара — это «м²/шт» из payload, кладётся через событие, НЕ влияет наWEIGHT/WIDTH). Не путать ед. измерения товара (продажную) с ед. габаритов (физической).WEIGHTпишем черезCCatalogProduct::Add/Update, а не через свойство.
В источнике цены нет → не создавать ценовую запись CPrice. По умолчанию
товар без цены (или в каталоге без модуля «Каталог» вообще цены нет). Для
подстановки своей цены — событие/фильтр (сайтовый слой). Поля «только при
создании» (статус активности ACTIVE) — не перезатирать при апдейте (§5.6).
Поле ACTIVE элемента (Y/N). Битрикс не имеет 4 статусов как WP
(publish/pending/draft/private). Маппинг: настройка «статус новых» →
ACTIVE='Y'|'N' (+ опц. ACTIVE_FROM для отложенной публикации). Только при
создании; при апдейте — не трогаем. См. вопрос 9 (нет полного аналога draft/
pending).
- Список
public_idрежем на порции по «шагу импорта» (минимум 10). - Два механизма (как и в стандарте — фолбэк без планировщика):
- AJAX-степпер (основной для UI): админ-страница шлёт
ajax/import.phpпорцию public_id, контроллер импортирует ихProductImporter'ом синхронно в рамках одного запроса (порция ≤ шаг → укладываемся в таймаут), возвращает прогресс/лог; JS опрашивает и шлёт следующую порцию. Это надёжнее агентов (агенты в Битриксе запускаются на хитах/кроне и непредсказуемы по времени). - Агенты (
CAgent::AddAgent) — фолбэк/фоновый режим: регистрируем агент, который на каждом запуске берёт следующую порцию из своей таблицы очередиoc_queue(статусы pending/running/done/error), импортирует и помечает. Агент не должен крутить весь импорт в одном запуске (память/таймаут) — одна порция за вызов, далее агент перезапланируется.
- AJAX-степпер (основной для UI): админ-страница шлёт
- Очередь хранить в своей таблице
oc_queue(id, batch, public_id, status, message, ts) — НЕ в опциях. Лог последних N результатов — оттуда. - Идемпотентность очереди: повторная постановка того же public_id не плодит задачи (find-or-skip по статусу).
⚠️ Агенты на shared-хостинге без cron запускаются «на хите» → могут стоять часами без посетителей. Поэтому основной путь — AJAX-степпер (активная вкладка админа гонит порции), агент — фон. См. вопрос 11.
См. раздел маппинга выше (Media-слой): ручное скачивание HttpClient →
MIME-детект → CFile::MakeFileArray; трекинг качества и дедуп в таблице
oc_media; общие файлы не удалять. HttpClient с таймаутом, ошибки → null/skip
(§5.5), без падения пакета.
Все опции через COption::SetOptionString('vendor.onecatalog', $key, $val) /
GetOptionString. Страница настроек — admin/vendor_onecatalog_settings.php
(стиль options.php, табы, CAdminTabControl).
Опция (COMODULE vendor.onecatalog) |
Назначение | Дефолт |
|---|---|---|
API_TOKEN |
X-API-Key (env/константа приоритетнее) |
пусто |
API_BASE_URL |
базовый URL (переопределяемо) | https://api.onecatalog.net/wiki/v1 |
LANG |
lang, только из en/ru/ar/zh/kk |
en |
STEP |
размер порции, кламп ≥ 10 | 10 |
NEW_STATUS |
ACTIVE Y/N для новых | Y |
IMPORT_COLLECTIONS + COLLECTION_TARGET_TYPE/_TARGET |
вкл; инфоблок/раздел/свойство + цель | вкл, инфоблок |
IMPORT_BRAND + BRAND_TARGET_TYPE/_TARGET |
вкл; HL/список/раздел + цель | вкл, HL |
IMPORT_COUNTRY + COUNTRY_TARGET_TYPE/_TARGET |
список/HL + цель | выкл, список |
IMPORT_TAGS |
вкл/выкл | выкл |
SPEC_MANUAL_MAPPING + карта |
строгий маппинг spec_id→свойство, bool true/false термы | выкл |
CATALOG_IBLOCK_ID |
целевой инфоблок каталога | — (выбор при установке) |
- Валидация и кламп (
STEP≥ 10);LANG— только из списка; приоритет env/константы (define('ONECATALOG_API_TOKEN', …)вdbconn.php/.settings.php) над опцией токена. - Для справочных сущностей — единый UI-паттерн «тип объекта + цель»; JS скрывает нерелевантные поля цели по выбранному типу.
- Ручной маппинг характеристик — отдельная страница
vendor_onecatalog_mapping.php: тянет/specifications/(одним запросом с высокимlimit— §2.1!), показывает список spec → выбор свойства инфоблока; для boolean — поля VALUE термов true/false. Ключ —specification_id(стабильный).
- Исходные строки — английские, через
Loc::getMessage('VENDOR_OC_…'). - Файлы
lang/en/…phpиlang/ru/…php(массив$MESS[...]) — зеркало структуры файлов модуля (lang/<lang>/lib/api.phpи т.д.). - Строки фронтовых JS (прогресс, пикер) — передавать из PHP в JS локализованными
(
CUtil::PhpToJSObject/BX.message), НЕ хардкодить в JS. ⚠️ Битрикс не использует POT/PO/MO (это WordPress-механизм). Аналог — пары файловlang/en+lang/ru. При добавлении строки UI — добавить в оба языка (иначе на одном языке покажется код сообщения). Это замена «пересборки .mo» из стандарта.
- Манифест:
install/index.php(классvendor_onecatalog extends CModule):MODULE_ID,MODULE_VERSION/MODULE_VERSION_DATE(изinstall/version.php),MODULE_NAME, требования (MODULE_GROUP_RIGHTS='Y'). Зависимость от движка магазина: вDoInstall()проверятьIsModuleInstalled('iblock')иIsModuleInstalled('catalog')— без них установка корректно сообщает об ошибке, не падает (§10: «активация не должна падать без движка магазина»). - SemVer + CHANGELOG (§10, и по памяти проекта): версия в
install/version.php(одно место — лучше, чем 2 в WP); вестиCHANGELOG.md(Keep a Changelog), бэклог = «Не выпущено», поднимать версию перед сборкой дистрибутива (.tar.gzмодуля /last_versionвupdate_info). Новые строки UI — вlang/en+lang/ru. - Установка (
DoInstall): регистрация классов (include.php), создание инфоблока(ов) каталога/коллекций/брендов и базовых свойств (ARTICLE/MORE_PHOTO/TAGS/служебные), таблицoc_queue/oc_media(или HL-блоков), регистрация агента (CAgent::AddAgent), регистрация событий модуля (RegisterModuleDependences), установка прав (InstallFiles/группы доступа). - Обновление/миграция (§10): механизм
updaterБитрикса (updater.phpвinstall/), флаг «миграция выполнена» в опции; перенос старых ключей (напр. public_id из свойства вXML_ID), удаление устаревших опций. - Деинсталляция:
DoUninstall— снять агент, события, опц. удалить таблицы (с подтверждением «сохранять данные?»). Инфоблоки/товары по умолчанию НЕ удалять.
- Модульные права через
MODULE_GROUP_RIGHTS='Y'+GetModuleRightList()(уровни D/R/W). AJAX-контроллерajax/import.phpпроверяет:$USER->IsAdmin()или право модуля ≥ W, иcheck_bitrix_sessid()(аналог nonce/CSRF из §11) на каждый POST. Без валидной сессии — 403.
-
Целевой инфоблок: создавать модулем или брать существующий? На ceram-online уже может быть каталог (фронт — Vue/Nuxt по памяти проекта). Модуль должен создать свой инфоблок
ONECATALOG_CATALOGили импортировать в действующий каталог сайта? От этого зависит вся схема свойств и риск конфликта кодов свойств. -
Простой каталог или торговые предложения (SKU)? В стандарте у товара нет вариаций (
options[]— фасеты, не SKU). Подтвердить, что ТП (инфоблок offers) НЕ нужны. Если на сайте каталог построен на ТП — товар без ТП может не отображаться корректно в типовых компонентах. -
public_id →
XML_ID: нет ли конфликта с обменом 1С?XML_ID— это поле, которым пользуется штатный обмен1С:Предприятие ↔ Битрикс(CommerceML). Если на сайте есть выгрузка из 1С, она перезапишетXML_IDсвоими GUID-ами. Тогда public_id нужно класть в отдельное свойствоOC_PUBLIC_ID, а не вXML_ID. Используется ли обмен с 1С? (Влияет на ключ идемпотентности — критично.) -
Уникальность
XML_ID. Битрикс не гарантирует уникальностьXML_ID. ОК ли полагаться на «модуль всегда ищет перед Add», или нужен жёсткий constraint (своя проверка + индекс)? Подтвердить политику при гонке параллельных импортов. -
Тип свойства для характеристик. Стандарт/таблица говорит «всё в список». Но numeric-характеристики разумнее хранить как свойство «число», а boolean — как список «Да/Нет». Согласовать: (а) numeric → число или список? (б) использовать HL-блоки-справочники для характеристик с доп. данными или хватит plain enum-списков?
-
Иерархия категорий. В payload
categories[]— плоский массив (id, menutitle, slug) без явногоparent. Приходит ли родитель/дерево из API (отдельный вызов/categories/)? Строить плоско под корнем или дерево? Нужна разведка API (§порядок прототипирования, шаг 0). -
Единицы габаритов товара. Внутренние единицы Битрикса — г/мм (совпадают с OneCatalog). Подтвердить, что писать надо именно в
CCatalogProduct.WEIGHT(граммы) иWIDTH/LENGTH/HEIGHT(мм), а НЕ в произвольные свойства. И отдельно:measurement_unitтовара (м²/шт) — куда (свойство-список «единица», справочникmeasurement-units)? — это сайтовый слой (событие) или ядро? -
Уникальность артикула. Нужна ли проверка уникальности
ARTICLE(как SKU в WP)? В Битриксе артикул по умолчанию не уникален. Блокировать запись при коллизии или просто логировать? -
Статусы товара. В WP 4 статуса, в Битриксе фактически
ACTIVE=Y/N(+ACTIVE_FROM). Как маппить «pending/draft/private»? Достаточно ли Y/N, или нужен доп. свойство-статус «на модерации»? -
HL-блоки: редакция и права. Highload-блоки требуют модуль
highloadblock(есть в «Малом бизнесе»? — да, начиная с определённых версий). Если бренд/ страна планируются в HL — подтвердить наличие модуля и право на создание HL-блока при установке. Иначе дефолт — свойства-списки. -
Очередь: cron настроен? AJAX-степпер требует открытой вкладки админа; агенты без cron запускаются «на хите». На каком хостинге крутится сайт, есть ли системный cron (
cron_events.php/ agent on cron)? Это определяет, можно ли закрывать вкладку во время большого импорта. -
Лимиты API и пагинация. Каков фактический дефолтный
limitинстанса (§2.1 предупреждает о «молчаливом обрезании»)? Есть ли rate-limit наX-API-Key? Сколько товаров реально импортируем (десятки/тысячи)? — влияет на размер порции и тайминги. -
Пикер (iframe) в админке Битрикса. Виджет
tools.onecatalog.net/picker.htmlгрузится в iframe cpostMessage. Админка Битрикса часто за HTTPS + CSP/X-Frame-Options. Разрешён ли внешний iframe в админке (CSP инстанса)? Если нет — фолбэк: импорт по вставленному списку public_id (стандарт §2.4 допускает «просто по списку ID»). Нужен ли пикер вообще на первом этапе? -
Язык и мультиязычность. Сайт одноязычный (ru)? Если мультиязычный — в Битриксе классический паттерн «инфоблок на язык», и тогда
langAPI ↔ языковая версия инфоблока — отдельная нетривиальная задача. Подтвердить, что нужен один язык (напримерru). -
Версия Битрикса и редакция. Точная версия ядра/редакция (для доступности
Bitrix\Main\Web\HttpClient, ORM, HL-блоков, D7-API). Можно ли писать на D7 (ORM,Bitrix\Main) или нужна совместимость со старым ядром (CIBlock*)? -
Дедуп медиа: где хранить контент-ключ. Своя таблица
oc_mediaили HL-блок? УCFileнет места под мету. Подтвердить, что создание своей таблицы при установке приемлемо (а не только HL).
-
Нет понятия «таксономия» — характеристики и справочники это РАЗНЫЕ механизмы. В WP атрибут/таксономия — единая абстракция (термины + связь). В Битриксе нет единой сущности: характеристика = свойство-список (enum) ИЛИ HL-справочник; категория = раздел; тег = свойство; бренд = HL/список/раздел. Поэтому «универсальный
Taxonomies-слой» из §4 распадается на несколько разнотипных обработчиков. Обход: свойTaxonomies-слой с адаптерами под каждый тип цели (enum / HL / section), единый интерфейс find-or-create поверх них. -
«post type vs таксономия» для коллекции — нет CPT. В Битриксе нет «типов записей» как в WP; ближайший аналог CPT — отдельный инфоблок. Обход: коллекция-как-инфоблок (богатые поля, дефолт) / коллекция-как-раздел / коллекция-как-свойство — настройка «тип объекта + цель» реализуется через разные сущности Битрикса, но семантика «CPT» подменяется «инфоблоком», что тяжелее в создании/правах.
-
Нет родного «бренда». В отличие от WooCommerce-таксономии
product_brand, у Битрикса нет нативной сущности бренда. Обход: HL-блок-справочник (дефолт) — но это требует модуляhighloadblockи создания таблицы/прав при установке. Дефолт стандарта «нативная таксономия брендов» на Битриксе невыполним as-is. -
XML_IDне уникален на уровне БД. Идемпотентность поXML_ID(§5.1) в WP опирается на уникальную мету; в Битриксе уникальность — только «по соглашению». Обход: всегда find-by-XML_ID перед Add + опц. включить контроль уникальности в настройках инфоблока (работает только в админ-форме, не в API) + защита от гонок на уровне модуля (блокировка/транзакция). Полной гарантии БД нет. -
Нет родного поля «Артикул/SKU» у элемента. WP SKU уникален и встроен; в Битриксе артикул — обычное свойство-строка без уникальности. Обход: свойство
ARTICLE+ (опц.) самостоятельная проверка коллизий. «SKU уникален» (§5.2) — не гарантируется платформой. -
CFileне хранит произвольную мету → трекинг качества/дедуп невозможны «рядом с файлом». В WP мета вложения хранит размер и контент-ключ. УCFileтаких полей нет. Обход: своя таблицаoc_media(content_key, size, FILE_ID, shared) — обязательна; иначе §5.3 (трекинг качества + дедуп) не выполнить. Доп. сложность: счётчик ссылок, чтобы «общие файлы не удалять при апгрейде». -
URL изображений без расширения +
CFileпридирчив к типу. Аналогаmedia_sideload_imageнет;CFile::MakeFileArrayждёт корректные имя/MIME. Обход: ручное скачиваниеHttpClient→ MIME-детект по содержимому → подстановка расширения в имя файла передMakeFileArray. Без этого картинки сохранятся без типа/не сохранятся. -
Агенты ≠ полноценная очередь. В WP — Action Scheduler (надёжный планировщик). Битрикс-агенты запускаются «на хите» (без cron — непредсказуемо), не дают гарантий времени, плохо параллелятся, легко «залипают» при ошибке. Обход: основной механизм — AJAX-степпер (синхронные порции под управлением вкладки админа), агенты — только фоновый фолбэк; своя таблица очереди со статусами. Импорт «в фоне при закрытой вкладке» надёжен только при системном cron.
-
iframe-пикер в админке под CSP/
X-Frame-Options. Админка Битрикса может запрещать внешние iframe (CSP инстанса/прокси). Обход: фолбэк «импорт по списку public_id» (стандарт это допускает, §2.4); пикер — опционально, при совместимом CSP.postMessage+ проверкаevent.originреализуемы, но встраивание не гарантировано. -
Мультиязычность через «инфоблок на язык» — дорого. WP-модель «один пост, переводы плагином» в Битриксе обычно = отдельный инфоблок на каждый язык. Связать
langAPI с языковой версией и сохранить идемпотентность поpublic_idмежду языковыми инфоблоками — отдельная крупная задача. Обход: на первом этапе — один язык (см. вопрос 14); мультиязык — отдельная фаза. -
Нет полного аналога статусов publish/pending/draft/private. Битрикс —
ACTIVE=Y/N(+ACTIVE_FROM). Обход: маппинг в Y/N; «на модерации» — через доп. свойство-статус, но это уже не родной механизм, а соглашение модуля. -
«Цена не задаётся» — но без
CPriceтовар может выпасть из типовых компонентов каталога. Это не блокер импорта, но штатные компоненты каталога/корзины ждут цену; товар безCPriceкорректно импортируется, однако может не показываться/не покупаться. Обход: цена — забота сайта через событие (§5.6/§8); на витрине ceram (Vue/Nuxt) это может быть неважно, но для типовых Битрикс-компонентов — учитывать. -
POT/PO/MO неприменимы. Механизм переводов стандарта (§9) — WordPress-овый. Обход: родные
lang/<lang>/*.php($MESS); «пересборка .mo» заменяется «обновить оба языковых файла». Семантически эквивалентно, но это другой инструментарий — пункт чек-листа §11 «POT + переводы» на Битриксе читать как «lang/en + lang/ru».
Новый сценарий стандарта поверх импорта каталога: живой B2B-фид цен/остатков (
retailer-share-products, отдельная авторизацияurl_key+private_key). Внешний контракт §13.1 и инварианты §13.4 неизменны; ниже — как это лечь в Битрикс и что подтвердить. Хорошая новость: у Битрикса цены/склады — нативные (типы цен, склады), поэтому раскладка по регионам/складам делается ядром, а не сайтовым слоем.
| §13 источник | Битрикс — сущность | Механизм |
|---|---|---|
base_price (регион) |
CPrice для типа цены (CCatalogGroup) |
CPrice::Add/Update (PRODUCT_ID, CATALOG_GROUP_ID, CURRENCY) |
регионы product_prices[] |
типы цен (опт/розница/регион) ИЛИ один тип BASE | маппинг region_id → CATALOG_GROUP_ID |
promo_price |
отдельный тип цены ИЛИ CCatalogDiscount |
нет нативного «sale_price» — см. вопрос 2 |
products_stocks[warehouse_id] |
склады CCatalogStore + CCatalogStoreProduct |
нативно поштучно по складам! |
| суммарный остаток | CCatalogProduct.QUANTITY |
сумма по складам (Битрикс считает сам при включённом складском учёте) |
| код поставщика | свойство OC_SUPPLIER_CODE |
для unknown-сопоставления |
| сигнатура change-detection | свойство OC_PRICESTOCK_SIG или таблица oc_pricestock |
§13.4 |
- Регионы → типы цен или один регион? Битрикс умеет несколько типов цен
(
CCatalogGroup). Раскладыватьproduct_prices[]по типам цен (тип на регион) или взять один регион по приоритету в базовый тип? От этого зависит схема цен. - promo_price — как? В Битриксе нет поля «цена со скидкой» как в WC. Варианты:
(а) отдельный тип цены «промо»; (б) скидка
CCatalogDiscount; (в) писать promo в базовую цену; (г) игнорировать. Что нужно витрине? - Остатки: нативные склады или суммарно? Использовать складской учёт
Битрикса (
CCatalogStore/CCatalogStoreProduct, раскладка по складам — нативно) или только суммарныйQUANTITY? Включён ли складской учёт на сайте? Маппингwarehouse_idфида → склад Битрикса (find-or-create поXML_ID/названию). - Валюта. В фиде валюты нет (
base_price— число).CPriceтребует валюту. Какая валюта каталога по умолчанию (CCurrency)? ⚠️ Конфликт с обменом 1С (критично). Если на сайте есть обмен1С ↔ Битрикс, цены и остатки ведёт 1С — B2B-синк будет с ним конфликтовать (какXML_IDв вопросе 3 импорта). Тогда синк цен/остатков либо не нужен, либо пишет в отдельный тип цены/склад, не пересекаясь с 1С. Используется ли обмен 1С для цен/остатков?- Доступность при нуле. Остаток 0 → деактивировать (
ACTIVE=N), пометить «нет на складе», или оставить с «Разрешить покупку при отсутствии» (CAN_BUY_ZERO)? Зависит от витрины. - Change-detection — где хранить сигнатуру? Свойство элемента
OC_PRICESTOCK_SIG(проще) или своя таблицаoc_pricestock(какoc_media)? §13.4 требует префетч сигнатур одним запросом — свойство индексируется, подойдёт. - Расписание/нагрузка. Скан фида + запись цен/остатков порциями — агент
CAgent(как очередь импорта, §6) или AJAX-степпер? На больших каталогах записьCPrice/CCatalogStoreProductдозировать. Системный cron есть? - unknown по коду поставщика. Свойство
OC_SUPPLIER_CODE(множественное), поиск элемента по нему. Куда класть код, чтобы не конфликтовать сARTICLE(артикул производителя, §5.2 импорта)?
- Нет нативного «sale_price». Акция (
promo_price) выражается типом цены илиCCatalogDiscount— это не одно поле, а отдельный механизм (см. вопрос 2). - Конфликт с 1С по цене/остатку — куда серьёзнее, чем в WC: 1С штатно перезаписывает цены/остатки. Нужна явная развязка (отдельный тип цены/склад) или отказ от синка там, где это ведёт 1С (вопрос 5).
- Change-detection без меты у элемента — как и в §5.3 (медиа): своя таблица/свойство под сигнатуру (вопрос 7).
- Плюс Битрикса: раскладку цен по регионам (типы цен) и остатков по складам §13.7 делает ядро нативно — сайтовый слой/ACF-аналог не требуется (в отличие от WC, где это через события). Точки расширения (события до/после) всё равно предусмотреть для проектных полей.