← Назад к вопросам

Механизм состава изделия с вложенностью

3.0 Senior🔥 121 комментариев
#Запросы и оптимизация#Конфигурации и типовые

Условие

Реализуйте механизм состава изделия (BOM — Bill of Materials) с минимум 2 уровнями вложенности.

Требования:

  • Справочник "Номенклатура" с табличной частью "Состав" (компоненты и их количество)
  • Рекурсивный расчёт потребности в материалах
  • Отчёт по потребности материалов для изготовления изделия

Пример

Изделие "Стол":

  • Столешница (1 шт.)
    • Доска (3 шт.)
    • Лак (0.5 л)
  • Ножка (4 шт.)
    • Брус (1 шт.)

Для изготовления 2 столов потребуется:

  • Доска: 6 шт.
  • Лак: 1 л
  • Брус: 8 шт.

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Решение

Структура справочника Номенклатура

Реквизиты

- Наименование (Строка)
- ТипНоменклатуры (Перечисление): Изделие, Компонент, Материал
- ЕдиницаИзмерения (Справочник.ЕдиницыИзмерения)
- Цена (Число, 12,2)

Табличная часть "Состав"

- Компонент (Справочник.Номенклатура) - ссылка на компонент
- Количество (Число, 10,3) - количество компонента
- ЕдиницаИзмерения (Справочник.ЕдиницыИзмерения)
- Примечание (Строка, неполное)

Функция расчёта потребности материалов (рекурсивная)

Функция ПолучитьПотребностьМатериалов(Номенклатура, Количество = 1) Экспорт
    
    ТаблицаПотребности = Новый ТаблицаЗначений();
    ТаблицаПотребности.Колонки.Добавить("Компонент", , "Справочник.Номенклатура");
    ТаблицаПотребности.Колонки.Добавить("НаименованиеКомпонента", , "Строка");
    ТаблицаПотребности.Колонки.Добавить("Количество", , "Число");
    ТаблицаПотребности.Колонки.Добавить("ЕдиницаИзмерения", , "Справочник.ЕдиницыИзмерения");
    ТаблицаПотребности.Колонки.Добавить("Уровень", , "Число");
    
    РазвернутьСтруктуруИзделия(Номенклатура, Количество, ТаблицаПотребности, 0);
    
    Возврат ТаблицаПотребности;
    
КонецФункции

Процедура РазвернутьСтруктуруИзделия(Номенклатура, Количество, ТаблицаПотребности, Уровень)
    
    НоменклатураОбъект = Номенклатура.ПолучитьОбъект();
    
    // Если нет состава — это материал
    Если НоменклатураОбъект.Состав.Количество() = 0 Тогда
        ДобавитьВТаблицуПотребности(Номенклатура, НоменклатураОбъект.Наименование, Количество, НоменклатураОбъект.ЕдиницаИзмерения, Уровень, ТаблицаПотребности);
        Возврат;
    КонецЕсли;
    
    // Проходим по компонентам
    Для каждого СтрокаСостава Из НоменклатураОбъект.Состав Цикл
        
        КоличествоКомпонента = СтрокаСостава.Количество * Количество;
        РазвернутьСтруктуруИзделия(СтрокаСостава.Компонент, КоличествоКомпонента, ТаблицаПотребности, Уровень + 1);
        
    КонецЦикла;
    
КонецПроцедуры

Процедура ДобавитьВТаблицуПотребности(Компонент, НаименованиеКомпонента, Количество, ЕдиницаИзмерения, Уровень, ТаблицаПотребности)
    
    // Проверяем, есть ли уже такой компонент
    ФильтрПоиска = Новый Структура("Компонент", Компонент);
    НайденныеСтроки = ТаблицаПотребности.НайтиСтроки(ФильтрПоиска);
    
    Если НайденныеСтроки.Количество() > 0 Тогда
        // Суммируем количество
        НайденныеСтроки[0].Количество = НайденныеСтроки[0].Количество + Количество;
    Иначе
        // Добавляем новую строку
        НоваяСтрока = ТаблицаПотребности.Добавить();
        НоваяСтрока.Компонент = Компонент;
        НоваяСтрока.НаименованиеКомпонента = НаименованиеКомпонента;
        НоваяСтрока.Количество = Количество;
        НоваяСтрока.ЕдиницаИзмерения = ЕдиницаИзмерения;
        НоваяСтрока.Уровень = Уровень;
    КонецЕсли;
    
КонецПроцедуры

Запрос для отчёта потребности материалов

Функция ПолучитьОтчётПотребностиМатериалов(Изделие, КоличествоИзделий) Экспорт
    
    ТаблицаПотребности = ПолучитьПотребностьМатериалов(Изделие, КоличествоИзделий);
    
    // Фильтруем только материалы (без промежуточных изделий)
    ОтфильтрованнаяТаблица = ТаблицаПотребности.Скопировать();
    
    Возврат ОтфильтрованнаяТаблица;
    
КонецФункции

Процедура вывода отчёта

Процедура ВывестиОтчётПотребности(Изделие, КоличествоИзделий)
    
    ТаблицаПотребности = ПолучитьПотребностьМатериалов(Изделие, КоличествоИзделий);
    
    Сообщить("Потребность для изготовления " + КоличествоИзделий + " изделия(й): " + Изделие.Наименование);
    Сообщить("");
    
    Для каждого Строка Из ТаблицаПотребности Цикл
        ОтступОтступ = Формат(Строка.Уровень * 2, "00") + " ";
        Сообщить(ОтступОтступ + "- " + Строка.НаименованиеКомпонента + ": " + Строка.Количество + " " + ?(Строка.ЕдиницаИзмерения <> Неопределено, Строка.ЕдиницаИзмерения.Аббревиатура, ""));
    КонецЦикла;
    
КонецПроцедуры

Альтернатива: с использованием запроса (БОМ дерево)

Функция ПостроитьБОМДерево(Номенклатура, Количество) Экспорт
    
    ДеревоБОМ = Новый ДеревоЗначений();
    ДеревоБОМ.Колонки.Добавить("Компонент", , "Справочник.Номенклатура");
    ДеревоБОМ.Колонки.Добавить("Наименование", , "Строка");
    ДеревоБОМ.Колонки.Добавить("Количество", , "Число");
    ДеревоБОМ.Колонки.Добавить("ЕдиницаИзмерения", , "Строка");
    
    КорневаяСтрока = ДеревоБОМ.Строки.Добавить();
    КорневаяСтрока.Компонент = Номенклатура;
    КорневаяСтрока.Наименование = Номенклатура.Наименование;
    КорневаяСтрока.Количество = Количество;
    КорневаяСтрока.ЕдиницаИзмерения = Номенклатура.ЕдиницаИзмерения.Аббревиатура;
    
    РекурсивноПостроитьДерево(Номенклатура, Количество, КорневаяСтрока);
    
    Возврат ДеревоБОМ;
    
КонецФункции

Процедура РекурсивноПостроитьДерево(Номенклатура, Количество, РодительскаяСтрока)
    
    НоменклатураОбъект = Номенклатура.ПолучитьОбъект();
    
    Для каждого СтрокаСостава Из НоменклатураОбъект.Состав Цикл
        
        НоваяСтрока = РодительскаяСтрока.Строки.Добавить();
        НоваяСтрока.Компонент = СтрокаСостава.Компонент;
        НоваяСтрока.Наименование = СтрокаСостава.Компонент.Наименование;
        НоваяСтрока.Количество = СтрокаСостава.Количество * Количество;
        НоваяСтрока.ЕдиницаИзмерения = СтрокаСостава.ЕдиницаИзмерения.Аббревиатура;
        
        РекурсивноПостроитьДерево(СтрокаСостава.Компонент, СтрокаСостава.Количество * Количество, НоваяСтрока);
        
    КонецЦикла;
    
КонецПроцедуры

Пример использования

// Для примера: Стол -> Столешница -> Доска, Лак; Ножка -> Брус

Запросил = Новый Запрос();
Стол = ПолучитьНоменклатуруПоНаименованию("Стол");

// Метод 1: простой расчёт
ТаблицаПотребности = ПолучитьПотребностьМатериалов(Стол, 2);
ДляКаждого Строка Из ТаблицаПотребности Цикл
    Сообщить(Строка.НаименованиеКомпонента + ": " + Строка.Количество);
КонецЦикла;

// Метод 2: дерево
ДеревоБОМ = ПостроитьБОМДерево(Стол, 2);

// Для вывода в UI
ВывестиОтчётПотребности(Стол, 2);

Ключевые моменты

  1. Рекурсия — обход всех уровней вложенности
  2. Суммирование компонентов — одинаковые компоненты считаются вместе
  3. Расширяемость — легко добавить уровни
  4. Дерево значений — отображение иерархии в UI
  5. Перемножение количеств — каждый уровень на каждый компонент
Механизм состава изделия с вложенностью | PrepBro