Как исправить внешнюю печатную форму в 1С, если не заполняется шапка и неверно считаются итоги?

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

При создании внешних печатных форм в 1С, особенно на начальном этапе, часто возникают одни и те же типовые проблемы. Самая распространенная ситуация: печатная форма открывается, табличная часть с товарами или услугами заполняется, но шапка и подвал документа остаются пустыми. Либо в подвале отображаются некорректные итоговые суммы. Рассмотрим по шагам, как диагностировать и исправить эти ошибки на примере создания печатной формы для документа «Приобретение товаров и услуг» в 1С: Комплексная автоматизация 2.

Проблема 1: Пустые данные в шапке печатной формы

Проанализируем исходную ситуацию. У нас есть код внешней обработки, который должен формировать печатную форму. Процедура печати вызывается, но в макет не передаются данные о номере, дате документа, контрагенте, складе и т.д.

Выясним причину:

Основная причина кроется в том, что в функцию, которая непосредственно формирует табличный документ, не передается ссылка на сам документ, из которого была вызвана печать. Посмотрим на первоначальный код:


Процедура Печать(МассивОбъектов, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
    УправлениеПечатью.ВывестиТабличныйДокументВКоллекцию(КоллекцияПечатныхФорм, 
        "ТребованиеНакладная", 
        "Требование-накладная", 
        СформироватьПечатнуюФорму(МассивОбъектов[0], ОбъектыПечати));
КонецПроцедуры

// ...

Функция СформироватьПечатнуюФорму(Флаг = Истина, Флаг2= Ложь) Экспорт
    // ...
    ОбластьШапки.Параметры.Организация = СсылкаНаОбъект.Организация;
    ОбластьШапки.Параметры.Склад = СсылкаНаОбъект.Склад;
    // ...
КонецФункции

Здесь сразу несколько моментов, на которые стоит обратить внимание:

  1. Процедура Печать — это стандартная экспортная процедура для механизма внешних печатных форм. Платформа автоматически передает в нее параметр МассивОбъектов. Этот массив содержит ссылки на документы, для которых нужно выполнить печать. Даже если мы печатаем один документ, он все равно будет в виде массива из одного элемента.
  2. В вызове СформироватьПечатнуюФорму(...) мы видим, что первый элемент массива МассивОбъектов[0] передается, но сама функция СформироватьПечатнуюФорму(Флаг = Истина, Флаг2= Ложь) не готова его принять в именованный параметр.
  3. Внутри функции для получения данных используется переменная СсылкаНаОбъект, которая нигде не определена. Соответственно, все попытки получить через нее реквизиты документа (СсылкаНаОбъект.Организация, СсылкаНаОбъект.Номер и т.д.) не приводят к результату, и шапка остается пустой.

Разберем решение по шагам:

Нужно привести в соответствие вызов функции и ее объявление. Правильно будет передать ссылку на документ и принять ее в параметр с осмысленным именем.

Шаг 1. Изменяем вызов функции СформироватьПечатнуюФорму.

В процедуре Печать передаем только необходимый параметр — ссылку на документ.


Процедура Печать(МассивОбъектов, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
    УправлениеПечатью.ВывестиТабличныйДокументВКоллекцию(КоллекцияПечатныхФорм, 
        "ТребованиеНакладная", 
        "Требование-накладная", 
        СформироватьПечатнуюФорму(МассивОбъектов[0])); // Передаем только ссылку на документ
КонецПроцедуры

Шаг 2. Корректируем объявление функции СформироватьПечатнуюФорму.

Теперь функция должна принимать один параметр, который мы и будем использовать для доступа к реквизитам. Назовем его СсылкаНаДокумент для лучшей читаемости кода.


Функция СформироватьПечатнуюФорму(СсылкаНаДокумент) Экспорт
    // ... здесь остальной код функции
КонецФункции

Шаг 3. Используем полученный параметр для заполнения шапки и запроса.

Теперь вместо неопределенной переменной СсылкаНаОбъект мы используем параметр СсылкаНаДокумент.


Функция СформироватьПечатнуюФорму(СсылкаНаДокумент) Экспорт
    ТабДок = Новый ТабличныйДокумент;
    // ...
    Макет = ПолучитьМакет("Макет");
    ОбластьШапки = Макет.ПолучитьОбласть("Шапка");

    // Заполняем параметры шапки
    ОбластьШапки.Параметры.ТекстЗаголовка = "Требование-Накладная № " + СсылкаНаДокумент.Номер + " от " + Формат(СсылкаНаДокумент.Дата, "ДФ=dd.MM.yyyy");
    ОбластьШапки.Параметры.Организация = СсылкаНаДокумент.Организация;
    ОбластьШапки.Параметры.Склад = СсылкаНаДокумент.Склад;
    ОбластьШапки.Параметры.Партнер = СсылкаНаДокумент.Партнер;
    ОбластьШапки.Параметры.Договор = СсылкаНаДокумент.Договор;
    ТабДок.Вывести(ОбластьШапки);

    // Используем ссылку в запросе
    Запрос = Новый Запрос;
    Запрос.Текст = "... |ГДЕ | ПриобретениеТоваровУслугТовары.Ссылка = &Ссылка";
    
    Запрос.УстановитьПараметр("Ссылка", СсылкаНаДокумент);
    
    // ...
    Возврат ТабДок;
КонецФункции

Обратите внимание на использование функции Формат() для вывода даты. Это хорошая практика, позволяющая отобразить дату в нужном формате без времени.

После этих изменений шапка печатной формы будет корректно заполняться данными из документа.

Проблема 2: Некорректный расчет итоговой суммы

Вторая частая ошибка — неправильный подсчет итогов. В нашем примере в подвале выводилась сумма только по последнему товару в документе, а не общая сумма.

Выясним причину:

Проблема кроется в логике работы с переменной, которая накапливает сумму. Посмотрим на ошибочный фрагмент кода:


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

Пока Выборка.Следующий() Цикл
    ИтогоСумма = 0; // ОШИБКА: переменная обнуляется на каждой итерации!
    ЗаполнитьЗначенияСвойств(ОбластьДанные.Параметры, Выборка);
    ИтогоСумма = ИтогоСумма + Выборка.Сумма;
    ТабДок.Вывести(ОбластьДанные);
КонецЦикла;

ОбластьПодвал.Параметры.ИтогоСумма = ИтогоСумма;
ТабДок.Вывести(ОбластьПодвал);
// ...

Строка ИтогоСумма = 0; находится внутри цикла обхода выборки. Это значит, что перед обработкой каждой новой строки документа накопленная сумма сбрасывается в ноль. В итоге после завершения цикла переменная ИтогоСумма будет содержать сумму только последней строки.

Решение 1: Простое исправление

Нужно вынести инициализацию переменной за пределы цикла. Переменная-счетчик должна быть объявлена и обнулена до начала цикла.


// ...
Выборка = Запрос.Выполнить().Выбрать();
ИтогоСумма = 0; // ПРАВИЛЬНО: инициализация до начала цикла

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

ОбластьПодвал.Параметры.ИтогоСумма = ИтогоСумма;
ТабДок.Вывести(ОбластьПодвал);
// ...

Решение 2: Оптимальный подход с использованием языка запросов

Хотя предыдущее решение работает, оно не является оптимальным, особенно для документов с большим количеством строк. Расчет итогов в цикле на языке 1С создает дополнительную нагрузку. Гораздо эффективнее поручить эту задачу системе управления базами данных (СУБД) с помощью встроенных возможностей языка запросов.

Для этого используется конструкция ИТОГИ.

Модифицируем наш запрос:


Запрос = Новый Запрос;
Запрос.Текст =
    "ВЫБРАТЬ
    |    ПриобретениеТоваровУслугТовары.НомерСтроки,
    |    ПриобретениеТоваровУслугТовары.Номенклатура.Наименование КАК Материал,
    |    ПриобретениеТоваровУслугТовары.Сумма
    |ИЗ
    |    Документ.ПриобретениеТоваровУслуг.Товары КАК ПриобретениеТоваровУслугТовары
    |ГДЕ
    |    ПриобретениеТоваровУслугТовары.Ссылка = &Ссылка
    |
    |ИТОГИ
    |    СУММА(Сумма)
    |ПО
    |    ОБЩИЕ";

Запрос.УстановитьПараметр("Ссылка", СсылкаНаДокумент);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

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

// Получаем итоговую сумму напрямую из результата запроса, без ручного подсчета
ОбластьПодвал.Параметры.ИтогоСумма = РезультатЗапроса.Итог("Сумма");
ТабДок.Вывести(ОбластьПодвал);

Этот подход не только более производителен, но и делает код чище и надежнее, избавляя от необходимости вручную управлять переменными-накопителями.

← На главную