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