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