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