Одной из частых задач при автоматизации производственных процессов в 1С:ERP является получение полного состава изделия — так называемое "разузлование" спецификации. Это необходимо, когда нам нужно узнать потребность в материалах не только для верхнего уровня готовой продукции, но и для всех входящих в нее полуфабрикатов, вплоть до исходного сырья — для этой задачи есть обработка разузлования полуфабрикатов и анализа себестоимости для 1С:ERP. Если вам требуется готовое решение, можно посмотреть, как реализована обработка по разузлованию спецификаций для 1С:ERP 2.5, или написать свой механизм, следуя инструкциям ниже.
В этой статье мы подробно разберем, как программно получить дерево состава изделия, используя типовые механизмы конфигурации ERP 2.5. Мы рассмотрим решение с использованием методов построения дерева и оптимизированный вариант с рекурсивным обходом для получения плоской таблицы материалов.
Задача заключается в том, чтобы для заданной РесурснойСпецификации и Количества продукции получить список всех необходимых материалов и полуфабрикатов с учетом вложенности. Часто это требуется, чтобы оценить потребность в материалах в заказах на производство согласно ресурсным спецификациям — с этим поможет обработка автоматического пересчета потребности в материалах для 1С:ERP. При этом желательно использовать типовой функционал, чтобы алгоритм корректно отрабатывал логику, заложенную разработчиками 1С (например, параметры выбора спецификаций).
В конфигурации ERP 2.5 существует общий модуль УправлениеДаннымиОбИзделиях, который отвечает за визуализацию структуры изделия в формах. Мы можем воспользоваться процедурой ПостроитьДеревоСпецификаций. Этот метод удобен тем, что он формирует структуру, полностью аналогичную той, что пользователь видит в интерфейсе "Дерево ресурсных спецификаций". Похожий принцип использует и популярный отчет (на СКД), который разузловывает полную спецификацию по изделию.
Давайте проанализируем код, который позволяет получить данные с помощью этого механизма.
Сначала нам необходимо подготовить параметры для построения дерева. Обратите внимание, что мы заполняем структуру параметров, отключая лишние визуальные элементы (этапы, операции), чтобы ускорить процесс, если нам нужны только материалы.
&НаСервере
Процедура РазузловатьНаСервере(Спецификация, Количество)
// Инициализируем параметры дерева стандартным методом
ПараметрыДерева = УправлениеДаннымиОбИзделияхКлиентСервер.ПараметрыДереваСпецификаций();
// Устанавливаем основные параметры отбора
ПараметрыДерева.Номенклатура = Справочники.Номенклатура.ПустаяСсылка;
ПараметрыДерева.ХарактеристикаНоменклатуры = Справочники.ХарактеристикиНоменклатуры.ПустаяСсылка;
ПараметрыДерева.Спецификация = Спецификация;
// Важный момент: параметры выбора спецификаций.
// Это влияет на то, какая спецификация подберется для полуфабрикатов (основная, активная и т.д.)
ПараметрыДерева.ПараметрыВыбораСпецификаций = УправлениеДаннымиОбИзделиях.ПараметрыВыбораСпецификаций(
Новый Структура,
Обработки.ДеревоРесурсныхСпецификаций
);
ПараметрыДерева.Дата = ТекущаяДата();
ПараметрыДерева.ДинамическоеСчитывание = Истина;
// Настраиваем видимость элементов дерева.
// Нам нужны материалы, но не нужны трудозатраты и этапы для этой задачи.
МнимаяФорма = Новый Структура(
"ДеревоВыводитьЭтапы, ДеревоВыводитьОперации, ДеревоВыводитьВыходныеИзделия, ДеревоВыводитьМатериалы, ДеревоВыводитьТрудозатраты, ДеревоВыводитьЗаголовкиГруппировок",
Ложь, // Этапы
Ложь, // Операции
Ложь, // Выходные изделия
Истина, // Материалы - это то, что нам нужно
Ложь, // Трудозатраты
Ложь // Заголовки
);
УправлениеДаннымиОбИзделиях.ЗаполнитьНастройкиОтбораДереваСпецификации(ПараметрыДерева, МнимаяФорма);
// Определяем параметры количества
ОсновноеИзделие = Справочники.РесурсныеСпецификаций.ДанныеОсновногоИзделияСпецификации(Спецификация);
ПараметрыДерева.Номенклатура = ОсновноеИзделие.Номенклатура;
ПараметрыДерева.КоличествоВыходногоИзделия = Количество * ОсновноеИзделие.Количество;
// Вызов построения дерева
// Обратите внимание: "ЭтотОбъект" подразумевает, что код выполняется в контексте формы или обработки,
// имеющей реквизит ДеревоСпецификаций
УправлениеДаннымиОбИзделиях.ПостроитьДеревоСпецификаций(ЭтотОбъект, ПараметрыДерева);
// Поскольку дерево строится динамически, его нужно развернуть программно
РазвернутьДерево(ДеревоСпецификаций);
КонецПроцедуры
Поскольку механизм динамического считывания подгружает данные только при раскрытии узлов, нам потребуется вспомогательная процедура для принудительного разворачивания уровней дерева. Без этого мы получим только верхний уровень.
&НаСервере
Процедура РазвернутьДерево(СтрокаДерево)
Для каждого Ст из СтрокаДерево.ПолучитьЭлементы() Цикл
Идентификатор = Ст.ПолучитьИдентификатор();
// Этот метод подгружает данные вложенных узлов
УправлениеДаннымиОбИзделияхКлиентСервер.ДеревоСпецификацийПередРазворачиванием(ЭтотОбъект, Идентификатор, Ложь);
// Рекурсивно идем глубже
РазвернутьДерево(Ст);
КонецЦикла;
КонецПроцедуры
Недостатки метода: Данный способ довольно ресурсоемкий. Он использует логику формы и предназначен в первую очередь для визуального отображения данных пользователю. Если вам нужно массово разузловывать тысячи спецификаций, этот метод может работать медленно. В таких случаях рекомендуем изучить методику использования сводных спецификаций для ускорения расчета потребностей плана производства.
Для более оптимального решения задачи, особенно если целью является получение плоской таблицы "Материал - Общее количество", лучше использовать подход, основанный на рекурсивном обходе. Однако при написании собственного рекурсивного алгоритма крайне важна предварительная проверка зацикливания спецификации, чтобы избежать бесконечных вызовов при ошибках в данных.
Идея заключается в том, чтобы взять за основу логику формирования печатной формы спецификации, но вместо вывода в табличный документ собирать данные в ТаблицаЗначений. Это позволяет исключить лишние визуальные вычисления.
Разделим задачу на две части: инициализацию и рекурсивный обход.
Создадим функцию, которая принимает данные дерева и преобразует их в таблицу.
&НаСервере
Функция ПолучитьПлоскуюТаблицуМатериалов(ДеревоСпецификацийЗначение)
// Создаем таблицу для результата
ИтоговыеКомпоненты = Новый ТаблицаЗначений;
ИтоговыеКомпоненты.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ИтоговыеКомпоненты.Колонки.Добавить("Характеристика", Новый ОписаниеТипов("СправочникСсылка.ХарактеристикиНоменклатуры"));
ИтоговыеКомпоненты.Колонки.Добавить("ЕдИзм", Новый ОписаниеТипов("СправочникСсылка.УпаковкиЕдиницыИзмерения"));
ИтоговыеКомпоненты.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
// Запускаем рекурсию с множителем 1 (для одной единицы изделия верхнего уровня)
СобратьКомпонентыРекурсивно(ДеревоСпецификацийЗначение.Строки, 1, ИтоговыеКомпоненты);
// Сворачиваем таблицу, чтобы просуммировать одинаковые материалы из разных узлов
ИтоговыеКомпоненты.Свернуть("Номенклатура, Характеристика, ЕдИзм", "Количество");
Возврат ИтоговыеКомпоненты;
КонецФункции
Это сердце алгоритма. Процедура проходит по строкам дерева. Если строка имеет подчиненные элементы, мы вызываем эту же процедуру для подчиненных строк, домножая количество.
&НаСервере
Процедура СобратьКомпонентыРекурсивно(ИсточникСтрок, Множитель, ТаблицаРезультатов)
Для Каждого СтрокаДерева Из ИсточникСтрок Цикл
// Проверяем, есть ли вложенные строки (полуфабрикаты)
Если СтрокаДерева.Строки.Количество() > 0 Тогда
// Если есть вложенность, углубляемся.
СобратьКомпонентыРекурсивно(
СтрокаДерева.Строки,
Множитель * СтрокаДерева.Количество,
ТаблицаРезультатов
);
Иначе
// Это конечный материал
НоваяСтрока = ТаблицаРезультатов.Добавить();
НоваяСтрока.Номенклатура = СтрокаДерева.Номенклатура;
НоваяСтрока.Характеристика = СтрокаДерева.Характеристика;
НоваяСтрока.ЕдИзм = СтрокаДерева.ЕдиницаИзмерения;
НоваяСтрока.Количество = Множитель * СтрокаДерева.Количество;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
При реализации данного механизма необходимо учитывать несколько особенностей архитектуры 1С:ERP:
Если вам требуется максимальная производительность, можно рассмотреть альтернативы:
Рассмотренный выше комбинированный метод является "золотой серединой" между сложностью реализации и надежностью результата.