Как правильно заполнить макет табличного документа из выборки запроса

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

Заполнение печатных форм в 1С — одна из самых частых задач разработчика. Однако при работе с выборками из запросов легко совершить логические ошибки, которые приводят либо к дублированию данных, либо к выводу пустых или некорректных строк. Разберем типичные ошибки и правильные подходы на примере ситуации, когда необходимо вывести шапку документа и его табличную часть.

Анализ типичных ошибок в коде

Рассмотрим исходный код, который часто вызывает проблемы у начинающих программистов. Проанализируем основные логические просчеты:


Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл      
    КоличествоЕдениц = Объект.МассивОбъектов.Товары.Выгрузить();
    Итого = КоличествоЕдениц.Итог("Количество");
    ТабДок.Очистить(); // Ошибка №1
    ОбластьШапки.Параметры.НомерДоговора = Выборка.НомерДоговора;
    // ... заполнение других параметров
КонецЦикла; // Ошибка №2
ТабДок.Вывести(ОбластьШапки);

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

Ошибка №1: Очистка внутри цикла. Метод ТабДок.Очистить() удаляет всё содержимое документа. Если вы вызываете его внутри цикла, то на каждой итерации документ будет обнуляться, и в итоге в нем останутся данные только последней строки (или вообще ничего, если логика вывода нарушена).

Ошибка №2: Некорректное заполнение параметров шапки. Если в выборке несколько строк, цикл по шапке будет перезаписывать параметры области ОбластьШапки.Параметры данными каждой следующей строки. В результате в макет попадут данные только из последней записи выборки. Обычно шапка заполняется один раз.

Ошибка №3: Двойное выполнение запроса. Вызов Запрос.Выполнить() дважды — это лишняя нагрузка на сервер базы данных. Если данные одни и те же, нужно выполнить запрос один раз и работать с результатом. Кроме того, если запрос сложный и соединяет разные таблицы, без правильной группировки вы получите "размножение" строк (декартово произведение), что и приводит к "неадекватному составу данных".

Решение 1: Исправление базовой логики (Классический обход)

Для начала приведем код к рабочему виду. Выясним причину странного поведения: скорее всего, в запросе данные для шапки дублируются для каждой строки табличной части. Рассмотрим, как это исправить программно, разделив вывод шапки и строк.


РезультатЗапроса = Запрос.Выполнить();

// Проверяем, есть ли данные
Если Не РезультатЗапроса.Пустой() Тогда
    
    Выборка = РезультатЗапроса.Выбрать();
    
    // Выводим шапку только один раз, используя данные первой строки
    Если Выборка.Следующий() Тогда
        
        // Заполняем параметры шапки
        ОбластьШапки.Параметры.НомерДоговора = Выборка.НомерДоговора;
        ОбластьШапки.Параметры.ДатаДоговора  = Выборка.ДатаДоговора;
        // ... другие поля шапки
        
        ТабДок.Вывести(ОбластьШапки);
        
        // Сбрасываем выборку в начало или продолжаем обход для строк
        // В данном случае, так как мы уже сделали Следующий(), 
        // нам нужно вывести данные текущей (первой) строки, а затем остальных
        
        Цикл
            ОбластьДанные.Параметры.Наименование = Выборка.Номенклатура;
            ОбластьДанные.Параметры.Артикул      = Выборка.Артикул;
            ОбластьДанные.Параметры.Количество   = Выборка.Количество;
            
            ТабДок.Вывести(ОбластьДанные);
            
            Если Не Выборка.Следующий() Тогда
                Прервать;
            КонецЕсли;
        КонецЦикла;
        
    КонецЕсли;
    
КонецЕсли;

Решение 2: Использование метода Заполнить()

Чтобы избежать длинных списков присваиваний вида Параметры.Поле = Выборка.Поле, которые часто приводят к опечаткам (как в примере "КоличетсвоЕдениц"), проанализируем более эффективный метод — Заполнить(). Этот метод автоматически сопоставляет имена параметров в макете с именами полей в выборке или структуре.

Рассмотрим, как упростить код:


Выборка = Запрос.Выполнить().Выбрать();

// Очищаем документ ОДИН РАЗ перед началом вывода
ТабДок.Очистить();

Если Выборка.Следующий() Тогда
    // Автоматическое заполнение всех параметров шапки
    ОбластьШапки.Параметры.Заполнить(Выборка);
    ТабДок.Вывести(ОбластьШапки);
    
    // Сбрасываем выборку на начало для обхода строк (или используем иерархию)
    Выборка.Сбросить(); 
    
    Пока Выборка.Следующий() Цикл
        // Автоматическое заполнение параметров строк
        ОбластьДанные.Параметры.Заполнить(Выборка);
        ТабДок.Вывести(ОбластьДанные);
    КонецЦикла;
КонецЕсли;

Важно: Для работы этого метода имена ячеек-параметров в вашем макете (например, в свойствах ячейки указано <Номенклатура>) должны в точности совпадать с именами колонок в результате запроса (ВЫБРАТЬ Номенклатура КАК Номенклатура...).

Решение 3: Профессиональный подход через ИТОГИ

Самый правильный способ вывода данных "Шапка + Табличная часть" — это использование иерархической выборки. Рассмотрим подробнее, как это реализовать. В тексте запроса необходимо добавить предложение ИТОГИ по тем полям, которые относятся к шапке.

Разберем по шагам создание такого отчета:

  1. Подготовка запроса: Группируем данные так, чтобы информация шапки (номер, дата, контрагент) была в итогах, а информация строк — в детальных записях.
  2. Обход по группировкам: Используем специальный режим метода Выбрать().

// В запросе: ... ИТОГИ ПО Ссылка (или НомерДоговора)
Результат = Запрос.Выполнить();

// Получаем выборку по группировке (верхний уровень - Шапка)
ВыборкаШапка = Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);

Пока ВыборкаШапка.Следующий() Цикл
    
    // Заполняем область шапки данными из группировки
    ОбластьШапки.Параметры.Заполнить(ВыборкаШапка);
    ТабДок.Вывести(ОбластьШапки);
    
    // Получаем выборку детальных записей (нижний уровень - Строки)
    ВыборкаСтроки = ВыборкаШапка.Выбрать();
    
    Пока ВыборкаСтроки.Следующий() Цикл
        ОбластьДанные.Параметры.Заполнить(ВыборкаСтроки);
        ТабДок.Вывести(ОбластьДанные);
    КонецЦикла;
    
    // Если документов несколько, можно вставить разделитель
    ТабДок.ВывестиГоризонтальныйРазделительСтраниц();
    
КонецЦикла;

Этот метод полностью исключает "неадекватный состав данных", так как система четко разделяет, какие данные относятся к целому документу, а какие — к его строкам.

Рекомендации по отладке и оптимизации

Если данные все равно выводятся некорректно, проанализируем следующие моменты:

1. Проблема дублей (Cartesian Product). Если вы соединяете в запросе таблицу товаров с таблицей дополнительных реквизитов или скидок, и у одного товара несколько скидок, то товар продублируется столько раз, сколько у него скидок. Проверьте результат запроса в Консоли запросов. Если видите лишние строки — используйте СГРУППИРОВАТЬ ПО или пакетные запросы.

2. Расшифровка. При заполнении строк не забывайте указывать параметр расшифровки. Это позволит пользователю кликнуть на строку в печатной форме и открыть документ или карточку номенклатуры:

ОбластьДанные.Параметры.НаименованиеРасшифровка = ВыборкаСтроки.Номенклатура;

3. Производительность. Для формирования очень длинных документов (сотни страниц) рекомендуется использовать транзакции и отключать авторасчет высоты строк до завершения формирования документа с помощью ТабДок.Автомасштаб = Ложь;.

Подведем итог: залог успеха в заполнении макета — это один чисто выполненный запрос, правильное использование уровней группировки (ИТОГИ) и использование встроенных методов платформы 1С, таких как Заполнить(), которые минимизируют человеческий фактор и ошибки в коде.

← На главную