Как применить процент наценки родительской группы ко всем вложенным подгруппам и товарам в 1С?

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

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

Почему не работает прямое обращение через «Родитель» и оператор «В ИЕРАРХИИ» в соединениях

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

  1. Ограничение глубины одного уровня: Конструкция Ссылка.Родитель.ПроцентНаценки обращается только к непосредственному родителю текущего элемента. Если товар находится в подгруппе второго или третьего уровня, а процент наценки задан в корневой группе, у непосредственного родителя значение наценки будет равно нулю или не заполнено. Попытка прописать Ссылка.Родитель.Родитель лишь жестко фиксирует проверку на определенную глубину, ломая логику для элементов с другой структурой вложенности.
  2. Направление действия оператора В ИЕРАРХИИ: В языке запросов 1С оператор В ИЕРАРХИИ работает по направлению «сверху вниз» (проверяет, входит ли объект в указанный узел-родитель). При использовании условия соединения вида ПО НаценкиГруппы.Номенклатура В ИЕРАРХИИ (&НоменклатураГруппа) платформа ищет подчиненные записи относительно параметра, а не родителей текущей позиции.
  3. Проблема производительности: Обращение к реквизитам через несколько точек (например, ТС.Номенклатура.Родитель.ПроцентНаценки) внутри цикла обхода строк табличной части генерирует неявные одиночные запросы к СУБД для каждой строки, что сильно замедляет проведение документов.

Способ 1. Получение цепочки родителей на встроенном языке (для одного элемента)

Если требуется получить действующую наценку для конкретной номенклатуры при интерактивном открытии карточки или расчете единичной позиции, соберем всех родителей элемента в массив с помощью цикла по свойству Родитель.

Посмотрим на пример кода:


МассивРодителей = Новый Массив;
ТекРодитель = НоменклатураСсылка.Родитель;

Пока ЗначениеЗаполнено(ТекРодитель) Цикл
    МассивРодителей.Добавить(ТекРодитель);
    ТекРодитель = ТекРодитель.Родитель;
КонецЦикла;

Если МассивРодителей.Количество() > 0 Тогда
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |    Рег.Номенклатура КАК Группа,
        |    Рег.ПроцентНаценки КАК ПроцентНаценки
        |ИЗ
        |    РегистрСведений.НаценкиПоПодразделениям КАК Рег
        |ГДЕ
        |    Рег.Номенклатура В (&МассивРодителей)
        |    И Рег.Подразделение = &Подразделение";
    
    Запрос.УстановитьПараметр("МассивРодителей", МассивРодителей);
    Запрос.УстановитьПараметр("Подразделение", ПодразделениеСсылка);
    
    ТЗНаценки = Запрос.Выполнить().Выгрузить();
    
    // Ищем наценку от ближайшего родителя к корню
    Для Каждого Группа Из МассивРодителей Цикл
        НайденнаяСтрока = ТЗНаценки.Найти(Группа, "Группа");
        Если НайденнаяСтрока <> Неопределено И НайденнаяСтрока.ПроцентНаценки <> 0 Тогда
            НаценкаГруппы = НайденнаяСтрока.ПроцентНаценки;
            Прервать;
        КонецЕсли;
    КонецЦикла;
КонецЕсли;

Способ 2. Разворачивание иерархии в запросе для списка товаров

Рассмотрим оптимальный подход для групповой обработки строк (например, при проведении документов «Изменение цен» или расчете цен по накладной). За один запрос выбираются все товары и соединяются со своими родителями на несколько уровней вверх. Действующая наценка определяется с помощью функции ЕСТЬNULL с учетом приоритета: товар → родитель 1-го уровня → родитель 2-го уровня → родитель 3-го уровня.

Разберем пример пакетного запроса:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    Товары.Ссылка КАК Номенклатура,
    |    Товары.Родитель КАК Родитель1,
    |    Товары.Родитель.Родитель КАК Родитель2,
    |    Товары.Родитель.Родитель.Родитель КАК Родитель3
    |ПОМЕСТИТЬ ВТ_Товары
    |ИЗ
    |    Справочник.Номенклатура КАК Товары
    |ГДЕ
    |    Товары.Ссылка В (&СписокТоваров)
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |    ВТ.Номенклатура КАК Номенклатура,
    |    ЕСТЬNULL(НацТовара.ПроцентНаценки,
    |        ЕСТЬNULL(НацРод1.ПроцентНаценки,
    |            ЕСТЬNULL(НацРод2.ПроцентНаценки,
    |                ЕСТЬNULL(НацРод3.ПроцентНаценки, 0)))) КАК ИтоговаяНаценка
    |ИЗ
    |    ВТ_Товары КАК ВТ
    |    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.НаценкиПоПодразделениям КАК НацТовара
    |        ПО НацТовара.Номенклатура = ВТ.Номенклатура 
    |        И НацТовара.Подразделение = &Подразделение
    |    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.НаценкиПоПодразделениям КАК НацРод1
    |        ПО НацРод1.Номенклатура = ВТ.Родитель1 
    |        И НацРод1.Подразделение = &Подразделение
    |    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.НаценкиПоПодразделениям КАК НацРод2
    |        ПО НацРод2.Номенклатура = ВТ.Родитель2 
    |        И НацРод2.Подразделение = &Подразделение
    |    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.НаценкиПоПодразделениям КАК НацРод3
    |        ПО НацРод3.Номенклатура = ВТ.Родитель3 
    |        И НацРод3.Подразделение = &Подразделение";

Запрос.УстановитьПараметр("СписокТоваров", ТаблицаТоваров.ВыгрузитьКолонку("Номенклатура"));
Запрос.УстановитьПараметр("Подразделение", Подразделение);

РезультатНаценок = Запрос.Выполнить().Выгрузить();

Способ 3. Проверка подчиненности через метод «ПринадлежитЭлементу»

Для объектной модели данных в 1С предусмотрен встроенный метод ссылки ПринадлежитЭлементу(). Он проверяет подчиненность элемента группе на любом уровне вложенности без необходимости вручную раскручивать дерево родителей.

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


Если НоменклатураСсылка.ПринадлежитЭлементу(ГруппаСНаценкой) Тогда
    // Товар входит в группу или в любую из ее подгрупп
    ПроцентНаценки = ПолучитьНаценкуГруппы(ГруппаСНаценкой);
КонецЕсли;

Важно: Метод ПринадлежитЭлементу() выполняет последовательное считывание всех вышестоящих элементов из базы данных на момент вызова. Применяйте его точечно, избегая массового использования внутри больших циклов.

Способ 4. Архитектурная альтернатива — «Ценовые группы»

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

  1. В справочнике Номенклатура создается реквизит ЦеноваяГруппа (ссылка на отдельный справочник).
  2. Наценки в регистрах сведений назначаются не на папки каталога, а на значения ценовых групп.
  3. При записи номенклатуры в подгруппу значение реквизита наследуется, либо устанавливается индивидуально. В результате в запросах исчезает потребность разворачивать цепочки родителей, что значительно повышает скорость проведения документов.
← На главную