Как вывести итоговую сумму в шапку печатной формы, если она рассчитывается в процессе вывода строк

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

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

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

Метод 1: Прямое изменение значения в уже выведенной области

Рассмотрим самый простой и изящный способ. Когда мы используем метод Вывести объекта ТабличныйДокумент, система возвращает нам ссылку на область, которая только что была добавлена в итоговый документ. Мы можем сохранить эту ссылку в переменную и вернуться к ней позже, когда цикл по строкам завершится и итоговая сумма будет рассчитана.

Разберем этот процесс по шагам:

  1. Выводим область шапки и сохраняем результат функции в переменную.
  2. В цикле по строкам рассчитываем суммы и накапливаем итог в переменной.
  3. После завершения цикла обращаемся к сохраненной области и устанавливаем значение суммы.

Пример реализации этого метода:


// Инициализируем документ и общую сумму
ТабДок = Новый ТабличныйДокумент;
Макет = ПолучитьМакет("Макет");
ОбластьШапка = Макет.ПолучитьОбласть("Шапка");

// Выводим шапку и запоминаем выведенную область
ВыведеннаяШапка = ТабДок.Вывести(ОбластьШапка);

ВсегоПоДокументу = 0;
ОбластьСтрока = Макет.ПолучитьОбласть("Строка");

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

// Теперь, когда сумма известна, записываем её в шапку
// Предположим, параметр в макете называется ИтогоПоДокументу
ВыведеннаяШапка.Параметры.ИтогоПоДокументу = ВсегоПоДокументу;

Важный нюанс: Чтобы этот код сработал корректно, убедитесь, что в макете для ячейки с итогом установлено свойство Заполнение в значение Параметр.

Метод 2: Использование массива для отложенного вывода

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

Посмотрим на пример кода, который демонстрирует этот подход:


ТабДок = Новый ТабличныйДокумент;
МассивОбластей = Новый Массив;
Макет = ПолучитьМакет("Макет");

// Сначала получаем область шапки, но не выводим её сразу
ОблШапка = Макет.ПолучитьОбласть("Шапка");
МассивОбластей.Добавить(ОблШапка);

ИтоговаяСумма = 0;

Для Каждого ТекСтрока Из Объект.Товары Цикл
    // Создаем НОВЫЙ экземпляр области для каждой строки
    ОблСтрока = Макет.ПолучитьОбласть("Строка");
    
    // Выполняем расчеты
    Сумма = ТекСтрока.Количество * 100; // Пример расчета
    ИтоговаяСумма = ИтоговаяСумма + Сумма;
    
    // Заполняем параметры
    ОблСтрока.Параметры.Товар = ТекСтрока.Номенклатура;
    ОблСтрока.Параметры.Сумма = Сумма;
    
    // Добавляем область в массив
    МассивОбластей.Добавить(ОблСтрока);
КонецЦикла;

// Теперь у нас есть итоговая сумма, заполняем её в первой области массива (в шапке)
МассивОбластей[0].Параметры.ИтогоПоДокументу = ИтоговаяСумма;

// Финальный вывод всех подготовленных областей
Для Каждого Область Из МассивОбластей Цикл
    ТабДок.Вывести(Область);
КонецЦикла;

Этот метод удобен тем, что позволяет полностью подготовить структуру документа в памяти перед его визуализацией. Однако стоит помнить, что при очень большом количестве строк (десятки тысяч) хранение массива объектов ОбластьТабличногоДокумента может потреблять значительный объем оперативной памяти.

Метод 3: Использование именованных ячеек в сформированном документе

Если структура вашей печатной формы сложная и вы не хотите хранить ссылки на области в переменных, можно воспользоваться механизмом именованных областей самого ТабличногоДокумента. Выясним, как это работает.

Вы можете назначить имя не просто области в макете, а конкретной ячейке (например, «ПолеИтога»). После того как документ полностью сформирован, вы обращаетесь к этой ячейке по имени прямо в результирующем документе:

ТабДок.Область("ПолеИтога").Текст = Формат(ИтоговаяСумма, "ЧДЦ=2");

Преимущество этого метода: Даже если вы добавите новые строки или колонки в макет в будущем, 1С найдет нужную ячейку по её уникальному имени, и ваш код не «сломается» из-за изменения координат.

Метод 4: Предварительный расчет (Оптимальный путь)

Рассмотрим ситуацию с точки зрения производительности. Выполнение запросов к базе данных (например, получение цен из РегистрСведений.ЦеныНоменклатуры.СрезПоследних) внутри цикла — это плохая практика («запрос в цикле»). Это существенно замедляет формирование печатной формы.

Проанализируем правильный алгоритм:

  1. Подготавливаем один запрос, который соединяет таблицу товаров документа с виртуальной таблицей среза последних цен.
  2. В этом же запросе рассчитываем общую сумму документа, используя агрегатную функцию СУММА(...) или итоги запроса.
  3. Выгружаем результат запроса в ТаблицуЗначений.
  4. Теперь у нас уже есть готовая итоговая сумма еще до начала вывода печатной формы.
  5. Выводим шапку с готовой суммой, а затем проходим циклом по таблице значений для вывода строк.

Пример структуры такого запроса:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Товары.Номенклатура,
    |	Товары.Количество,
    |	ЕСТЬNULL(Цены.Цена, 0) КАК Цена,
    |	Товары.Количество * ЕСТЬNULL(Цены.Цена, 0) КАК Сумма
    |ПОМЕСТИТЬ ВТ_Данные
    |ИЗ
    |	Документ.РеализацияТоваровУслуг.Товары КАК Товары
    |		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&ДатаДок, ) КАК Цены
    |		ПО Товары.Номенклатура = Цены.Номенклатура
    |ГДЕ
    |	Товары.Ссылка = &Ссылка
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |	*
    |ИЗ
    |	ВТ_Данные КАК ВТ_Данные
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |	СУММА(ВТ_Данные.Сумма) КАК ИтогоСумма
    |ИЗ
    |	ВТ_Данные КАК ВТ_Данные";

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

Результаты = Запрос.ВыполнитьПакет();
ТаблицаТоваров = Результаты[1].Выгрузить();
ИтоговаяСумма = Результаты[2].Выгрузить()[0].ИтогоСумма;

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

Резюме

Мы рассмотрели несколько подходов к решению задачи. Выбор метода зависит от ваших ограничений:

Помните, что использование ТабличногоДокумента в 1С предоставляет огромную гибкость: вы можете менять содержимое ячеек даже после того, как они были выведены на экран, главное — знать правильные методы обращения к этим ячейкам.

← На главную