Компания продаёт автосервисное оборудование и расходные материалы одновременно рознице, оптовым клиентам, дилерам и партнёрам. У каждой группы покупателей свои условия: у кого-то процент от прайс-листа, у кого-то отдельная согласованная цена, у кого-то - индивидуальные условия на отдельные категории товаров. Всё это ведётся в 1С:ERP и там же меняется - дублировать условия на сайте никто не должен.
Требование к витрине звучит просто, но стандартными средствами платформы его не выполнить.
Один каталог, одна карточка товара - и каждый покупатель видит свою цену. Розница не должна видеть дилерские условия, а дилер не должен искать свою цену в PDF-прайсе.
Почему это не решается настройками магазина
Стандартный интернет-магазин связывает цену с группой пользователей: "этой группе - такая-то ценовая колонка". Такая связка плоская и покрывает только простые случаи. В реальной коммерческой политике компании оказалось три требования, которых в ней нет.
- →Условия зависят сразу от двух вещей. Не только от того, кто покупатель, но и от того, что он покупает: на шиноремонтные материалы у дилера одни условия, на автохимию - другие. Это два измерения, а стандартная связка "группа - цена" знает только одно.
- →Покупателю доступна не одна цена, а несколько. У сегмента одновременно есть личная согласованная цена, общая розничная и цена распродажи. Вопрос "какую из них показать" платформа не решает - она отдаёт цену конкретной колонки.
- →Фильтровать и сортировать нужно по цене, которой ещё нет. Пока не известно, кто перед нами, неизвестна и цена товара. А покупателю нужен фильтр "от 30 до 50 тысяч" и сортировка по возрастанию - по его ценам, а не по общим.
Решение
Одна витрина - у каждого своя цена
Коммерческая политика остаётся в 1С. На сайте её никто не заводит руками: правила и суммы приезжают из 1С сами и обновляются в течение минут после изменения.
Что приходит из 1С
Из 1С приходят две разные вещи, и это разделение - ключевое. Правила доступа отвечают на вопрос "кому что видно": какому сегменту покупателей на какой категории товаров какая ценовая колонка доступна. Сейчас таких правил 741 - это 19 сегментов, помноженные на 13 категорий товаров и на 3 доступные колонки. Суммы - это сами цены: "товар, колонка, сумма". Таких записей больше 200 тысяч, и у ходового товара может быть до 22 колонок одновременно.
Отдельно стоит правило-сокращение: если в правиле не указана категория товаров, оно автоматически разворачивается на все категории каталога. Так из одной строки в 1С включается распродажа по всему ассортименту - перечислять категории вручную не нужно.
Правило минимума
Это правило снимает целый класс ручной работы. Когда начинается распродажа, никто не проверяет, у кого личная цена оказалась выгоднее акционной. Оптовик с личной ценой около 42 500 ₽ во время распродажи получает цену около 37 000 ₽ - система сама отдаёт то, что выгоднее клиенту. Когда распродажа кончится, он вернётся к своей цене.
Путь цены от 1С до ценника
Сайт проверяет очередь выгрузок каждые ~10 секунд, поэтому новая выгрузка из 1С попадает в работу за секунды: цены, остатки и склады на витрине обновляются с той частотой, с какой их отдаёт 1С, а не по расписанию сайта. После каждой выгрузки цен сайт пересчитывает персональные цены изменившихся товаров - для всех сегментов сразу - и складывает результат в готовую таблицу.
Почему цены считаются заранее
Считать цену в момент показа страницы: чтобы отобрать товары "от 30 до 50 тысяч", пришлось бы посчитать цену каждого товара раздела заново - на каждый показ и для каждого посетителя.
Цены посчитаны заранее, сразу после выгрузки из 1С: около 220 тысяч готовых ценников хранятся в оперативной памяти сервера - витрина ничего не вычисляет, она берёт готовое.
Это главное техническое решение кейса. Готовая таблица хранит по одной строке на сочетание "товар + сегмент + раздел каталога", поэтому фильтр по цене, сортировка и надпись "Цена от…" на разделе работают по личным ценам покупателя и не тормозят каталог.
Как это устроено внутри
| Слой | Решение |
|---|---|
| Приём данных | Отдельный сервис синхронизации разбирает выгрузки 1С и пишет правила доступа в справочник, а суммы - в стандартные цены каталога |
| Правила доступа | Highload-справочник "сегмент × категория × ценовая колонка"; развёртка правила без категории на все категории выполняется при импорте, поэтому на витрине правила всегда конкретны |
| Сегмент покупателя | Свойство пользователя, а не группа доступа: гость и непрошедший модерацию клиент получают сегмент по умолчанию, прошедший - свой |
| Расчёт | Пакетный расчёт по всем сегментам сразу: цена сегмента - минимум из разрешённых правилами колонок; зачёркнутая - максимальная цена товара; выгода - разница между ними |
| Хранение | Денормализованная таблица "товар × сегмент × раздел" в оперативной памяти, с составным индексом под фильтр и сортировку в разрезе раздела |
| Витрина | Каталог не обращается к ценам напрямую - только к готовой таблице; сортировка и пагинация по цене выполняются поверх стандартного компонента |
| Пересчёт | Сервис синхронизации после записи цен вызывает пересчёт на стороне сайта по списку изменившихся товаров |
Почему сегмент - не группа пользователей. Группы доступа Битрикса плоские: они не умеют выразить "дилеру на инструмент - одна колонка, на расходники - другая". Сегмент вынесен в отдельный справочник, синхронизируемый с 1С, а доступ к колонкам решает матрица правил. Заодно это развязывает права доступа на сайте и коммерческую сегментацию: новый сегмент в 1С не требует заводить группу пользователей и раздавать права.
Почему скидка вычисляется, а не назначается. 1С присылает готовые суммы по колонкам - никаких процентов на стороне сайта не применяется. Процент выгоды выводится из разницы между самой дорогой колонкой товара и ценой сегмента и служит только витриной ("было / стало"). Это исключает класс ошибок, когда сайт и 1С считают скидку по-разному: единственный источник истины - суммы из 1С.
Что это дало бизнесу
| Раньше | Стало |
|---|---|
| Прайс-листы для дилеров рассылались файлами и устаревали | Дилер видит актуальную цену на витрине; условия меняются в 1С и приезжают за минуты |
| Одна витрина не могла обслуживать розницу и опт одновременно | Один каталог на все сегменты; дилерские условия не видны посторонним |
| Запуск распродажи требовал проверки, у кого личная цена выгоднее | Правило минимума решает это автоматически, для всех сегментов сразу |
| Индивидуальные условия на категорию товаров вести было негде | Условия задаются парой "сегмент + категория товаров" и приходят из 1С |
| Фильтр и сортировка по цене считали бы по единой цене - и вводили покупателя в заблуждение | Фильтр "от - до", сортировка и "Цена от…" считают личные цены покупателя |
| Появление нового сегмента - это доработка | Новый сегмент заводится в 1С; сайт подхватывает его и рассчитывает цены сам |
В цифрах
| Показатель | Значение |
|---|---|
| Сегментов покупателей | 19 |
| Категорий товаров в правилах | 13 |
| Правил доступа к ценам | 741 |
| Записей о ценах товаров | более 200 тысяч |
| Ценовых колонок у ходового товара | до 22 одновременно |
| Товаров в каталоге | более 13 000 активных |
| Готовых персональных ценников | около 220 тысяч |
| Разделов каталога в расчёте | 585 |
| Подхват выгрузок сайтом | очередь проверяется каждые ~10 секунд; свежесть цен равна частоте выгрузки из 1С |
| Разброс цены одного товара по сегментам | примерно от 29 500 до 45 000 ₽ (пример из рабочего каталога) |
Эта витрина - часть большой B2B-платформы: каталог, дилерский кабинет и двусторонний обмен с 1С. Что вошло в проект целиком - в кейсе B2B e-commerce платформы.



