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