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