Дисклеймер
Данная статья не является агитацией за какую-либо 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