В работе с документами, содержащими табличные части (например, «Заказ клиента» в 1С:Управление торговлей), часто возникает задача обеспечить мгновенный пересчет итоговых значений. Когда пользователь меняет количество, цену или скидку в строке, сумма этой строки и общая сумма документа должны обновляться автоматически, без необходимости сохранять или перепроводить документ. Это делает интерфейс более отзывчивым и удобным для пользователя. Разберем по шагам, как реализовать такой функционал на управляемых формах.
Проанализируем типичную ситуацию: у нас есть документ с табличной частью Товары, в которой есть колонки Количество, Цена, СкидкаПроцент и Сумма. Также в «шапке» документа есть поле СуммаДокумента. Наша цель — автоматизировать расчеты при изменении данных в первых трех колонках.
Самый прямой способ решения этой задачи — использовать события элементов формы. Для каждой колонки, изменение которой должно запускать пересчет (Количество, Цена, СкидкаПроцент), необходимо создать свой обработчик события ПриИзменении.
Шаг 1: Создание обработчиков событий
Откроем форму документа в конфигураторе. В дереве элементов формы найдем нашу таблицу (например, Товары) и внутри нее — нужные поля (ТоварыКоличество, ТоварыЦена, ТоварыСкидкаПроцент). Для каждого из этих полей в палитре свойств на вкладке «События» найдем событие ПриИзменении и создадим для него клиентский обработчик (с директивой &НаКлиенте).
Шаг 2: Написание кода для пересчета строки и документа
Внутри обработчика события нам нужно выполнить две операции:
Для получения данных строки, в которой пользователь вносит изменения, используется свойство ТекущиеДанные элемента формы-таблицы. Обращение выглядит так: Элементы.ИмяТаблицы.ТекущиеДанные.
Рассмотрим код для обработчика изменения количества:
&НаКлиенте
Процедура ТоварыКоличествоПриИзменении(Элемент)
// 1. Получаем доступ к данным текущей строки, в которой произошло изменение
СтрокаТабличнойЧасти = Элементы.Товары.ТекущиеДанные;
// 2. Выполняем расчет суммы по строке с учетом скидки
// Формула: Сумма = Количество * Цена * (1 - Скидка / 100)
СуммаСтроки = СтрокаТабличнойЧасти.Количество * СтрокаТабличнойЧасти.Цена * (1 - СтрокаТабличнойЧасти.СкидкаПроцент / 100);
// 3. Записываем результат в колонку "Сумма" текущей строки
СтрокаТабличнойЧасти.Сумма = СуммаСтроки;
// 4. Вызываем процедуру пересчета итогов по документу
РассчитатьСуммуДокумента();
КонецПроцедуры;
Шаг 3: Пересчет итогов документа
Для пересчета общей суммы документа можно использовать два подхода. Первый, менее эффективный, — это перебор всех строк в цикле. Второй, рекомендуемый и более производительный, — использование метода Итог().
Неэффективный способ (через цикл):
&НаКлиенте
Процедура РассчитатьСуммуДокумента()
ИтоговаяСумма = 0;
Для Каждого СтрокаТовара Из Объект.Товары Цикл
ИтоговаяСумма = ИтоговаяСумма + СтрокаТовара.Сумма;
КонецЦикла;
Объект.СуммаДокумента = ИтоговаяСумма;
КонецПроцедуры;
Этот метод работает, но при большом количестве строк (сотни или тысячи) может вызывать заметные задержки в интерфейсе, так как все вычисления происходят на клиенте.
Эффективный способ (через метод Итог()):
Платформа 1С предоставляет специальный метод Итог() для табличных частей объекта, который вычисляет сумму по указанной колонке гораздо быстрее. Это оптимальный выбор.
&НаКлиенте
Процедура РассчитатьСуммуДокумента()
// Метод Итог() быстро подсчитывает сумму значений в указанной колонке
Объект.СуммаДокумента = Объект.Товары.Итог("Сумма");
КонецПроцедуры;
Теперь нужно создать аналогичные обработчики ТоварыЦенаПриИзменении и ТоварыСкидкаПроцентПриИзменении. Их код будет абсолютно идентичен ТоварыКоличествоПриИзменении. Это приводит к дублированию кода, что не является хорошей практикой. Разберем, как это исправить.
Чтобы избежать дублирования и сделать код более читаемым и легким в поддержке, вынесем повторяющуюся логику в отдельные общие процедуры.
Шаг 1: Создание процедуры для расчета строки
Создадим отдельную клиентскую процедуру, которая будет принимать на вход строку табличной части и выполнять расчет суммы только для нее.
&НаКлиенте
// Процедура для расчета суммы одной строки табличной части
Процедура РассчитатьСуммуСтроки(СтрокаТЧ)
Сумма = СтрокаТЧ.Количество * СтрокаТЧ.Цена;
СуммаСоСкидкой = Сумма * (1 - СтрокаТЧ.СкидкаПроцент / 100);
// Присваиваем рассчитанное значение
СтрокаТЧ.Сумма = СуммаСоСкидкой;
КонецПроцедуры;
Шаг 2: Модификация обработчиков событий
Теперь наши обработчики событий станут гораздо короче и понятнее. Они будут просто получать текущую строку и вызывать общие процедуры для расчета строки и итогов документа.
&НаКлиенте
Процедура ТоварыКоличествоПриИзменении(Элемент)
// Получаем текущую строку
СтрокаТЧ = Элементы.Товары.ТекущиеДанные;
// Вызываем общую процедуру расчета для этой строки
РассчитатьСуммуСтроки(СтрокаТЧ);
// Вызываем процедуру пересчета итогов
РассчитатьСуммуДокумента();
КонецПроцедуры;
&НаКлиенте
Процедура ТоварыЦенаПриИзменении(Элемент)
СтрокаТЧ = Элементы.Товары.ТекущиеДанные;
РассчитатьСуммуСтроки(СтрокаТЧ);
РассчитатьСуммуДокумента();
КонецПроцедуры;
&НаКлиенте
Процедура ТоварыСкидкаПроцентПриИзменении(Элемент)
СтрокаТЧ = Элементы.Товары.ТекущиеДанные;
РассчитатьСуммуСтроки(СтрокаТЧ);
РассчитатьСуммуДокумента();
КонецПроцедуры;
// Процедура расчета итогов остается прежней
&НаКлиенте
Процедура РассчитатьСуммуДокумента()
Объект.СуммаДокумента = Объект.Товары.Итог("Сумма");
КонецПроцедуры;
Такой подход не только избавляет от дублирования, но и упрощает внесение изменений в будущем. Если формула расчета суммы изменится, ее нужно будет поправить только в одном месте — в процедуре РассчитатьСуммуСтроки.
Контекст выполнения &НаКлиенте
Важно понимать, почему все эти операции выполняются с директивой &НаКлиенте. Это обеспечивает мгновенную реакцию интерфейса на действия пользователя без отправки запросов на сервер. Для пользователя это выглядит как плавная и быстрая работа программы. Серверные вызовы для таких частых операций привели бы к заметным "тормозам".
Серверная проверка для целостности данных
Хотя расчет на клиенте удобен для пользователя, он не гарантирует 100% корректность данных, которые будут записаны в базу. Например, данные могут быть изменены программно в обход формы. Поэтому критически важно продублировать логику пересчета итогов на сервере в событии ПередЗаписьюНаСервере. Это гарантирует, что в базу данных всегда будут записаны верные итоговые суммы.
&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
// Пересчитываем итоговую сумму документа перед записью
// чтобы гарантировать целостность данных
ТекущийОбъект.СуммаДокумента = ТекущийОбъект.Товары.Итог("Сумма");
// Здесь также можно пересчитать суммы для всех строк, если есть сомнения в клиентских расчетах
Для Каждого Стр Из ТекущийОбъект.Товары Цикл
Стр.Сумма = Стр.Количество * Стр.Цена * (1 - Стр.СкидкаПроцент / 100);
КонецЦикла;
// Повторно пересчитываем итог после пересчета всех строк
ТекущийОбъект.СуммаДокумента = ТекущийОбъект.Товары.Итог("Сумма");
КонецПроцедуры;
Таким образом, мы рассмотрели полный цикл реализации автоматического пересчета сумм в документах: от простого решения с обработчиками событий до его оптимизации и обеспечения надежности данных с помощью серверных проверок. Этот подход позволяет создавать удобные и отзывчивые интерфейсы, не жертвуя при этом целостностью информации в базе данных.