Как правильно суммировать табличные части в шапку документа в 1С?

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

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

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

Отображение суммы на форме без программирования

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

Рассмотрим, как это сделать:

  1. Добавьте новое поле на форму или включите отображение подвала в таблице.
  2. В свойствах поля найдите параметр ПутьКДанным.
  3. Укажите виртуальный реквизит платформы, который формируется по шаблону: Объект.ИмяТабличнойЧасти.ИтогИмяКолонки (например, Объект.Лимиты.ИтогСуммаСчета).

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

Почему сумму все-таки нужно хранить в объекте?

Часто разработчики задают вопрос: зачем плодить сущности и заводить отдельный реквизит в шапке, если сумму всегда можно получить динамически из табличных частей? Если сумма нужна только на форме — это справедливое замечание. Однако в типовых конфигурациях 1С (ERP, УТ, БП) хранение итоговой суммы в шапке документа — это строгий стандарт.

Разберем причину такого подхода. Речь идет о намеренной денормализации данных ради оптимизации производительности СУБД. При построении сложных отчетов, журналов документов или динамических списков системе потребуется вывести общую сумму документа. Если реквизита в шапке нет, платформе придется каждый раз выполнять тяжелое соединение таблиц с группировкой и агрегатной функцией расчета сумм по всем строкам табличной части. Это существенно замедлит работу системы на больших объемах данных. Чтение готового числа из основной таблицы документа работает в десятки раз быстрее.

Пересчет суммы на клиенте: избегаем лишних вызовов сервера

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


&НаКлиенте
Процедура ЛимитыСуммаСчетаПриИзменении(Элемент)
    ИтогПоСумме();
КонецПроцедуры

&НаСервере
Процедура ИтогПоСумме()
    Объект.Сумма = Объект.Лимиты.Итог("СуммаСчета") + Объект.ЗаявкиНаРасходование.Итог("Сумма");
КонецПроцедуры

Проанализируем этот код. Использование директивы &НаСервере для такого простого расчета — это грубая ошибка производительности. Тип ДанныеФормыКоллекция, которым представлена табличная часть на управляемой форме, поддерживает метод Итог() непосредственно на клиенте. Вызов сервера при каждом изменении цифры в ячейке заставит систему гонять контекст формы по сети туда-обратно, вызывая микро-фризы (зависания интерфейса).

Код должен быть полностью клиентским:


&НаКлиенте
Процедура ИтогПоСумме()
    Объект.Сумма = Объект.Лимиты.Итог("СуммаСчета") + Объект.ЗаявкиНаРасходование.Итог("Сумма");
КонецПроцедуры

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

Гарантированный расчет: Модуль объекта

Ограничиваться расчетом только в модуле формы категорически нельзя. Пользовательский интерфейс — не единственный способ создания и изменения данных. Документы могут генерироваться программно: загружаться через обмены данными (XML, JSON, OData), создаваться внешними обработками, перепроводиться регламентными заданиями. Во всех этих случаях форма документа вообще не открывается.

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

Посмотрим на идеальный паттерн гарантированного расчета — использование события ПередЗаписью в Модуле объекта документа:


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    // Окончательный пересчет суммы перед физической записью в СУБД
    Сумма = Лимиты.Итог("СуммаСчета") + ЗаявкиНаРасходование.Итог("Сумма");
КонецПроцедуры

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

  1. Для интерфейса и удобства пользователя: выполняем быстрый пересчет с помощью &НаКлиенте на форме документа без обращений к серверу.
  2. Для целостности данных: обязательно дублируем окончательный пересчет в Модуле Объекта в событии ПередЗаписью, чтобы защитить систему от записи некорректных данных при программных интеграциях.
← На главную