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

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

В работе с документами, содержащими табличные части (например, «Заказ клиента» в 1С:Управление торговлей), часто возникает задача обеспечить мгновенный пересчет итоговых значений. Когда пользователь меняет количество, цену или скидку в строке, сумма этой строки и общая сумма документа должны обновляться автоматически, без необходимости сохранять или перепроводить документ. Это делает интерфейс более отзывчивым и удобным для пользователя. Разберем по шагам, как реализовать такой функционал на управляемых формах.

Проанализируем типичную ситуацию: у нас есть документ с табличной частью Товары, в которой есть колонки Количество, Цена, СкидкаПроцент и Сумма. Также в «шапке» документа есть поле СуммаДокумента. Наша цель — автоматизировать расчеты при изменении данных в первых трех колонках.

Решение 1: Использование событий полей табличной части

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

Шаг 1: Создание обработчиков событий

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

Шаг 2: Написание кода для пересчета строки и документа

Внутри обработчика события нам нужно выполнить две операции:

  1. Получить данные текущей строки, в которой произошли изменения.
  2. Рассчитать новую сумму для этой строки.
  3. Пересчитать общую сумму по всему документу.

Для получения данных строки, в которой пользователь вносит изменения, используется свойство ТекущиеДанные элемента формы-таблицы. Обращение выглядит так: Элементы.ИмяТаблицы.ТекущиеДанные.

Рассмотрим код для обработчика изменения количества:


&НаКлиенте
Процедура ТоварыКоличествоПриИзменении(Элемент)
    // 1. Получаем доступ к данным текущей строки, в которой произошло изменение
    СтрокаТабличнойЧасти = Элементы.Товары.ТекущиеДанные;
    
    // 2. Выполняем расчет суммы по строке с учетом скидки
    // Формула: Сумма = Количество * Цена * (1 - Скидка / 100)
    СуммаСтроки = СтрокаТабличнойЧасти.Количество * СтрокаТабличнойЧасти.Цена * (1 - СтрокаТабличнойЧасти.СкидкаПроцент / 100);
    
    // 3. Записываем результат в колонку "Сумма" текущей строки
    СтрокаТабличнойЧасти.Сумма = СуммаСтроки;
    
    // 4. Вызываем процедуру пересчета итогов по документу
    РассчитатьСуммуДокумента();
КонецПроцедуры;

Шаг 3: Пересчет итогов документа

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

Неэффективный способ (через цикл):


&НаКлиенте
Процедура РассчитатьСуммуДокумента()
    ИтоговаяСумма = 0;
    Для Каждого СтрокаТовара Из Объект.Товары Цикл
        ИтоговаяСумма = ИтоговаяСумма + СтрокаТовара.Сумма;
    КонецЦикла;
    Объект.СуммаДокумента = ИтоговаяСумма;
КонецПроцедуры;

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

Эффективный способ (через метод Итог()):

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


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

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

Решение 2: Оптимизация через общие процедуры (рефакторинг)

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

Шаг 1: Создание процедуры для расчета строки

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


&НаКлиенте
// Процедура для расчета суммы одной строки табличной части
Процедура РассчитатьСуммуСтроки(СтрокаТЧ)
    Сумма = СтрокаТЧ.Количество * СтрокаТЧ.Цена;
    СуммаСоСкидкой = Сумма * (1 - СтрокаТЧ.СкидкаПроцент / 100);
    
    // Присваиваем рассчитанное значение
    СтрокаТЧ.Сумма = СуммаСоСкидкой;
КонецПроцедуры;

Шаг 2: Модификация обработчиков событий

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


&НаКлиенте
Процедура ТоварыКоличествоПриИзменении(Элемент)
    // Получаем текущую строку
    СтрокаТЧ = Элементы.Товары.ТекущиеДанные;
    
    // Вызываем общую процедуру расчета для этой строки
    РассчитатьСуммуСтроки(СтрокаТЧ);
    
    // Вызываем процедуру пересчета итогов
    РассчитатьСуммуДокумента();
КонецПроцедуры;

&НаКлиенте
Процедура ТоварыЦенаПриИзменении(Элемент)
    СтрокаТЧ = Элементы.Товары.ТекущиеДанные;
    РассчитатьСуммуСтроки(СтрокаТЧ);
    РассчитатьСуммуДокумента();
КонецПроцедуры;

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

// Процедура расчета итогов остается прежней
&НаКлиенте
Процедура РассчитатьСуммуДокумента()
    Объект.СуммаДокумента = Объект.Товары.Итог("Сумма");
КонецПроцедуры;

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

Дополнительные соображения и лучшие практики

Контекст выполнения &НаКлиенте

Важно понимать, почему все эти операции выполняются с директивой &НаКлиенте. Это обеспечивает мгновенную реакцию интерфейса на действия пользователя без отправки запросов на сервер. Для пользователя это выглядит как плавная и быстрая работа программы. Серверные вызовы для таких частых операций привели бы к заметным "тормозам".

Серверная проверка для целостности данных

Хотя расчет на клиенте удобен для пользователя, он не гарантирует 100% корректность данных, которые будут записаны в базу. Например, данные могут быть изменены программно в обход формы. Поэтому критически важно продублировать логику пересчета итогов на сервере в событии ПередЗаписьюНаСервере. Это гарантирует, что в базу данных всегда будут записаны верные итоговые суммы.


&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
    // Пересчитываем итоговую сумму документа перед записью
    // чтобы гарантировать целостность данных
    ТекущийОбъект.СуммаДокумента = ТекущийОбъект.Товары.Итог("Сумма");
    
    // Здесь также можно пересчитать суммы для всех строк, если есть сомнения в клиентских расчетах
    Для Каждого Стр Из ТекущийОбъект.Товары Цикл
        Стр.Сумма = Стр.Количество * Стр.Цена * (1 - Стр.СкидкаПроцент / 100);
    КонецЦикла;
    
    // Повторно пересчитываем итог после пересчета всех строк
    ТекущийОбъект.СуммаДокумента = ТекущийОбъект.Товары.Итог("Сумма");
КонецПроцедуры;

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

← На главную