Как в СКД реализовать расчет количества с учетом вложенности иерархии (умножение на коэффициент родителя)

Программист 1С v8.3 (Обычные формы) Управленческий учет Промышленность, строительство и АПК
← На главную

В процессе разработки отчетов по производственным спецификациям или сложным вложенным структурам программисты 1С часто сталкиваются с задачей «разузлования». Суть проблемы заключается в том, что стандартные механизмы агрегации в Системе компоновки данных (СКД) отлично справляются с суммированием, но не имеют встроенной функции для произведения значений по иерархической ветке. Например, если для сборки одного комплекта мебели требуется 5 обеденных групп, а в каждую группу входит 4 стула, то на выходе мы должны получить 20 стульев. Если же внутри стула есть еще вложения (ножки, винты), расчет становится многоуровневым.

Разберем подробно, как решить эту задачу различными методами: от программной подготовки данных до использования продвинутых функций СКД.

Метод 1. Программная подготовка данных с использованием рекурсии

Рассмотрим наиболее гибкий и понятный способ, который применила автор темы. Если структура иерархии заранее не определена и количество вложений может быть любым, мы можем подготовить данные во внешней таблице значений (ТЗ) перед передачей их в СКД. Основная идея заключается в использовании рекурсивной процедуры, которая проходит по всем уровням спецификации и вычисляет накопленный множитель.

Проанализируем пример кода, который выполняет эту задачу:


Процедура Посчитать(ТаблицаРезультат, ССпецификация, Множитель = 1)
    // Обходим все комплектующие текущей спецификации
    Для Каждого СтрокаСпецификации Из ССпецификация.ИсходныеКомплектующие Цикл
        
        // Пытаемся найти, не обрабатывали ли мы уже эту номенклатуру в данном контексте
        ПараметрыПоиска = Новый Структура("Спецификация, СпецификацияРодитель, Номенклатура", 
            СтрокаСпецификации.Спецификация, ССпецификация, СтрокаСпецификации.Номенклатура);
        ЕстьСтрока = ТаблицаРезультат.НайтиСтроки(ПараметрыПоиска);
        
        Если ЕстьСтрока.Количество() = 0 Тогда
            НоваяСтрока = ТаблицаРезультат.Добавить;
            // Важный момент: передаем накопленный множитель
            НоваяСтрока.Множитель = Множитель;
            НоваяСтрока.СпецификацияРодитель = ССпецификация;
        Иначе
            НоваяСтрока = ЕстьСтрока[0];
            НоваяСтрока.Множитель = НоваяСтрока.Множитель * Множитель;
        КонецЕсли;
        
        НоваяСтрока.Спецификация = СтрокаСпецификации.Спецификация;
        НоваяСтрока.Номенклатура = СтрокаСпецификации.Номенклатура;
        // Записываем базовое количество из спецификации
        НоваяСтрока.СКоличество = СтрокаСпецификации.Количество;
        
        // Если у комплектующей есть своя спецификация — заходим глубже (рекурсия)
        Если НЕ СтрокаСпецификации.Спецификация = Справочники.СпецификацииНоменклатуры.ПустаяСсылка Тогда
            Посчитать(ТаблицаРезультат, СтрокаСпецификации.Спецификация, СтрокаСпецификации.Количество * Множитель);
        КонецЕсли;
        
    КонецЦикла;
КонецПроцедуры

В данном алгоритме параметр Множитель передает вниз по дереву накопленный коэффициент. На каждом уровне вложенности количество из текущей строки умножается на множитель, пришедший от родителя. Итоговое количество в СКД будет рассчитываться как СКоличество * Множитель.

Метод 2. Использование математических свойств логарифмов в СКД

Выясним, как можно решить задачу без написания сложного кода, используя только возможности языка запросов и ресурсов СКД. Поскольку произведение чисел равно экспоненте от суммы их логарифмов (A * B * C = exp(ln(A) + ln(B) + ln(C))), мы можем «обмануть» систему, превратив операцию умножения в операцию сложения.

  1. В наборе данных создадим поле ЛогарифмКоличества, которое вычисляется как LOG(Количество).
  2. В закладке «Ресурсы» укажем для этого поля выражение Сумма(ЛогарифмКоличества).
  3. Создадим итоговое вычисляемое поле РезультатКоличество с выражением EXP(Сумма(ЛогарифмКоличества)).

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

Метод 3. Функция «ВычислитьВыражение» в СКД

Рассмотрим более современный и «нативный» для СКД способ — использование функции ВычислитьВыражение с контекстом Иерархия. Этот метод позволяет получить доступ к данным вышестоящих уровней прямо в процессе компоновки.

В поле ресурса можно прописать выражение следующего вида:

ВычислитьВыражение("Количество", "Иерархия", , "Первая", "Текущая")

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


Функция ПеремножитьМассив(МассивЗначений) Экспорт
    Результат = 1;
    Для Каждого Значение Из МассивЗначений Цикл
        Результат = Результат * Значение;
    КонецЦикла;
    Возврат Результат;
КонецФункции

В СКД итоговое выражение будет выглядеть так: ОбщийМодуль.ПеремножитьМассив(ВычислитьВыражение("Количество", "Иерархия", , "Первая", "Текущая")).

Метод 4. Рекурсивные запросы (ОТВ) для платформы 8.3.27+

Проанализируем ситуацию для самых свежих версий платформы 1С. С появлением Обобщенных Табличных Выражений (CTE) задача разузлования решается одним-единственным запросом на стороне сервера СУБД. Мы можем описать рекурсивную часть запроса, где в каждой итерации количество текущего узла умножается на накопленный множитель родителя.

Пример структуры такого запроса (псевдокод):


ВЫБРАТЬ ... ИЗ Справочник.Спецификации КАК Начало
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ ... Начало.Количество * Присоединенный.Количество ...
ИЗ Начало СОЕДИНЕНИЕ Справочник.Спецификации КАК Присоединенный

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

Важные замечания и безопасность

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

  1. Зацикливание: В реальных базах (особенно в УПП или ERP) пользователи могут ошибочно указать изделие А в составе изделия Б, а Б — в составе А. При использовании рекурсии (Метод 1) это приведет к бесконечному циклу и падению рабочего процесса. Всегда добавляйте проверку на вхождение или ограничивайте глубину рекурсии (например, 50 уровней).
  2. Типовые механизмы: Перед тем как писать свое решение, проверьте наличие типовых функций. В УПП существуют общие модули для управления запасами, а в ERP — обработка ДеревоРесурсныхСпецификаций. Иногда проще вызвать штатную функцию разузлования, которая уже умеет учитывать кратность, доли стоимости и минимальные партии.
  3. Производительность: Если дерево спецификаций очень глубокое (сотни уровней), программный обход в цикле (Метод 1) может работать медленно. В таких случаях лучше отдавать предпочтение Методу 2 (логарифмы) или Методу 4 (рекурсивные запросы).

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

← На главную