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