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