УТ 11: Где хранится список номенклатуры в справочнике «Скидки (наценки)»?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При настройке маркетинговых акций в программе «1С:Управление торговлей» (редакция 11), а также в связанных решениях 1С:ERP и 1С:Комплексная автоматизация, специалисты часто сталкиваются с неочевидным поведением системы. Настроив скидку для определенного списка товаров, разработчики пытаются найти этот список в базе данных с помощью консоли запросов, но стандартные регистры оказываются пустыми. Выясним, почему так происходит, подробно проанализируем архитектуру хранения данных и разберем практические способы программного извлечения списков номенклатуры.

Три архитектурных варианта хранения номенклатуры в скидках

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

  1. Вариант «Список номенклатуры»

    В этом сценарии перечень товаров жестко задается пользователем в табличной части. При записи и активации скидки система транслирует эти данные в регистр сведений ДействиеСкидокНаценокПоНоменклатуре. Для каждой позиции номенклатуры (и для каждой характеристики отдельно, если включен их учет) создается физическая запись в таблице базы данных. Этот вариант прост для понимания и написания запросов, но имеет существенный минус: при объемах в десятки тысяч позиций регистр лавинообразно разрастается, что снижает производительность СУБД.

  2. Вариант «По сегменту»

    Скидка привязывается к определенному сегменту номенклатуры (справочник СегментыНоменклатуры). Сам состав сегмента формируется динамически или вручную и хранится в регистре сведений НоменклатураСегмента. Данный подход является наиболее оптимальным с точки зрения производительности СУБД, так как позволяет быстро накладывать условия в запросах расчета скидок без перебора индивидуальных позиций.

  3. Вариант «Отбор компоновки данных» (Номенклатура по отбору)

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

Разбор реквизита «ХранилищеНастроекКомпоновкиДанных»

Если для скидки выбран вариант отбора по условиям компоновки данных, правила фильтрации записываются в реквизит ХранилищеНастроекКомпоновкиДанных (тип значения — ХранилищеЗначения).

Рассмотрим, что находится внутри этого хранилища. Если программно вызвать метод Получить() у этого объекта, система вернет структуру, содержащую следующие ключи:

Программное извлечение списка отборов из СКД

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


// Выбираем конкретную скидку по коду или ссылке
СсылкаНаСкидку = Справочники.СкидкиНаценки.НайтиПоКоду("УТ-000001");

Если Не СсылкаНаСкидку.Пустая() Тогда
    
    // Получаем объект для доступа к реквизитам-хранилищам
    ОбъектСкидки = СсылкаНаСкидку.ПолучитьОбъект();
    
    // Извлекаем структуру настроек из двоичных данных
    СтруктураНастроек = ОбъектСкидки.ХранилищеНастроекКомпоновкиДанных.Получить();
    
    Если ТипЗнч(СтруктураНастроек) = Тип("Структура") Тогда
        
        // Нас интересует ключ ОтборПоНоменклатуре
        Если СтруктураНастроек.Свойство("ОтборПоНоменклатуре") Тогда
            
            НастройкиСКД = СтруктураНастроек.ОтборПоНоменклатуре;
            
            // Запускаем рекурсивный обход элементов отбора
            ОбходЭлементовОтбора(НастройкиСКД.Отбор.Элементы);
            
        КонецЕсли;
        
    КонецЕсли;
    
КонецЕсли;

Поскольку дерево отборов может содержать неограниченное количество вложенных групп (например, группа «ИЛИ», внутри которой находятся группы «И»), для корректного анализа структуры необходимо реализовать рекурсивную процедуру обхода элементов:


Процедура ОбходЭлементовОтбора(КоллекцияЭлементов) Экспорт
    
    Для Каждого Элемент Из КоллекцияЭлементов Цикл
        
        // Если элемент является группой условий (И, ИЛИ, НЕ)
        Если ТипЗнч(Элемент) = Тип("ГруппаЭлементовОтбораКомпоновкиДанных") Тогда
            
            Сообщить("--- Начало группы условий: " + Элемент.ТипГруппы);
            // Рекурсивный вызов для обхода вложенных условий
            ОбходЭлементовОтбора(Элемент.Элементы);
            Сообщить("--- Конец группы условий");
            
        // Если это конкретное условие сравнения
        ИначеЕсли ТипЗнч(Элемент) = Тип("ЭлементОтбораКомпоновкиДанных") Тогда
            
            ПолеЛевое = Строка(Элемент.ЛевоеЗначение);
            ВидСравнения = Строка(Элемент.ВидСравнения);
            ПравоеЗначение = Элемент.ПравоеЗначение;
            
            // Обрабатываем списки значений, если условие "В списке"
            Если ТипЗнч(ПравоеЗначение) = Тип("СписокЗначений") Тогда
                ЗначенияСписка = "";
                Для Каждого ЭлементСписка Из ПравоеЗначение Цикл
                    ЗначенияСписка = ЗначенияСписка + ЭлементСписка.Значение + "; ";
                КонецЦикла;
                Сообщить(Шаблон("Отбор: %1 | %2 | [%3]", ПолеЛевое, ВидСравнения, ЗначенияСписка));
            Иначе
                Сообщить(Шаблон("Отбор: %1 | %2 | %3", ПолеЛевое, ВидСравнения, ПравоеЗначение));
            КонецЕсли;
            
        КонецЕсли;
        
    КонецЦикла;
    
КонецПроцедуры

Как система вычисляет применимость скидки «на лету»

Многих разработчиков интересует вопрос: если физических записей по товарам в базе данных нет, каким образом документ продажи понимает, что на конкретную позицию действует скидка?

Разобрав код общего модуля СкидкиНаценкиСервер, мы увидим следующий алгоритм работы ядра системы:

  1. В момент расчета скидок в документе (например, при нажатии кнопки «Рассчитать скидки» или при проведении документа) вызывается экспортная функция ВыполнитьЗапросыУсловийПредоставления.
  2. Программа анализирует дерево всех действующих в данный момент скидок в соответствии с установленным расписанием и маркетинговыми документами.
  3. Если у действующей скидки установлен вариант отбора по компоновке, система считывает правила из реквизита ХранилищеНастроекКомпоновкиДанных.
  4. На основе прочитанной схемы СКД платформа динамически генерирует SQL-запрос. Этот запрос накладывает условия отбора непосредственно на временную таблицу товаров, переданную из табличной части документа продажи.
  5. Позиции, прошедшие проверку динамическим запросом, получают признак применимости данной скидки.

Проблема анализа и оптимизация архитектуры для разработчиков

Главная сложность текущей архитектуры 1С:УТ 11 заключается в **невозможности написать простой и быстрый запрос** к базе данных, который выдаст ответ на задачу: «В каких скидках по отбору участвует вот этот конкретный товар?».

Поскольку данные хранятся внутри сжатых бинарных объектов (Blob-поля СУБД), сервер баз данных не может проиндексировать эти условия. Чтобы решить такую задачу, разработчику приходится:

Это крайне ресурсоемкая операция, которая может сильно нагрузить сервер при частом использовании (например, при выгрузке каталога товаров с ценами на сайт или в мобильное приложение).

Рекомендация по оптимизации: Если перед вашим бизнесом стоит задача регулярного анализа применимости скидок в разрезе товаров или требуется частая интеграция с внешними системами, настоятельно рекомендуется отказаться от использования «Отбора компоновки данных» внутри скидок. Переведите маркетинговые инструменты на использование **Сегментов номенклатуры**. В этом случае все данные будут храниться в плоской структурированной таблице регистра НоменклатураСегмента, откуда их можно мгновенно извлечь простым и быстрым запросом.

← На главную