При создании внешних печатных форм в 1С, особенно на начальном этапе, часто возникают одни и те же типовые проблемы. Самая распространенная ситуация: печатная форма открывается, табличная часть с товарами или услугами заполняется, но шапка и подвал документа остаются пустыми. Либо в подвале отображаются некорректные итоговые суммы. Рассмотрим по шагам, как диагностировать и исправить эти ошибки на примере создания печатной формы для документа «Приобретение товаров и услуг» в 1С: Комплексная автоматизация 2.
Проанализируем исходную ситуацию. У нас есть код внешней обработки, который должен формировать печатную форму. Процедура печати вызывается, но в макет не передаются данные о номере, дате документа, контрагенте, складе и т.д.
Выясним причину:
Основная причина кроется в том, что в функцию, которая непосредственно формирует табличный документ, не передается ссылка на сам документ, из которого была вызвана печать. Посмотрим на первоначальный код:
Процедура Печать(МассивОбъектов, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
УправлениеПечатью.ВывестиТабличныйДокументВКоллекцию(КоллекцияПечатныхФорм,
"ТребованиеНакладная",
"Требование-накладная",
СформироватьПечатнуюФорму(МассивОбъектов[0], ОбъектыПечати));
КонецПроцедуры
// ...
Функция СформироватьПечатнуюФорму(Флаг = Истина, Флаг2= Ложь) Экспорт
// ...
ОбластьШапки.Параметры.Организация = СсылкаНаОбъект.Организация;
ОбластьШапки.Параметры.Склад = СсылкаНаОбъект.Склад;
// ...
КонецФункции
Здесь сразу несколько моментов, на которые стоит обратить внимание:
Печать — это стандартная экспортная процедура для механизма внешних печатных форм. Платформа автоматически передает в нее параметр МассивОбъектов. Этот массив содержит ссылки на документы, для которых нужно выполнить печать. Даже если мы печатаем один документ, он все равно будет в виде массива из одного элемента.СформироватьПечатнуюФорму(...) мы видим, что первый элемент массива МассивОбъектов[0] передается, но сама функция СформироватьПечатнуюФорму(Флаг = Истина, Флаг2= Ложь) не готова его принять в именованный параметр.СсылкаНаОбъект, которая нигде не определена. Соответственно, все попытки получить через нее реквизиты документа (СсылкаНаОбъект.Организация, СсылкаНаОбъект.Номер и т.д.) не приводят к результату, и шапка остается пустой.Разберем решение по шагам:
Нужно привести в соответствие вызов функции и ее объявление. Правильно будет передать ссылку на документ и принять ее в параметр с осмысленным именем.
Шаг 1. Изменяем вызов функции СформироватьПечатнуюФорму.
В процедуре Печать передаем только необходимый параметр — ссылку на документ.
Процедура Печать(МассивОбъектов, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
УправлениеПечатью.ВывестиТабличныйДокументВКоллекцию(КоллекцияПечатныхФорм,
"ТребованиеНакладная",
"Требование-накладная",
СформироватьПечатнуюФорму(МассивОбъектов[0])); // Передаем только ссылку на документ
КонецПроцедуры
Шаг 2. Корректируем объявление функции СформироватьПечатнуюФорму.
Теперь функция должна принимать один параметр, который мы и будем использовать для доступа к реквизитам. Назовем его СсылкаНаДокумент для лучшей читаемости кода.
Функция СформироватьПечатнуюФорму(СсылкаНаДокумент) Экспорт
// ... здесь остальной код функции
КонецФункции
Шаг 3. Используем полученный параметр для заполнения шапки и запроса.
Теперь вместо неопределенной переменной СсылкаНаОбъект мы используем параметр СсылкаНаДокумент.
Функция СформироватьПечатнуюФорму(СсылкаНаДокумент) Экспорт
ТабДок = Новый ТабличныйДокумент;
// ...
Макет = ПолучитьМакет("Макет");
ОбластьШапки = Макет.ПолучитьОбласть("Шапка");
// Заполняем параметры шапки
ОбластьШапки.Параметры.ТекстЗаголовка = "Требование-Накладная № " + СсылкаНаДокумент.Номер + " от " + Формат(СсылкаНаДокумент.Дата, "ДФ=dd.MM.yyyy");
ОбластьШапки.Параметры.Организация = СсылкаНаДокумент.Организация;
ОбластьШапки.Параметры.Склад = СсылкаНаДокумент.Склад;
ОбластьШапки.Параметры.Партнер = СсылкаНаДокумент.Партнер;
ОбластьШапки.Параметры.Договор = СсылкаНаДокумент.Договор;
ТабДок.Вывести(ОбластьШапки);
// Используем ссылку в запросе
Запрос = Новый Запрос;
Запрос.Текст = "... |ГДЕ | ПриобретениеТоваровУслугТовары.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", СсылкаНаДокумент);
// ...
Возврат ТабДок;
КонецФункции
Обратите внимание на использование функции Формат() для вывода даты. Это хорошая практика, позволяющая отобразить дату в нужном формате без времени.
После этих изменений шапка печатной формы будет корректно заполняться данными из документа.
Вторая частая ошибка — неправильный подсчет итогов. В нашем примере в подвале выводилась сумма только по последнему товару в документе, а не общая сумма.
Выясним причину:
Проблема кроется в логике работы с переменной, которая накапливает сумму. Посмотрим на ошибочный фрагмент кода:
// ...
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
ИтогоСумма = 0; // ОШИБКА: переменная обнуляется на каждой итерации!
ЗаполнитьЗначенияСвойств(ОбластьДанные.Параметры, Выборка);
ИтогоСумма = ИтогоСумма + Выборка.Сумма;
ТабДок.Вывести(ОбластьДанные);
КонецЦикла;
ОбластьПодвал.Параметры.ИтогоСумма = ИтогоСумма;
ТабДок.Вывести(ОбластьПодвал);
// ...
Строка ИтогоСумма = 0; находится внутри цикла обхода выборки. Это значит, что перед обработкой каждой новой строки документа накопленная сумма сбрасывается в ноль. В итоге после завершения цикла переменная ИтогоСумма будет содержать сумму только последней строки.
Решение 1: Простое исправление
Нужно вынести инициализацию переменной за пределы цикла. Переменная-счетчик должна быть объявлена и обнулена до начала цикла.
// ...
Выборка = Запрос.Выполнить().Выбрать();
ИтогоСумма = 0; // ПРАВИЛЬНО: инициализация до начала цикла
Пока Выборка.Следующий() Цикл
ЗаполнитьЗначенияСвойств(ОбластьДанные.Параметры, Выборка);
ИтогоСумма = ИтогоСумма + Выборка.Сумма; // Накопление суммы
ТабДок.Вывести(ОбластьДанные);
КонецЦикла;
ОбластьПодвал.Параметры.ИтогоСумма = ИтогоСумма;
ТабДок.Вывести(ОбластьПодвал);
// ...
Решение 2: Оптимальный подход с использованием языка запросов
Хотя предыдущее решение работает, оно не является оптимальным, особенно для документов с большим количеством строк. Расчет итогов в цикле на языке 1С создает дополнительную нагрузку. Гораздо эффективнее поручить эту задачу системе управления базами данных (СУБД) с помощью встроенных возможностей языка запросов.
Для этого используется конструкция ИТОГИ.
Модифицируем наш запрос:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ПриобретениеТоваровУслугТовары.НомерСтроки,
| ПриобретениеТоваровУслугТовары.Номенклатура.Наименование КАК Материал,
| ПриобретениеТоваровУслугТовары.Сумма
|ИЗ
| Документ.ПриобретениеТоваровУслуг.Товары КАК ПриобретениеТоваровУслугТовары
|ГДЕ
| ПриобретениеТоваровУслугТовары.Ссылка = &Ссылка
|
|ИТОГИ
| СУММА(Сумма)
|ПО
| ОБЩИЕ";
Запрос.УстановитьПараметр("Ссылка", СсылкаНаДокумент);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
// Цикл для вывода строк остается прежним
Пока Выборка.Следующий() Цикл
ЗаполнитьЗначенияСвойств(ОбластьДанные.Параметры, Выборка);
ТабДок.Вывести(ОбластьДанные);
КонецЦикла;
// Получаем итоговую сумму напрямую из результата запроса, без ручного подсчета
ОбластьПодвал.Параметры.ИтогоСумма = РезультатЗапроса.Итог("Сумма");
ТабДок.Вывести(ОбластьПодвал);
Этот подход не только более производителен, но и делает код чище и надежнее, избавляя от необходимости вручную управлять переменными-накопителями.