Как рассчитать и вывести итог по таблице значений на управляемой форме в 1С

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

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

Архитектурные особенности: почему "старые" методы не работают на управляемой форме

В обычных формах разработчик мог напрямую манипулировать элементами интерфейса и переменными в едином контексте. В управляемых формах интерфейс строго отделен от данных. Элементы формы (визуальные поля, таблицы) — это лишь проекция реальных данных, которые хранятся в реквизитах формы (имеющих специальные типы, такие как ДанныеФормыКоллекция, ДанныеФормыСтруктура и т.д.).

Попытка напрямую изменить свойство визуального элемента (например, писать сумму в Элементы.стСумма.Заголовок в цикле) приводит к ошибкам или к отсутствию обновлений на экране. Правильный подход — всегда изменять значение реквизита данных, к которому привязано это поле на форме. Платформа сама позаботится о том, чтобы обновить визуальное представление на клиенте. Для детального изучения принципов работы с интерфейсом можно использовать пример динамического добавления и удаления элементов управляемой формы.

---

Способ 1. Автоматический вывод итогов в подвал таблицы (без написания кода)

Многие разработчики тратят время на написание программных обработчиков для расчета суммы по колонке, не зная, что в платформе 1С этот функционал уже реализован на уровне платформенных свойств. Итоговые значения можно автоматически выводить в подвал таблицы. Это работает как для табличных частей объектов, так и для произвольных таблиц значений, добавленных в реквизиты формы.

Разберем по шагам, как настроить автоматический вывод итога без единой строчки кода:

  1. Откройте форму вашей обработки или документа в конфигураторе.
  2. В дереве элементов формы найдите элемент типа Таблица (вашу таблицу значений, выведенную на форму).
  3. Перейдите в палитру свойств этого элемента и установите флажок Подвал (Footer) в значение Истина. Это активирует отображение нижней серой строки под таблицей.
  4. Разверните элементы таблицы и выберите конкретное Поле колонки, по которой нужно посчитать сумму (например, колонку Сумма).
  5. В свойствах этого поля найдите пункт ПутьКДаннымПодвала (Footer Data Path).
  6. Нажмите на кнопку выбора (три точки) и выберите путь к данным вида: ИмяВашейТЗ.ИтогИмяКолонкиСуммы (например, ТЗ.ИтогСум).

Готово! Теперь платформа силами клиентского движка будет автоматически пересчитывать сумму по всей колонке и отображать её в подвале таблицы при любом интерактивном изменении строк пользователем. Нам не потребовалось писать ни одной строчки кода, и это решение работает максимально быстро.

---

Способ 2. Программный пересчет сумм через события колонок

Если вам необходимо выводить сумму не в подвал таблицы, а в отдельное поле ввода или декорацию на форме (например, в крупный индикатор общего итога), потребуется написать программный обработчик. Рассмотрим правильный паттерн его реализации.

Частой ошибкой является использование события таблицы ПриОкончанииРедактирования или общего события ПриИзменении всей таблицы. Это приводит к лишним срабатываниям и неудобству для пользователя. Правильнее всего привязывать расчет к событиям ПриИзменении конкретных колонок, которые влияют на результат (например, колонок Цена и Количество).

Рассмотрим пример правильной организации кода. Нам потребуется создать одну общую клиентскую процедуру расчета и вызвать ее из обработчиков изменения колонок:


&НаКлиенте
Процедура ТЗЦенаПриИзменении(Элемент)
    РассчитатьСуммуСтрокиИИтог();
КонецПроцедуры

&НаКлиенте
Процедура ТЗКоличествоПриИзменении(Элемент)
    РассчитатьСуммуСтрокиИИтог();
КонецПроцедуры

&НаКлиенте
Процедура РассчитатьСуммуСтрокиИИтог()
    // Получаем текущую строку, которую редактирует пользователь
    ТекДанные = Элементы.ТЗ.ТекущиеДанные;
    
    Если ТекДанные <> Неопределено Тогда
        // Рассчитываем сумму в текущей строке
        ТекДанные.Сум = ТекДанные.Кол * ТекДанные.Цен;
    КонецЕсли;
    
    // Рассчитываем общий итог по колонке "Сум" и записываем его в реквизит формы глСум
    // Метод Итог() доступен на клиенте для ДанныеФормыКоллекция
    глСум = ТЗ.Итог("Сум");
КонецПроцедуры

Обратите внимание на важную деталь: мы присваиваем значение переменной глСум, которая является реквизитом формы (типа Число). На форме у нас располагается поле ввода стСумма, у которого в свойстве ПутьКДанным указано именно глСум. Как только мы меняем значение реквизита в коде, платформа мгновенно обновляет отображение на форме.

Важная деталь: почему метод Итог() может не работать на клиенте?

Иногда при вызове метода Итог() на клиенте разработчики получают нулевое значение или ошибку. Это происходит по двум причинам:

---

Опасность обхода коллекции в цикле на клиенте (Проблема производительности)

В сообщениях темы автор пытается обойти таблицу значений в цикле на клиенте для ручного подсчета суммы:


Для Каждого ТекСтрока Из ТЗ Цикл
    СМ = СМ + ТекСтрока.Сум;
КонецЦикла;

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

Дело в том, что реквизиты формы с типом ДанныеФормыКоллекция оптимизированы для передачи по сети. Платформа не передает всю таблицу на клиент сразу — данные подгружаются порциями (по умолчанию около 20–40 строк) по мере прокрутки таблицы пользователем. Когда вы запускаете цикл Для каждого на клиенте, платформа вынуждена за кулисами совершать множественные синхронные серверные вызовы, чтобы "довыгрузить" недостающие строки с сервера.

Если в вашей таблице будет 500 строк, такой цикл выполнит десятки невидимых сетевых запросов. На локальном компьютере разработчика это может быть незаметно, но в реальной работе через интернет (в тонком или веб-клиенте) форма будет "замирать" на несколько секунд при каждом изменении количества или цены.

Правило: Для получения агрегатных величин (сумма, максимум, количество) всегда используйте штатный метод Итог(). Если вам действительно нужно выполнить сложный обход коллекции с вычислениями, делайте это на сервере, передавая управление контекстным вызовом.

---

Способ 3. Вывод таблицы значений и итогов на печать с помощью макета

Рано или поздно встает задача вывести заполненную таблицу значений и рассчитанные итоги на печать. Автор темы в сообщении №16 пришел к верному решению — использованию макета табличного документа.

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

В исходном коде автора получение макета выглядит так:


&НаСервереБезКонтекста
Функция ПолучитьМакетНаСервере()
    ОбработкаОбъект = Новый ("ВнешняяОбработкаОбъект.МояОбработка");
    Возврат ОбработкаОбъект.ПолучитьМакет("Макет");
КонецФункции

В чем опасность этого кода? Конструкция Новый ("ВнешняяОбработкаОбъект.МояОбработка") жестко привязывает имя обработки к конкретной строке "МояОбработка". Если пользователь переименует файл внешней обработки на диске или если вы загрузите её в справочник "Дополнительные отчеты и обработки", система выдаст критическую ошибку, так как объект с таким именем не будет зарегистрирован в метаданных.

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


&НаСервере
Функция ПечатьЗаявкиНаСервере()
    
    // Создаем результирующий табличный документ
    ТабДок = Новый ТабличныйДокумент;
    ТабДок.КлючПараметровПечати = "ПараметрыПечати_Заявка";
    
    // БЕЗОПАСНЫЙ СПОСОБ: получаем объект текущей запущенной обработки из контекста формы.
    // Этот метод гарантирует корректную работу при любом переименовании файла.
    ОбработкаОбъект = РеквизитФормыВЗначение("Объект");
    Макет = ОбработкаОбъект.ПолучитьМакет("Макет");
    
    // Получаем области макета
    ОбластьШапка  = Макет.ПолучитьОбласть("Шапка");
    ОбластьСтрока = Макет.ПолучитьОбласть("Строка");
    ОбластьПодвал = Макет.ПолучитьОбласть("Подвал");
    
    // Заполняем параметры шапки из реквизитов формы
    // Предположим, у нас есть реквизит формы КонтрагентСсылка
    ОбластьШапка.Параметры.Контрагент = СокрЛП(КонтрагентСсылка);
    ОбластьШапка.Параметры.ДатаОформления = ТекущаяДата();
    ТабДок.Вывести(ОбластьШапка);
    
    НомерСтроки = 0;
    
    // Обходим коллекцию строк ТЗ на сервере (здесь это абсолютно безопасно и быстро!)
    Для Каждого СтрокаТЗ Из ТЗ Цикл
        НомерСтроки = НомерСтроки + 1;
        
        // Заполняем параметры строки
        ОбластьСтрока.Параметры.Ном      = НомерСтроки;
        ОбластьСтрока.Параметры.Товар    = СтрокаТЗ.Товар;
        ОбластьСтрока.Параметры.Кол      = СтрокаТЗ.Кол;
        ОбластьСтрока.Параметры.Цен      = СтрокаТЗ.Цен;
        ОбластьСтрока.Параметры.Сум      = СтрокаТЗ.Сум;
        
        // Выводим заполненную строку в итоговый документ
        ТабДок.Вывести(ОбластьСтрока);
    КонецЦикла;
    
    // Заполняем параметры подвала. 
    // Вместо ручного суммирования в цикле используем быстрый метод Итог()
    ОбластьПодвал.Параметры.ИтогоСумма = ТЗ.Итог("Сум");
    ТабДок.Вывести(ОбластьПодвал);
    
    Возврат ТабДок;
    
КонецФункции

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

← На главную