Приветствуем вас, уважаемые разработчики! Вывод данных из запроса в макет табличного документа — это фундаментальная задача в среде «1С:Предприятие», с которой регулярно сталкивается каждый программист. Начинающие разработчики часто совершают типовые ошибки, пытаясь вручную написать алгоритм заполнения параметров и вывода областей. В результате печатная форма либо отображается пустой, либо выводит только одну (первую или последнюю) строку, либо вообще вызывает критические ошибки выполнения кода.
В рамках этой статьи мы подробно проанализируем ошибки из исходного кода автора темы, разберем базовые механизмы работы с табличными документами и макетами, а также предоставим готовые и оптимизированные шаблоны решений как для обычного, так и для управляемого интерфейса 1С.
Давайте внимательно изучим фрагмент кода, предоставленный автором темы, и выясним причины, по которым данный алгоритм не выполняет поставленную задачу:
Процедура КнопкаВыполнитьНажатие(Кнопка)
Запрос=Новый Запрос;
Запрос.Текст= "ВЫБРАТЬ ...";
// ... установка параметров ...
Результат=Запрос.Выполнить().Выбрать();
Результат.Следующий();
Макет=ПолучитьМакет("Макет");
ОбластьМакета=Макет.ПолучитьОбласть("Заголовок");
ОбластьМакета=Макет.ПолучитьОбласть("Шапка");
ОбластьМакета=Макет.ПолучитьОбласть("Данные");
ОбластьМакета.Параметры.Заказ=Результат.Заказ;
ОбластьМакета.Параметры.Маршрутник=Результат.Маршрутник;
КонецПроцедуры
Проанализируем эту ситуацию по шагам и выделим четыре ключевые ошибки:
ОбластьМакета инициализируется три раза подряд. Сначала в нее помещается область «Заголовок», затем она заменяется областью «Шапка», а в конце — областью «Данные». Предыдущие области просто уничтожаются в оперативной памяти компьютера. Для каждой уникальной секции макета необходимо использовать собственную отдельную переменную.
Макет — это всего лишь шаблон, чертеж будущей формы. Чтобы пользователь увидел результат на экране, нам необходим «чистый лист бумаги» — объект класса ТабличныйДокумент. Именно в него методом Вывести() последовательно переносятся подготовленные области макета. В коде автора табличный документ даже не создается.
Результат.Следующий() вызывается ровно один раз. Метод Следующий() позиционирует указатель выборки на первую строку данных и возвращает Истина, если данные есть. Если запрос возвращает множество строк, то без использования цикла Пока ... Цикл все последующие строки (начиная со второй) будут проигнорированы.
СГРУППИРОВАТЬ ПО без использования агрегатных функций (таких как СУММА, КОЛИЧЕСТВО, СРЕДНЕЕ) является избыточным и неоптимальным. Если целью является исключение дублирования строк в выборке, гораздо правильнее использовать ключевое слово РАЗЛИЧНЫЕ в самом начале инструкции ВЫБРАТЬ.
Теперь, когда мы выяснили все причины некорректного поведения кода, перейдем к реализации правильных и надежных алгоритмов.
Рассмотрим подробнее исправленный вариант процедуры для обычного приложения. Здесь мы создаем табличный документ, последовательно выводим в него статические области заголовка и шапки таблицы, а затем в цикле заполняем и выводим динамические строки данных.
Процедура КнопкаВыполнитьНажатие(Кнопка)
// 1. Создаем результирующий табличный документ, который отобразится пользователю
ТабДок = Новый ТабличныйДокумент;
// 2. Получаем макет обработки/документа
Макет = ПолучитьМакет("Макет");
// 3. Получаем статичные области макета и сразу выводим их в табличный документ
ОбластьЗаголовок = Макет.ПолучитьОбласть("Заголовок");
ТабДок.Вывести(ОбластьЗаголовок);
ОбластьШапка = Макет.ПолучитьОбласть("Шапка");
ТабДок.Вывести(ОбластьШапка);
// 4. Подготавливаем область для вывода строк таблицы
ОбластьДанные = Макет.ПолучитьОбласть("Данные");
// 5. Инициализируем и настраиваем запрос
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| МаршрутныйЛистЗаказы.Ссылка КАК Маршрутник,
| МаршрутныйЛистЗаказы.Заказ.Ссылка КАК Заказ
|ИЗ
| Документ.МаршрутныйЛист.Заказы КАК МаршрутныйЛистЗаказы
|ГДЕ
| МаршрутныйЛистЗаказы.Заказ.Дата МЕЖДУ &НачалоПериода И &КонецПериода";
// Устанавливаем границы периода с приведением к началу и концу дня
Запрос.УстановитьПараметр("НачалоПериода", НачалоДня(НачалоПериода));
Запрос.УстановитьПараметр("КонецПериода", КонецДня(КонецПериода));
РезультатЗапроса = Запрос.Выполнить();
// Проверяем, есть ли данные в результате запроса
Если Не РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
// 6. Запускаем цикл обхода всех полученных записей
Пока Выборка.Следующий() Цикл
// Заполняем параметры области макета данными из текущей строки выборки
ОбластьДанные.Параметры.Заказ = Выборка.Заказ;
ОбластьДанные.Параметры.Маршрутник = Выборка.Маршрутник;
// Выводим подготовленную область в итоговый документ
ТабДок.Вывести(ОбластьДанные);
КонецЦикла;
КонецЕсли;
// 7. Показываем сформированный табличный документ пользователю на экране
ТабДок.Показать("Маршрутные листы по заказам");
КонецПроцедуры
В управляемом приложении действует строгое разграничение выполнения кода между клиентом и сервером. На клиенте мы можем только отображать окна и взаимодействовать с пользователем, в то время как формирование запросов, чтение базы данных и сборка макетов должны осуществляться строго на стороне сервера. Проанализируем, как правильно разделить логику вывода печатной формы на управляемой форме.
Код в модуле управляемой формы будет выглядеть следующим образом:
&НаКлиенте
Процедура ВыполнитьНажатие(Команда)
// Вызываем серверную функцию для формирования печатного документа
ТабличныйДокументОтчета = СформироватьПечатнуюФормуНаСервере();
// Выводим результат пользователю в интерактивном режиме
ТабличныйДокументОтчета.Показать("Реестр маршрутных листов");
КонецПроцедуры
&НаСервере
Функция СформироватьПечатнуюФормуНаСервере()
ТабДок = Новый ТабличныйДокумент;
// Для получения макета внешней обработки на сервере используем РеквизитФормыВЗначение
ОбработкаОбъект = РеквизитФормыВЗначение("Объект");
Макет = ОбработкаОбъект.ПолучитьМакет("Макет");
// Получаем и сразу выводим верхнюю часть отчета
ТабДок.Вывести(Макет.ПолучитьОбласть("Заголовок"));
ТабДок.Вывести(Макет.ПолучитьОбласть("Шапка"));
ОбластьДанные = Макет.ПолучитьОбласть("Данные");
// Формируем запрос к базе данных
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| МаршрутныйЛистЗаказы.Ссылка КАК Маршрутник,
| МаршрутныйЛистЗаказы.Заказ.Ссылка КАК Заказ
|ИЗ
| Документ.МаршрутныйЛист.Заказы КАК МаршрутныйЛистЗаказы
|ГДЕ
| МаршрутныйЛистЗаказы.Заказ.Дата МЕЖДУ &НачалоПериода И &КонецПериода";
// Обращение к реквизитам обработки на форме происходит через "Объект.<ИмяРеквизита>"
Запрос.УстановитьПараметр("НачалоПериода", НачалоДня(Объект.НачалоПериода));
Запрос.УстановитьПараметр("КонецПериода", КонецДня(Объект.КонецПериода));
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
// Полезный лайфхак: автоматическое заполнение параметров при совпадении имен
ЗаполнитьЗначенияСвойств(ОбластьДанные.Параметры, Выборка);
ТабДок.Вывести(ОбластьДанные);
КонецЦикла;
// Настраиваем свойства отображения табличного документа для удобства пользователя
ТабДок.ТолькоПросмотр = Истина; // Защищаем ячейки от случайного редактирования
ТабДок.ОтображатьСетку = Ложь; // Отключаем стандартную сетку для улучшения визуального стиля
ТабДок.ОтображатьЗаголовки = Ложь; // Скрываем служебные координаты строк (1, 2) и колонок (A, B)
Возврат ТабДок;
КонецФункции
Чтобы ваша работа с макетами табличных документов была максимально эффективной, рекомендуем взять на вооружение несколько профессиональных приемов:
ЗаполнитьЗначенияСвойств(). Если параметры в вашей области макета (например, Заказ и Маршрутник) названы точно так же, как и поля, возвращаемые в результате запроса (алиасы после слова КАК), вам не нужно вручную прописывать каждую строку присвоения параметров. Достаточно вызвать метод ЗаполнитьЗначенияСвойств(ОбластьДанные.Параметры, Выборка). Платформа самостоятельно сопоставит имена свойств и заполнит их значениями из базы данных, что значительно сокращает объем программного кода и снижает вероятность опечаток.
Заказ). При выводе области этот параметр автоматически заполнится ссылкой на документ, и стандартный механизм расшифровки 1С откроет форму объекта при двойном клике.
Разработанные шаблоны и подходы помогут вам быстро и безболезненно реализовывать любые печатные формы и отчеты, избегая зависаний и логических нестыковок в работе алгоритмов платформы 1С.