Как правильно заполнить несколько таблиц в макете внешней печатной формы 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке внешних печатных форм (ВПФ) начинающие программисты часто сталкиваются с ситуацией, когда в одном документе нужно вывести несколько независимых таблиц. Типичная ошибка заключается в попытке вывести строки всех таблиц внутри одного цикла обхода выборки запроса. В результате данные либо перемешиваются, либо (как в описанном случае) корректно заполняется только последняя таблица, так как параметры предыдущих областей перезаписываются или выводятся в неверном порядке.

В этой статье мы подробно разберем, почему возникает такая проблема, и рассмотрим правильные алгоритмы компоновки табличного документа при наличии нескольких логических блоков данных.

Разбор типичной ошибки: почему данные заполняются некорректно

Проанализируем ситуацию: у автора есть один запрос, который собирает данные для «Номерных деталей» и «Мелких деталей». В коде используется один цикл Пока Выборка.Следующий() Цикл, внутри которого поочередно заполняются параметры разных областей и вызывается метод ТабДок.Вывести().

Проблема: Метод Вывести() добавляет область в конец табличного документа. Если вы вызываете вывод строки Таблицы №1, а затем сразу строки Таблицы №2 внутри одного цикла, ваш документ будет выглядеть так:

  1. Строка 1 из Таблицы 1
  2. Строка 1 из Таблицы 2
  3. Строка 2 из Таблицы 1
  4. Строка 2 из Таблицы 2

Это нарушает структуру печатной формы, где сначала должна полностью идти первая таблица (с шапкой и всеми строками), а затем — вторая. Рассмотрим, как этого избежать.

Решение №1: Пакетные запросы — профессиональный подход

Наиболее эффективным способом разделения данных является использование пакетных запросов. Вместо того чтобы пытаться получить все данные в одной плоской выборке, мы разделяем их на уровне SQL-запроса к базе данных.

Выясним, как это работает: в тексте одного объекта Запрос мы пишем несколько запросов, разделяя их точкой с запятой. Результат мы получаем не через Выполнить().Выбрать(), а через ВыполнитьПакет(). Это возвращает массив результатов, каждый из которых мы можем обойти в отдельном цикле.

Рассмотрим пример кода:


// Подготавливаем запрос с пакетом
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ Номенклатура, НомерДетали ИЗ СнятиеНомерныхДеталей ГДЕ Ссылка = &Ссылка;
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ КатегорияЛома ИЗ СнятиеМелкихДеталей ГДЕ Ссылка = &Ссылка";

Запрос.УстановитьПараметр("Ссылка", СсылкаНаОбъект);

// Получаем массив результатов
МассивРезультатов = Запрос.ВыполнитьПакет();

// МассивРезультатов[0] - данные для первой таблицы
// МассивРезультатов[1] - данные для второй таблицы

Теперь мы можем последовательно выводить таблицы одну за другой, используя разные выборки.

Решение №2: Алгоритм «Шапка — Строки — Подвал» для каждой таблицы

Чтобы печатная форма выглядела корректно, необходимо соблюдать строгую последовательность вывода областей. Разберем по шагам, как должен выглядеть процесс вывода в коде функции Печать():

  1. Вывод первой таблицы:
    • Получаем область шапки первой таблицы: ОбластьШапка1 = Макет.ПолучитьОбласть("Шапка1");.
    • Выводим её: ТабДок.Вывести(ОбластьШапка1);.
    • Запускаем цикл по ПЕРВОЙ выборке (например, Выборка1 = МассивРезультатов[0].Выбрать();).
    • Внутри цикла заполняем параметры области строки и выводим её: ТабДок.Вывести(ОбластьСтрока1);.
  2. Вывод второй таблицы:
    • Только после завершения первого цикла выводим шапку второй таблицы.
    • Запускаем второй цикл по ВТОРОЙ выборке.
    • Выводим строки второй таблицы.

Посмотрим на реализацию этого алгоритма:


// Вывод Таблицы №1 (Номерные детали)
ТабДок.Вывести(ОбластьШапкаНомерные);
ВыборкаНомерные = МассивРезультатов[0].Выбрать();
Пока ВыборкаНомерные.Следующий() Цикл
    ОбластьСтрокаНомерные.Параметры.Заполнить(ВыборкаНомерные);
    ТабДок.Вывести(ОбластьСтрокаНомерные);
КонецЦикла;

// Вывод Таблицы №2 (Мелкие детали)
ТабДок.Вывести(ОбластьШапкаМелкие);
ВыборкаМелкие = МассивРезультатов[1].Выбрать();
Пока ВыборкаМелкие.Следующий() Цикл
    ОбластьСтрокаМелкие.Параметры.Заполнить(ВыборкаМелкие);
    ТабДок.Вывести(ОбластьСтрокаМелкие);
КонецЦикла;

Решение №3: Фильтрация данных из общей таблицы значений

Если по каким-то причинам вы не можете использовать пакетные запросы и данные приходят одним общим списком, проанализируем ситуацию с использованием ТаблицыЗначений. Мы можем выгрузить весь результат запроса в таблицу, а затем фильтровать её для каждой области вывода.

Рассмотрим последовательность действий:

  1. Выгружаем результат запроса: ТЗ_Данные = Запрос.Выполнить().Выгрузить();.
  2. Используем метод НайтиСтроки() или создаем копии таблицы с фильтром.
  3. Выводим первую часть данных, затем вторую.

Пример фильтрации:


// Отбираем только строки, где заполнена номенклатура (для первой таблицы)
ПараметрыОтбора = Новый Структура("ТипДетали", Перечисления.ТипыДеталей.Номерная);
СтрокиНомерные = ТЗ_Данные.НайтиСтроки(ПараметрыОтбора);

Для Каждого СтрокаДанных Из СтрокиНомерные Цикл
    ЗаполнитьЗначенияСвойств(ОбластьСтрока.Параметры, СтрокаДанных);
    ТабДок.Вывести(ОбластьСтрока);
КонецЦикла;

Важные технические нюансы и оптимизация

При работе с внешними печатными формами стоит обратить внимание на следующие полезные приемы:

1. Использование метода ЗаполнитьЗначенияСвойств().
Вместо того чтобы вручную присваивать каждый параметр (Область.Параметры.Имя = Выборка.Имя), используйте ЗаполнитьЗначенияСвойств(Область.Параметры, Выборка). Это значительно ускоряет написание кода и уменьшает вероятность ошибок, если имена полей в запросе совпадают с именами параметров в макете.

2. Проверка на наличие данных.
Хорошей практикой считается проверка: а нужно ли вообще выводить таблицу, если в ней нет данных? Выясним причину появления пустых заголовков — это отсутствие проверки выборки. Используйте условие Если НЕ Выборка.Пустой() Тогда, чтобы не загромождать печатную форму пустыми шапками таблиц.

3. Метод Присоединить() vs Вывести().
Помните, что метод Вывести() всегда переносит печать на новую строку (вниз). Если ваша задача — расположить две таблицы рядом (слева и справа), то для вывода правой таблицы нужно использовать метод ТабДок.Присоединить(Область). В контексте обсуждаемой темы (таблицы друг под другом) мы используем только Вывести().

4. Оптимизация работы с макетом.
Получайте области макета (Макет.ПолучитьОбласть()) один раз до начала всех циклов. Обращение к макету — ресурсоемкая операция, и выполнение её внутри цикла Пока Выборка.Следующий() может существенно замедлить формирование формы при большом количестве строк.

Подводя итог, можно сказать: залог корректной работы сложной печатной формы — это разделение логики получения данных и логики их визуального отображения. Пакетные запросы и последовательный вывод областей — это стандарт качества для разработчика 1С.

← На главную