Битрикс. Интеграция. Реализация каталога с двумя структурами групп номенклатуры

в 17:18, , рубрики: 1С-Битрикс, битрикс, Веб-разработка, интеграция, Интеграция с 1С, интернет-магазин, каталог, метки: , , , ,

Дисклеймер

Данная статья не является агитацией за какую-либо CMS, какой бы хорошей или плохой она не была…

Пролог

Одной из самых и первых и распространённых проблем, которая всплывает при начале работ по интеграции 1С и интернет магазина — это проблема структуры каталога. Как правило, структура номенклатуры, имеющаяся в базе 1С заказчика, мягко говоря, не готова к экспорту на сайт, а заказчик крайне против её изменения, т.к. бизнес-процесс отработан, все привыкли к такой структуре, которая есть и никто не имеет ни малейшего желания изменять своим привычкам.
Что мы обычно делаем в такой ситуации? Я думаю, что то же самое, что и другие, создаём альтернативную структуру, к ней привязываются все товары и уже новая структура выгружается на сайт, а старая остаётся нетронутой в базе 1С. В итоге все довольны. Для реализации этого в связке с Битриксом достаточно немного модернизировать выгрузку, идущую в комплекте...*

* — Написанием этой статьи было запланировано до выхода 12-й версии Битрикса и обновления выгрузки соответственно. Теперь же анонсирован штатный функционал для создания структуры каталога отличной от используемой в базе 1С. Да, конечно, наличие штатного функционала — здорово, но всё же делать настройку структуры во время настройки выгрузки, мне кажется, не всегда удобным и поэтому я предпочёл бы, как и раньше реализовать отдельную структуру в виде отдельного справочника. Но это уж дело ситуации и вкуса каждого...

Итак, когда проблема структуры обусловлена лишь просто нежеланием поменять основную структуру — мы легко можем решить проблему подменой групп. Но, а что если всё-таки такая иерархия в базе не просто прихоть, а требование бизнес-процесса?..

Задача

Вот однажды к нам попала такая задача. В этот раз всё интереснее, добавилась необходимость привязывать скидки к группам товаров, не к новым, а к тем, которые в базе. Например:

Есть следующая иерархия в базе (в примере я выделил группы на основании производителя, в реальной базе какой-либо единой логики не было, например часть групп могла быть по производителю, часть по поставщику, а часть вообще по виду товара):

Philips
	Телевизор P11 - 100руб
	Утюг P11 - 50руб
Samsung
	Телевизор S11 - 110руб
	Телефон S11 - 80руб

Иерархия в интернет-магазине другая:

Техника
	Утюги
		Утюг P11
	Телевизоры
		Телевизор P11
		Телевизор S11
	Телефоны
		Телефон S11

Предположим, что у контрагента A скидка на все товары группы Philips — 10%, а на товары Samsung — 15%. А у контрагента B Philips — 20%, а на товары Samsung — 30%.

Тогда, получается, что контрагент A должен видеть каталог так:

Техника
	Утюги
		Утюг P11 - 45руб
	Телевизоры
		Телевизор P11 - 90руб
		Телевизор S11 - 93.5руб
	Телефоны
		Телефон S11 - 68руб

А контрагент B:

Техника
	Утюги
		Утюг P11 - 40руб
	Телевизоры
		Телевизор P11 - 80руб
		Телевизор S11 - 77руб
	Телефоны
		Телефон S11 - 56руб

У Битрикса есть отличный механизм работы со скидками, и трогать его крайне не хотелось бы, т.к. эти изменения потянули бы массу других правок за собой. Да, конечно теоретически можно было бы делать скидки не на группу номенклатуры, а на определённый товар, но тогда придётся при выгрузке тянуть набор скидок для каждого товара. А уже сейчас в базе порядка 10тыс товаров и порядка тысячи контрагентов, у каждого из которых может быть по паре десятков уникальных скидок и к тому же база растёт, контрагенты добавляются… Такой подход сразу был отброшен.
Очевидно, что сайту необходимо знать обе структуры групп, чтобы опираясь на первую рассчитывать цену, а опираясь на вторую строить дерево каталога.

Решение

Для решения нам понадобится очень удобное свойство элемента инфоблока в Битриксе — «Привязка к разделам». А удобно оно тем, что при использовании API Битрикса для получения списка элементов — функция CIBlockElement::GetList вернёт не только элементы, которые находятся в этой группе, но и элементы, которые имеют привязку через свойство «Привязка к разделам». Интересно также то, что раздел этого же инфоблока не может быть выбран для свойства «Привязка к разделам», хотя в то же время аналогичное свойство «Привязка к элементам» подобных ограничений не накладывает. Это сразу наталкивает на мысль, что должно быть два инфоблока в одном первая структура с самими элементами, а во втором группы альтернативные группы.
Казалось бы, всё, вопрос решён, раз всё предусмотрено на уровне модуля, то дальше проблем возникнуть не должно, берём за основу группы второго инфоблока, и вытаскиваем элементы первого. Но не тут-то было. Штатный компонент каталога не умеет. Запрос в службу поддержки подтвердил отсутствие такой возможности. Но почему? Ведь для этого всё есть, и необходимое свойство и даже сама платформа заточена, чтобы делать подобные выборки, но, увы…

Да, разумеется, никто не мешает сделать свой компонент и делать там что угодно, но, во-первых, это существенно увеличит время разработки, а значит и стоимость, а во-вторых было любопытно, что же там, в стандартном компоненте мешает. Решено, будем ковырять штатный каталог и если возможно кастомизируя его реализовывать поддержку необходимого нам функционала.

Для начала создаём 2 инфоблока описанных выше, забиваем тестовую информацию, настраиваем привязку. У нас должно получится так:

Битрикс. Интеграция. Реализация каталога с двумя структурами групп номенклатуры

(в левой части мы видим два инфоблока описанных выше, а справа элементы раздела Phlips первого инфоблока)

Битрикс. Интеграция. Реализация каталога с двумя структурами групп номенклатуры

(а здесь в правой части мы видим элементы раздела Samsung первого инфоблока)

Если мы откроем главную страницу каталога, то видим разделы нашего инфоблока:

Битрикс. Интеграция. Реализация каталога с двумя структурами групп номенклатуры

Если перейти в раздел, например, Телевизоры, то убедимся, что он пуст:

Битрикс. Интеграция. Реализация каталога с двумя структурами групп номенклатуры

Итак, отсюда начнём. Используемый нами компонент catalog — многостраничный. Внутри он состоит из набора одностраничных компонентов.
За вывод содержимого определённого раздела отвечает section.php основного компонента catalog. Открываем его и находим место, где идёт вызов одностраничного компонента bitrix:catalog.section. Это одностраничный компонент, который и выводит список элементов. Сразу копируем его в директорию custom и заменяем вызов на custom:catalog.section.
Открываем файл component.php, отвечающий за логику, компонента custom:catalog.section. находим интересующий (да, вообще-то он там единственный) вызов API CIBlockElement::GetList. Нас интересует $arFilter, т.к. именно этот параметр определяет какие записи будут отфильтрованы. Чуть выше находим место, где определён сам $arFilter:

$arFilter = array(
	"IBLOCK_ID" => $arParams["IBLOCK_ID"],
	"IBLOCK_LID" => SITE_ID,
	"IBLOCK_ACTIVE" => "Y",
	"ACTIVE_DATE" => "Y",
	"ACTIVE" => "Y",
	"CHECK_PERMISSIONS" => "Y",
	"MIN_PERMISSION" => "R",
	"INCLUDE_SUBSECTIONS" => $arParams["INCLUDE_SUBSECTIO
NS"],
);

Причина, почему компонент не видит наши элементы, видна — «IBLOCK_ID» => $arParams[«IBLOCK_ID»]. Этой строкой мы отфильтровываем все элементы, которые находятся вне нашего инфоблока, а у нас-то два инфоблока, в одном элементы, а в другом нужные разделы. Впринципе достаточно убрать эту строку, обновить страницу раздела телевизоры и мы увидим, что наши элементы появились:

Битрикс. Интеграция. Реализация каталога с двумя структурами групп номенклатуры

Всё хорошо, но когда мы убрали фильтрацию по инфоблоку, теперь функция GetList ищет по элементам всех инфоблоков, даже по тем, которые нам и вовсе не нужны, что явно не добавит производительности. Поэтому лучше всего кастомизировать сам компонент catalog, добавить в него дополнительный параметр ID_IBLOCK2, передавать его в компонент custom:catalog.section. Соответственно, определение $arFilter будет выглядеть так:

$arFilter = array(
	"IBLOCK_ID" => $arParams["IBLOCK_ID2"],
	"IBLOCK_LID" => SITE_ID,
	"IBLOCK_ACTIVE" => "Y",
	"ACTIVE_DATE" => "Y",
	"ACTIVE" => "Y",
	"CHECK_PERMISSIONS" => "Y",
	"MIN_PERMISSION" => "R",
	"INCLUDE_SUBSECTIONS" => $arParams["INCLUDE_SUBSECTIONS"],
);

Всё, теперь со списком элементо всё в порядке, но если мы попытаемся открыть страницу карточки товара, например, Телевизор P11, то увидим ошибку:

Битрикс. Интеграция. Реализация каталога с двумя структурами групп номенклатуры

Тут всё аналогично, catalog.section мы полечили, а catalog.element по-прежнему ищет наш товар в другом инфоблоке. Кастомизируем и его. Находим в самом верху строку:

$arParams["IBLOCK_ID"] = intval($arParams["IBLOCK_ID"]);

Соответственно меняем её на:

$arParams["IBLOCK_ID"] = intval($arParams["IBLOCK_ID2"]);

И успех, карточка товара стала открываться:

Битрикс. Интеграция. Реализация каталога с двумя структурами групп номенклатуры

Всё. Можно создавать скидки для групп первого инфоблока и при построении каталога всё будет работать.
Решение протестировано на работающем сайте с ~10тыс товаров, с тремя типами цен, 1000 контрагентов и более чем с 5-ю тысячами скидок.

Эпилог

Вот такими простыми манипуляциями мы решили поставленную задачу и реализовали интересный функционал, который думаю, востребован далеко не в нашем единичном случае… Каких либо препятствий реализовать данную функциональность из коробки не вижу, но, несмотря на это, увы…

Если будет интересно, могу в следующих статьях поделиться другими рецептами, которые тоже сходу не очевидно для человека, не имеющего достаточного опыта работы с Битриксом, но частенько бывают полезными.

Постскриптум

Всем спасибо за внимание! Буду рад вашим комментариям, вопросам, обсуждениям.
Если вы заметили опечатку или какую-либо погрешность, вэлком в ЛС ;)

Автор: galichmark

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js