Перейти к содержимому

Дилерский доступ - свои цены, свой каталог, документы

Открываем дилерам свои цены и свой каталог, не заводя второй сайт: группа, тип цены, права на разделы и закрытые документы.

Механика

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

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

Дилер появляется не сам. Заявку на дилерство подтверждает менеджер, и только после этого учётная запись попадает в нужную группу. Автоматическое зачисление по галочке в форме превращает дилерские цены в публичные за неделю.

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

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

Стоит заранее решить, что дилер видит в личном кабинете. Обычно это его заказы, его документы и его условия, а не общий раздел с чужими данными; и чем раньше это описано словами, тем меньше потом переделок в правах и в шаблонах кабинета.

Шаги

  1. Завести группу дилеров и тип цены, доступный только ей.
  2. Настроить права на тип цены и проверить их на трёх ролях.
  3. Скрыть от розницы те разделы и товары, которые ей не предназначены.
  4. Разделить кэш витрины по группам пользователей.
  5. Отдавать документы дилера через скрипт с проверкой прав.
  6. Сделать заявку на дилерство с подтверждением менеджером.

Код

Заводим тип цены с правами:

\Bitrix\Main\Loader::includeModule('catalog');
$priceTypeId = \Bitrix\Catalog\GroupTable::add(['NAME' => 'DEALER',
'BASE' => 'N', 'SORT' => 200])->getId();
// право на чтение этого типа цены выдаётся конкретной группе пользователей
\Bitrix\Catalog\GroupAccessTable::add(['CATALOG_GROUP_ID' => $priceTypeId,
'GROUP_ID' => $dealerGroupId, 'ACCESS' => 'Y']);

Дилерская цена - это тип цены с правом на группу. Права задаются один раз, и дальше платформа сама решает, какую цену показать конкретному посетителю.

Показываем доступную цену на витрине:

'PRICE_CODE' => ['BASE', 'DEALER'], // перечисляем оба типа
'USE_PRICE_COUNT' => 'N',
// компонент покажет ту цену, которая доступна текущей группе пользователя
// если доступны обе - показывают минимальную или ту, что задана порядком

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

Скрываем часть каталога от розницы:

$filter = ['IBLOCK_ID' => 12, 'ACTIVE' => 'Y'];
if (!in_array($dealerGroupId, $USER->GetUserGroupArray())) {
$filter['!PROPERTY_DEALER_ONLY_VALUE'] = 'Y'; // рознице этих позиций не видно
}
// то же самое делают правами на разделы: они надёжнее фильтра в коде

Часть товаров и разделов дилерам показывают отдельно. Фильтр в коде проще, а права на разделы надёжнее: их невозможно обойти прямой ссылкой на карточку товара из поисковой выдачи.

Разделяем кэш по группам:

$groups = $USER->GetUserGroupArray();
sort($groups);
$cache->initCache(3600, 'catalog_' . $sectionId . '_' . implode('_', $groups), '/vendor/catalog');
// без группы в ключе первый же дилер оставит свою цену в кэше для всех

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

Отдаём документы с проверкой прав:

if (!in_array($dealerGroupId, $USER->GetUserGroupArray())) {
\Bitrix\Main\Application::getInstance()->getContext()->getResponse()->setStatus('403 Forbidden');
return;
}
header('X-Accel-Redirect: /protected/price-dealer.xlsx'); // файл вне корня сайта
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment; filename="price-dealer.xlsx"');
// скачивание закрытых документов полезно писать в журнал: кто и когда забирал

Прайсы и договоры отдают с проверкой прав. Файл при этом лежит вне открытого каталога: проверка в коде бессмысленна, пока к документу ведёт прямая ссылка.

Принимаем заявку на дилерство:

$id = DealerRequestTable::add(['USER_ID' => $USER->GetID(), 'COMPANY' => $company,
'INN' => $inn, 'STATUS' => 'new'])->getId();
\Bitrix\Main\Mail\Event::send(['EVENT_NAME' => 'DEALER_REQUEST', 'LID' => SITE_ID,
'C_FIELDS' => ['ID' => $id, 'COMPANY' => $company]]);
// в группу дилеров пользователя добавляет менеджер, а не форма
CUser::SetUserGroup($userId, array_merge($USER->GetUserGroupArray(), [$dealerGroupId]));
// этот вызов живёт в обработчике подтверждения заявки, а не в самой форме

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

Ограничения

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

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

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

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

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

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

Типичные проблемы

Розничный покупатель увидел дилерскую цену.

Кэш витрины собран без учёта групп пользователя. В ключ кэша обязательно добавляют состав групп.

Дилер видит розничную цену.

Право на тип цены не выдано его группе или тип не перечислен в вызове компонента. Проверяют и права, и параметры.

Закрытый прайс нашёлся в поиске.

Файл лежит в открытом каталоге, а проверка прав стоит только на странице. Такие файлы держат вне корня сайта.

Дилерами стали все, кто заполнил форму.

Форма сама добавляет пользователя в группу. Решение принимает менеджер после проверки реквизитов.

После обмена дилерские цены пропали.

Выгрузка не знает про новый тип цены и заполняет только базовый. Тип цены добавляют в настройки обмена.

Частые вопросы

Чем тип цены лучше скидки для дилеров?

Он не пересчитывается правилами корзины и виден только своей группе. Скидка при этом остаётся инструментом акций.

Как скрыть товары от розницы?

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

Нужен ли отдельный сайт для дилеров?

Обычно нет: группа, тип цены и права закрывают задачу. Второй сайт нужен, если отличается всё, включая вёрстку.

Как проверить настройку?

Открыть каталог тремя способами: гостем, розничным покупателем и дилером. Только это и доказывает, что права работают.

Что делать с ушедшим дилером?

Убирать из группы: цена и документы закроются сами. Учётную запись при этом обычно оставляют.

Смежное

Первоисточник