При настройке маркетинговых акций в программе «1С:Управление торговлей» (редакция 11), а также в связанных решениях 1С:ERP и 1С:Комплексная автоматизация, специалисты часто сталкиваются с неочевидным поведением системы. Настроив скидку для определенного списка товаров, разработчики пытаются найти этот список в базе данных с помощью консоли запросов, но стандартные регистры оказываются пустыми. Выясним, почему так происходит, подробно проанализируем архитектуру хранения данных и разберем практические способы программного извлечения списков номенклатуры.
То, где физически будут находиться данные о товарах, участвующих в скидке, целиком зависит от реквизита ВариантОтбораНоменклатуры в справочнике СкидкиНаценки. Система поддерживает три принципиально разных механизма работы:
В этом сценарии перечень товаров жестко задается пользователем в табличной части. При записи и активации скидки система транслирует эти данные в регистр сведений ДействиеСкидокНаценокПоНоменклатуре. Для каждой позиции номенклатуры (и для каждой характеристики отдельно, если включен их учет) создается физическая запись в таблице базы данных. Этот вариант прост для понимания и написания запросов, но имеет существенный минус: при объемах в десятки тысяч позиций регистр лавинообразно разрастается, что снижает производительность СУБД.
Скидка привязывается к определенному сегменту номенклатуры (справочник СегментыНоменклатуры). Сам состав сегмента формируется динамически или вручную и хранится в регистре сведений НоменклатураСегмента. Данный подход является наиболее оптимальным с точки зрения производительности СУБД, так как позволяет быстро накладывать условия в запросах расчета скидок без перебора индивидуальных позиций.
Именно этот вариант чаще всего вызывает недоумение у разработчиков. Пользователь настраивает гибкий отбор в режиме компоновщика настроек (например, «Вид номенклатуры Равно Продукты И Производитель Равно Сладкий мир»). При этом в регистрах сведений не создается ни одной записи. Сама скидка при этом прекрасно рассчитывается в документах продажи. Данные о правилах отбора упаковываются в бинарный формат и сохраняются непосредственно в реквизите элемента справочника.
Если для скидки выбран вариант отбора по условиям компоновки данных, правила фильтрации записываются в реквизит ХранилищеНастроекКомпоновкиДанных (тип значения — ХранилищеЗначения).
Рассмотрим, что находится внутри этого хранилища. Если программно вызвать метод Получить() у этого объекта, система вернет структуру, содержащую следующие ключи:
ДополнительныеУсловия — объект типа НастройкиКомпоновкиДанных, отвечающий за общие параметры компоновщика.Запрос — служебная структура метаданных, описывающая правила построения динамических запросов.ОтборПоНоменклатуре — непосредственно настройки компоновки данных (НастройкиКомпоновкиДанных), в которых содержится дерево элементов отбора (группы И, ИЛИ, НЕ, конкретные значения, сравнения в группе и т.д.).Проанализируем ситуацию, когда нам необходимо программно прочитать условия отбора номенклатуры из конкретной скидки. Напишем алгоритм, который извлекает структуру настроек компоновки и обходит элементы отбора. Посмотрим на пример кода:
// Выбираем конкретную скидку по коду или ссылке
СсылкаНаСкидку = Справочники.СкидкиНаценки.НайтиПоКоду("УТ-000001");
Если Не СсылкаНаСкидку.Пустая() Тогда
// Получаем объект для доступа к реквизитам-хранилищам
ОбъектСкидки = СсылкаНаСкидку.ПолучитьОбъект();
// Извлекаем структуру настроек из двоичных данных
СтруктураНастроек = ОбъектСкидки.ХранилищеНастроекКомпоновкиДанных.Получить();
Если ТипЗнч(СтруктураНастроек) = Тип("Структура") Тогда
// Нас интересует ключ ОтборПоНоменклатуре
Если СтруктураНастроек.Свойство("ОтборПоНоменклатуре") Тогда
НастройкиСКД = СтруктураНастроек.ОтборПоНоменклатуре;
// Запускаем рекурсивный обход элементов отбора
ОбходЭлементовОтбора(НастройкиСКД.Отбор.Элементы);
КонецЕсли;
КонецЕсли;
КонецЕсли;
Поскольку дерево отборов может содержать неограниченное количество вложенных групп (например, группа «ИЛИ», внутри которой находятся группы «И»), для корректного анализа структуры необходимо реализовать рекурсивную процедуру обхода элементов:
Процедура ОбходЭлементовОтбора(КоллекцияЭлементов) Экспорт
Для Каждого Элемент Из КоллекцияЭлементов Цикл
// Если элемент является группой условий (И, ИЛИ, НЕ)
Если ТипЗнч(Элемент) = Тип("ГруппаЭлементовОтбораКомпоновкиДанных") Тогда
Сообщить("--- Начало группы условий: " + Элемент.ТипГруппы);
// Рекурсивный вызов для обхода вложенных условий
ОбходЭлементовОтбора(Элемент.Элементы);
Сообщить("--- Конец группы условий");
// Если это конкретное условие сравнения
ИначеЕсли ТипЗнч(Элемент) = Тип("ЭлементОтбораКомпоновкиДанных") Тогда
ПолеЛевое = Строка(Элемент.ЛевоеЗначение);
ВидСравнения = Строка(Элемент.ВидСравнения);
ПравоеЗначение = Элемент.ПравоеЗначение;
// Обрабатываем списки значений, если условие "В списке"
Если ТипЗнч(ПравоеЗначение) = Тип("СписокЗначений") Тогда
ЗначенияСписка = "";
Для Каждого ЭлементСписка Из ПравоеЗначение Цикл
ЗначенияСписка = ЗначенияСписка + ЭлементСписка.Значение + "; ";
КонецЦикла;
Сообщить(Шаблон("Отбор: %1 | %2 | [%3]", ПолеЛевое, ВидСравнения, ЗначенияСписка));
Иначе
Сообщить(Шаблон("Отбор: %1 | %2 | %3", ПолеЛевое, ВидСравнения, ПравоеЗначение));
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Многих разработчиков интересует вопрос: если физических записей по товарам в базе данных нет, каким образом документ продажи понимает, что на конкретную позицию действует скидка?
Разобрав код общего модуля СкидкиНаценкиСервер, мы увидим следующий алгоритм работы ядра системы:
ВыполнитьЗапросыУсловийПредоставления.ХранилищеНастроекКомпоновкиДанных.Главная сложность текущей архитектуры 1С:УТ 11 заключается в **невозможности написать простой и быстрый запрос** к базе данных, который выдаст ответ на задачу: «В каких скидках по отбору участвует вот этот конкретный товар?».
Поскольку данные хранятся внутри сжатых бинарных объектов (Blob-поля СУБД), сервер баз данных не может проиндексировать эти условия. Чтобы решить такую задачу, разработчику приходится:
ХранилищеНастроекКомпоновкиДанных.Это крайне ресурсоемкая операция, которая может сильно нагрузить сервер при частом использовании (например, при выгрузке каталога товаров с ценами на сайт или в мобильное приложение).
Рекомендация по оптимизации: Если перед вашим бизнесом стоит задача регулярного анализа применимости скидок в разрезе товаров или требуется частая интеграция с внешними системами, настоятельно рекомендуется отказаться от использования «Отбора компоновки данных» внутри скидок. Переведите маркетинговые инструменты на использование **Сегментов номенклатуры**. В этом случае все данные будут храниться в плоской структурированной таблице регистра НоменклатураСегмента, откуда их можно мгновенно извлечь простым и быстрым запросом.