​Конфигуратор товаров в 1С-Битрикс, два подхода

Пролог

Решение нестандартных задач всегда приводит к той или иной кастомизации. В статье мы рассмотрим два подхода к решению одной задачи, а также сравним плюсы и минусы каждого из решений.

Задача

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

Что должно получиться на выходе

  • Интерфейс для администратора, через который можно было бы гибко управлять конфигуратором.
  • Интерфейс для пользователя, при помощи которого он мог бы «собрать» себе товар, к примеру матрас размером 140×200, дефолтным чехлом и тремя зонами пружин, а в процессе подбора параметров видеть изменение цены.
  • Сконфигурированные товары на ряду с обычными должны добавляться в корзину, а затем быть доступными в списке ранее заказанных.
  • Сконфигурированные товары на ряду с обычными должны адекватно обрабатываться внутренними механизмами системы и быть доступными в стандартном разделе заказов.

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

Подход номер один, первая итерация

В первой итерации мы должны дать возможность администратору сайта создавать конфигурированные товары, матрасы.

Задачи первой итерации:

  • Написание интерфейса для администратора сайта, в котором он мог бы в полуавтоматическом режиме создавать конфигурируемые товары.
  • Написать интерфейс для пользователя, чтобы он мог подобрать нужные параметры товара, а затем положить его в корзину.
  • Сделать поддержку конфигурируемых товаров в пользовательской корзине и административном разделе заказов.

С виду почти ничего сложного, да? Теперь посмотрим на вводные данные.

Какие параметры определяют конфигурируемый матрас и как они зависят друг от друга

  • Размер. В основе всех вычислений лежит размер, их около 20 штук — 140×180, 140×200... У размера есть ширина и длинна, они тоже меняются. У матраса для каждого размера должна быть стоимость.
  • Чехол. У каждого матраса есть набор чехлов в которых он может быть выполнен, также можно указать дефолтный чехол, который изначально идет в комплекте с матрасом. Стоимость чехла зависит от размера матраса.
  • Пружинные зоны. Каждый матрас может быть выполнен с различными пружинными зонами или вообще без них. Стоимость зон также зависит от размера матраса.

Итог по входным параметрам матраса

Итак у нас всего три параметра, два из которых связаны с первым. У матраса всегда есть размер, не важно какой именно, но он есть. У матраса всегда есть дефолтный чехол, также может быть и несколько других на выбор. Также у матраса может быть опция пружинных блоков, а может и не быть. Вроде бы все понятно? Идем дальше.

Обычный матрас из каталога имеет около 15 размерных вариантов, семь чехлов и до трех вариантов пружинных блоков. Вспоминаем что мы должны предоставить пользователю конечную стоимость матраса здесь и сейчас, а значит её нужно вычислить. Также держим в уме и другие задачи, к примеру «интерфейс администратора», полуавтоматическое конфигурирование из административной панели и прочее. Напоследок сдобрим это тем что всю эту прелесть нужно «подружить» с 1С-Битрикс. Какие будут идеи?

Первое над чем я задумался — это «дружба» с платформой, а точнее с её штатным функционалом. Крайне не хочется писать то, что уже реализовано в коробке, плюс, т.к. оно родное, поддержка всеми интерфейсами идет в комплекте. На примете было всего три функциональных блока которые можно было бы приспособить для данной задачи:

  • Комплекты
  • Наборы
  • SKU (торговые предложения)

К сожалению комплекты и наборы не давали того функционала, который был необходим для создания конфигуратора, с релизом обновленной e-commerce платформы возможно что-то поменяется, тут приведен отрывок моего разговора с Юрием Волошиным по поводу новой платформы и в частности о подходе к созданию конфигураторов. Помимо этого для использования комплектов и наборов пришлось бы апнуть лицензию до «Бизнес», но это мелочи. Остался последний вариант — SKU.

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

Промежуточный итог

Мы определились с подходом — весь функционал конфигуратора будет построен на базе штатного механизма SKU. Это уже хорошо: знакомый функционал, и он полностью поддерживается платформой, т.е. в корзине и административном разделе заказов все заработает без дополнительных телодвижений. Остаются два интерфейса — административный и пользовательский.

Структура данных

Матрасы надо из чего-то генерировать, для этого нужен справочник сущностей. В первой итерации будет три справочника:

  1. Размеры
  2. Чехлы
  3. Пружинные зоны

Каждый из справочников редактируемый и при необходимости в них можно что-либо добавить или убрать.

Административный интерфейс

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

  • Свойство привязки к размеру и его стоимость
  • Свойство привязки к пружинным зонам
  • Свойство привязки зон к размерам
  • Свойство, флаг, генерации SKU

Большая часть свойств представляли из себя некие табличные интерфейсы (screenshot 1).

Screenshot 1.

Заполнение товара

Справочник, как не сложно догадаться, заполняется в три этапа:

  • Первым этапом заполняются размеры, т.к. от них зависит стоимость конкретных предложений и комплектующих.
  • Вторым этапом заполняются чехлы, в них, помимо описания и картинки, присутствует свойство привязки к размерам.
  • На последнем, третьем, этапе заполняются пружинные зоны, они также имеют свойство привязки к размерам.

После того как справочник заполнен, можно приступать к товарам.

У каждого матраса в административном разделе появилась вкладка «генератор SKU». Во вкладке по порядку идут свойства:

  • Привязка к размерам
  • Привязка к чехлам
  • Привязка к чехлу по умолчанию
  • Привязка к пружинным зонам
  • Флаг перегенерации SKU
Заполнив все необходимые данные, ставим флаг перегенерации SKU и сохраняем элемент инфоблока. (Тут в трех скринах показать админку).

Генерация SKU

После сохранения элемента инфоблока начинается магия: мы перехватываем событие OnAfterElementIblockUpdate, это событие срабатывает после обновления элемента инфоблока. Проверяем относится ли элемент к инфоблоку матрасов, а также сверяем флаг перегенерации SKU. Если все данные подтверждены, то подключаем класс генерации SKU. Генерация SKU состоит из нескольких этапов:
  • Сбор всех возможных размерных предложений
  • Сбор всех доступных чехлов, в том числе и дефолтного
  • Сбор всех доступных пружинных зон

После сбора всех параметров, начинается перебор, нас интересуют точки пересечения массивов, точками пересечения являются размеры матраса, т.к. это основополагающий параметр от которого зависит конечная стоимость элемента SKU. В ходе перебора находятся все точки пересечения размеров, чехлов и пружинных зон. Если какой-либо из параметров имеет размерное предложение не доступное для матраса, оно будет проигнорировано.

На выходе мы получаем массив с перебором всех возможных вариантов матраса, во всех доступных размерах, со всеми чехлами и пружинными зонами. Помимо размера функция учитывает стоимость комплектующих и общую скидку (в данной реализации скидка применяется только к матрасу), чехлы и пружинные зоны добавляются без скидки; далее формула обрабатывает входные данные и высчитывает стоимость товара, на которую нужно дать положенную скидку и получить желаемую стоимость, т.е. формула считает конечную стоимость товара и размер скидки как бы «задом наперед».

После сбора всех необходимых данных запускается функция создания элементов SKU. По окончании работы функции у матраса появляется в среднем от 250-ти до 500-сот вариантов, элементов SKU.

Пользовательский интерфейс

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

Детальная карточка товара

В детальной карточке товара происходит проверка: проверяем есть ли у товара SKU, если есть, то подключаем дополнительный файл, в котором содержится интерфейс подбора параметров (screenshot 2).

Screenshot 2.

Помимо самого интерфейса, в файле происходит подключение класса для работы генератора SKU.

Для построения пользовательского интерфейса нам понадобится выборка всех элементов SKU для дефолтного размерного ряда — того, что пользователь видит при переходе в детальную карточку товара.

Первым делом получим дефолтный размерный ряд, затем выделим дефолтное предложение — матрас с наименьшей стоимостью. На основе этих данных выбираем необходимые для построения интерфейса SKU и записываем в кэш. Строим интерфейс и ждем когда пользователь начнет осуществлять подбор. Если пользователь изменил любой из параметров, запускается следующая выборка, удовлетворяющая всем заявленным параметрам — размер, чехол, пружинная зона. При этом перестраивается интерфейс и меняется конечная стоимость товара. Благодаря кэшированию и довольно мощному серверу, интерфейс отрабатывает молниеносно быстро: пользователь и не догадывается, что перебирает до полутора тысяч вариантов одного и того же матраса.

Покупка, корзина и административный раздел заказов

По клику на кнопку «Купить» элемент SKU, как и обычный товар, отправляется в корзину. В корзине пользователь видит товар, в названии которого перечислены параметры размера, чехла и пружинной зоны. Это плохой стиль, нужно было передать данные параметры как свойства товара, но в первой итерации сделано именно так.

В административном разделе заказов выбранные товары отображаются так же, как у пользователя в корзине — все параметры перечислены в названии элемента SKU.

На этом первая итерация была закончена, о плюсах и минусах я напишу в конце статьи, после рассмотрения второго подхода.

Подход номер два, вторая итерация

Во второй итерации мы должны дать возможность администратору сайта создавать конфигурированные товары, кровати.

Задачи второй итерации:

  • Написание интерфейса для администратора сайта, в котором он мог бы в полуавтоматическом режиме создавать конфигурируемые товары.
  • Написать интерфейс для пользователя, чтобы он мог подобрать нужные параметры товара, а затем положить его в корзину.
  • Сделать поддержку конфигурируемых товаров в пользовательской корзине и административном разделе заказов.

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

Какие параметры определяют конфигурируемую кровать и как они зависят друг от друга:

  1. Производитель. От производителя зависят все ниже лежащие параметры.
  2. Размер. В основе всех вычислений лежит размер, у каждого производителя они разные — 140×180, 140×200... У размера есть ширина и длинна, они тоже меняются. У кровати для каждого размера должна быть стоимость.
  3. Материал основания. Необязательный параметр.
  4. Цвет основания. Необязательный параметр.
  5. Материал отделки кровати. Необязательный параметр.
  6. Комплектация кровати. Сюда входят ящики для белья, подъемные механизмы и прочее. Необязательный параметр.

Итог по входным параметрам матраса

Итак теперь у нас пять параметров. Как и ранее, обязательным параметром является размер, но теперь он зависим и зависит от производителя. Все параметры в конфигураторе зависят от производителя, далее идут размеры, а за ним остальные необязательные свойства. Идем дальше.

Именно до этого момента я думал об использовании первой версии конфигуратора, но прикинув, сколько элементов SKU будет генерироваться под каждую кровать, пришлось искать альтернативы.

В первом варианте у нас было три параметра, и в среднем на один матрас выходило от 250-ти до 500-т торговых предложений; самое большое число предложений которое мне удалось зафиксировать — 1245 штук. Кровати же сулили увеличить эту цифру в десятки раз, ведь каждый новый параметр комбинации возводит число вариантов в следующую степень.

Пришлось искать альтернативный вариант решения задачи и желательно без создания физических единиц, без создания элементов SKU. В ходе размышлений и штудирования мануалов по API 1С-Битрикс, я пришел к выводу, что можно вообще ничего не генерировать — все необходимые параметры и так содержатся в кастомных свойствах элемента инфоблока, в публичной части их нужно обработать и показать пользователю. Мне показалось это интересным и я продолжил прикидывать механизм работы.

Промежуточный итог

Мы определились с методом для второго подхода — это будет генератор без использования SKU, он не будет создавать сущностей, а представит из себя пользовательский и административный интерфейс, плюс некоторые сервисные функции.

Реализация

От первой итерации я взял административный и пользовательский интерфейс. В принципе, они меня полностью устраивали, в админке нужно было произвести некоторую модернизацию, но по большому счету мало что изменилось. Но если внешнее отображение сменилось незначительно, то «под капотом»" осталось 10% старого кода — та часть которая генерировала отображение интерфейсов.

Был перестроен справочник (как я сказал ранее, теперь все параметры объединялись вокруг производителей), были сильно переработаны кастомные свойства — теперь они стали иметь настройки, чтобы при развертывании генератора для других инфоблоков не пришлось лезть в код (как это было в первой итерации), а все можно было перенастроить через админку.

Еще одним изменением стало то, что дефолтное предложение (а точнее — его цена) теперь стало формироваться автоматически на основе данных о вариантах товара, т.е. если раньше стоимость товара устанавливалась вручную во вкладке «Торговый каталог», теперь же генератор взял эту функцию на себя.

В этой версии также отпала нужда во флаге «Перегенерация SKU», потому что SKU по факту нет, а все кэшированные данные по товару, элементу инфоблока отлично перетираются при его обновлении (спасибо тегированному кэшу). Справочник заполнен, товар заполнен, цена сконфигурирована, переходим к пользовательскому интерфейсу.

Пользовательский интерфейс

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

Запускаем функции разбора свойств элемента инфоблока, ведь вся необходимая информация хранится в них. Как и ранее находил дефолтный размерный ряд. Ищем все точки пересечения (они, как и ранее, находятся в размерах), затем строим массив из всех возможных вариантов модификаций товара.

Важно, что данные для постройки и работы интерфейса у нас есть с самого начала, и за ними не приходится дополнительно обращаться в базу данных; исключением являются данные, описывающие чехлы и пружинные зоны — их все же придется запросить из БД (но единожды), а затем брать из кэша. На основе полученных данных строим интерфейс.

Пока все было сильно похоже на первую итерацию, да мы отказались от SKU, но логика работы изменилась не сильно. Теперь об интересном.

Добавление в корзину

Как уже можно было заметить, у нас всего один товар, весь подбор параметров и внешнее изменение цен строится при помощи свойств. Пользователь выбрал нужную ему конфигурацию и тут встает вопрос: «Как товар передать в корзину?».

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

Как и прежде, тут на выручку приходит API: мы перехватываем событие добавления товара в корзину и меняем стоимость товара на ту, что вышла при конфигурировании. Свойства товара пересылаются штатным методом, тут напильник не понадобился.

Корзина

В корзине все чинно и аккуратно: стоимость приходит верная, правильно считается скидка, и подтягиваются свойства товара, к примеру размер и материал отделки (screenshot 3).

Screenshot 3.

Административный раздел

Здесь тоже все нормально: заказ формируется с правильной ценой, учитываются скидки и доставка, также товару передаются нужные свойства.

Эпилог

Я расписал два подхода к решению по сути одной и той же задачи, конфигурированию сложных товаров в среде 1С-Битрикс. Осталось сравнить два подхода и сделать выводы.

Сравнение

Первая итерация

Плюсы:
  • Реализована на основе штатного функционала, а значит поддерживается платформой.
  • Можно поддержать складские остатки и скидки на конкретное предложение товара.
  • При необходимости каждую из модификаций можно вывести в каталог как отдельный товар.
Минусы:
  • Создание огромного количества физических элементов (каждое из предложений это отдельный элемент инфоблока).
  • Трудности в поддержании информации в актуальном состоянии. Данные в справочнике могут меняться и чтобы они обновились в уже созданных SKU, приходится принудительно инициализировать перегенерацию.
  • Для построения пользовательского интерфейса генерируется много запросов в базу данных.

Вторая итерация

Плюсы:
  • Реализовано при помощи штатного API, это не жесткая самопись, но уже и не штатный функционал.
  • Не создает лишних сущностей, у нас есть один товар и его свойства.
  • Нет проблем с обновлением информации потому что нечего обновлять, достаточно сбросить кэш и все обновится автоматически.
  • Для построения пользовательского интерфейса нужно совсем немного запросов в базу данных, нужно получить только описание комплектующих.
Минусы:
  • Всё же это не штатный функционал, и его поддержкой приходится заниматься самостоятельно (грядущее обновление платформы наверняка заставит внести корректировки в работу механизма).
  • Не поддерживаются складские остатки и скидки на отдельные предложения (потому что предложений физически не существует).
  • На каждое из предложений нельзя сделать отдельную ссылку (причина та же — предложений физически не существует).

Выводы

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

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

Вторая итерация была очень интересным экспериментом, показывающим, что в принципе, на платформе при помощи API можно создать что угодно. Но, в связи с переходным периодом (а сейчас платформа будет мигрировать на новое ядро и старое API перестанет обновляться), приходится думать о поддержке. К тому же может случиться так, что завтра понадобится складской учет элементов SKU: И если в первой итерации он есть, то во второй даже не планировался.

Оставить комментарий

Комментарии (8)

  1. timir 19.04.2018 Ответить
    подскажите пжл, есть товар с конфигурациями как у вас в статье, но при нажатии корзины, товар в корзину не добавляется
  2. Дмитрий
    Дмитрий 24.04.2018 Ответить
    До уровня Ванги я пока не дошел. Лечить пациентов по телефону тоже не умею.
    Проблем "почему товар не кладется в корзину" может быть великое множество и проявляться они могут на разных уровнях системы.
    Зависит это от реализации каталога.
    Вы можете оставить запрос на доработку функционала сайта через форму - klondike-studio.ru/order/
  3. Леонид Тропин 11.09.2018 Ответить
    1245 SKU у товара ─ это, конечно, сильно. Хотя у меня был случай, когда подобный подход нагенерировал бы число с 30 нулями торговых предложений . Так что подобный подход действительно не на каждый день.
    Но у меня вопрос по второму варианту. Как вы храните конфигурации цены и как цены формируются? Меня больше интересует "Комплектация кровати". Например, "ящики для белья". У этого есть отдельное SKU (в теории, ящик не так сильно зависит от остальных параметров) или же это просто строчка "Цена ящика" в настройках конфигуратора?
  4. Дмитрий
    Дмитрий 13.09.2018 Ответить
    Леонид, честно сказать - я уже не помню как точно организовано хранение данных. Точно писались доп свойства для связи разных сущностей (кровати, основания, ящики). Также различные параметры бились на инфоблоки/разделы, а элементы как раз и были параметрами.
    В табличном интерфейсе мы можем чекнуть ящик для кровати и установить его стоимость как опции. В БД оно будет сохранено через кастомное свойство состоящее из 2х частей: 1) id сущности, 2) наценка. Частей может быть и больше, например можно сохранять тип сущности, признак дефолтности и еще что-то (по потребностям).
    Если не предполагается каких-то выборок и фильтраций по самим вариантам товара (читай SKU) то данные вообще можно хранить в поле типа "строка", пакуем их в json, а в админке просто строим другой интерфейс для этого свойства. Изменение обрабатываем JSом или перехватываем при сохранении через события.
    П.С. Правильнее конечно свое свойство написать.
  5. Леонид Тропин 14.09.2018 Ответить
    Как мудрёно всё . Без полных исходных данных и их взаимосвязей и не разберёшься. Но так, очень похоже это всё на обычные Highload-блоки. Меня больше беспокоило то, что если цена не хранится там, где должна хранится цена (в торговом каталоге), то везде придётся писать её обработку ручками.
    А работает ли с вашим подходом обычная битриксовская выгрузка? Или как при таком подходе цены обновляются?
  6. Дмитрий
    Дмитрий 27.09.2018 Ответить
    Не суть важно как хранить, тут нужен был удобный (на сколько это возможно) интерфейс для менеджеров.
    1й вариант, основанный на SKU полностью поддерживается системой и взаимодействует со штатным функционалом. Это ведь по сути надстройка - редактор.
    2й вариант, чистой воды кастом и никак не поддерживается системой. Цены, выгрузки и прочее нужно писать самому.

    Плюсы и минусы очевидны:
    1й вариант полностью поддерживается штатным функционалом, но громоздкий и генерирует оооочень много данных, к слову на днях сайт перестал справляться с перегенерацией. Из-за большого объема данных генерируются SKU довольно медленно, на клиентской части тоже вызывают подвисания (не всегда, но случается).
    2й вариант генерирует минимум данных, работает в десятки раз быстрее, не зависает. Но не поддерживается системой.
    P.S. у каждого есть выбор =)
  7. Артём Лесков 30.03.2020 Ответить
    В вашей задаче не была предусмотрена интеграция с 1С, верно?
    Если бы она была, то ни одно из ваших решений не подходит?
  8. Дмитрий
    Дмитрий 28.05.2020 Ответить
    Конкретно в данном кейсе вопрос интеграции с 1С отсутствовал.
    Если говорить об 1С и SKU то нужно отметить то что в 1С есть штатный фунционал для ведения SKU. При штатном ведении SKU в 1С они также штатно зальются на сайт и никаких приколов с конфигуратором писать не придется.
    Если на стороне 1С SKU нет или они реализованы нештатным способом то придется писать постобработчик который "переосмыслит" данные пришедшие из 1С и передаст их в каталог в нужном виде.
    Опять же, если данные поступают из 1С то зачем конфигуратор на стороне сайта? Тут нужен был интерфейс при помощи которого можно "создавать" данные (товары).