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