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