При разработке конфигураций на платформе 1С:Предприятие 8 (особенно на управляемых формах) часто возникает задача автоматического расчета итоговых показателей. Например, когда пользователь заполняет табличную часть документа товарами и указывает их стоимость, итоговая сумма должна автоматически складываться и записываться в реквизит шапки «Всего». В этой статье мы подробно разберем, как правильно реализовать этот функционал, рассмотрим подводные камни стандартных подходов и изучим альтернативные методы решения задачи.
Начинающие разработчики часто пытаются решить задачу «в лоб»: при изменении суммы в текущей строке прибавлять ее к общему реквизиту формы. Рассмотрим типичный пример неоптимального кода:
&НаКлиенте
Процедура СписокТоваровСуммаПриИзменении(Элемент)
Строка = Элементы.СписокТоваров.ТекущиеДанные;
Объект.всего = Объект.всего + Строка.сумма;
КонецПроцедуры
Этот подход крайне ненадежен. Проанализируем ситуацию: если пользователь сначала введет сумму 100 рублей, затем передумает и исправит ее на 150 рублей, программа прибавит к общему итогу новые 150 рублей, проигнорировав старое значение. В итоге реквизит Объект.всего станет равен 250 рублей вместо положенных 150. Кроме того, такой алгоритм совершенно не учитывает удаление строк или очистку таблицы.
Правильным и самым простым решением на платформе 1С является использование встроенного метода Итог() для коллекций данных формы. Этот метод автоматически суммирует значения по указанной колонке табличной части, избавляя нас от необходимости вручную перебирать строки в цикле или писать сложные математические формулы.
Рассмотрим правильный вариант реализации обработчика события изменения суммы:
&НаКлиенте
Процедура СписокТоваровСуммаПриИзменении(Элемент)
Объект.всего = Объект.СписокТоваров.Итог("Сумма");
КонецПроцедуры
Метод Итог() работает непосредственно на клиенте, не вызывая серверных контекстных вызовов, что гарантирует высокую скорость работы интерфейса.
Связывание расчета общего итога только лишь с событием ПриИзменении конкретной колонки табличной части — это классическая ошибка. Сумма в реквизите «Всего» не пересчитается в следующих случаях:
Чтобы исправить это поведение, мы должны запускать расчет итога при любых изменениях структуры и состава таблицы. Для этого необходимо использовать обработчик события самого элемента формы типа «Таблица» — событие ПриИзменении.
Разберем по шагам, как это сделать:
СписокТоваров).ПриИзменении (внимательно следите, чтобы это было событие всей таблицы, а не отдельной колонки).Посмотрим на пример итогового кода модуля формы:
&НаКлиенте
Процедура СписокТоваровПриИзменении(Элемент)
// Данный обработчик срабатывает при добавлении, копировании, удалении строк
РассчитатьОбщийИтогДокумента();
КонецПроцедуры
&НаКлиенте
Процедура СписокТоваровСуммаПриИзменении(Элемент)
// Данный обработчик срабатывает при ручном изменении суммы в ячейке
РассчитатьОбщийИтогДокумента();
КонецПроцедуры
&НаКлиенте
Процедура РассчитатьОбщийИтогДокумента()
Объект.всего = Объект.СписокТоваров.Итог("Сумма");
КонецПроцедуры
Вынесение расчета в отдельную вспомогательную процедуру РассчитатьОбщийИтогДокумента() позволяет избежать дублирования кода и делает разработку более структурированной.
В практике часто бывает так, что реквизит шапки «Всего» создается исключительно для удобства пользователя — чтобы он визуально контролировал итоговую сумму документа. Если записывать это значение в базу данных не требуется, в платформе 1С:Предприятие 8 предусмотрен мощный встроенный механизм автоматического подсчета итогов.
Рассмотрим, как настроить вывод итогов в подвал таблицы без программирования:
СписокТоваров).Истина.СписокТоваровСумма.Объект.СписокТоваров.ИтогСумма.Платформа автоматически займется расчетом и выводом суммы. При добавлении, удалении или изменении строк итоговое значение внизу таблицы мгновенно обновится без написания единой строчки кода на встроенном языке 1С.
В реальных бизнес-процессах пользователи редко вводят сумму строки вручную. Чаще всего они указывают количество товара и его цену, а система должна автоматически рассчитать сумму строки и обновить общий итог. Проанализируем, как правильно запрограммировать эту цепочку событий.
Для реализации автоматического расчета цены и количества нам потребуется привязать обработчики событий ПриИзменении к колонкам Количество и Цена. Посмотрим на практический пример кода:
&НаКлиенте
Процедура СписокТоваровКоличествоПриИзменении(Элемент)
СтрокаТаблицы = Элементы.СписокТоваров.ТекущиеДанные;
Если СтрокаТаблицы <> Неопределено Тогда
// Рассчитываем сумму по строке
СтрокаТаблицы.Сумма = СтрокаТаблицы.Количество * СтрокаТаблицы.Цена;
// Сразу же пересчитываем общий итог документа
РассчитатьОбщийИтогДокумента();
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура СписокТоваровЦенаПриИзменении(Элемент)
СтрокаТаблицы = Элементы.СписокТоваров.ТекущиеДанные;
Если СтрокаТаблицы <> Неопределено Тогда
// Рассчитываем сумму по строке
СтрокаТаблицы.Сумма = СтрокаТаблицы.Количество * СтрокаТаблицы.Цена;
// Сразу же пересчитываем общий итог документа
РассчитатьОбщийИтогДокумента();
КонецЕсли;
КонецПроцедуры
Здесь крайне важно делать проверку на Неопределено для переменной ТекущиеДанные, чтобы избежать потенциальных ошибок, если в момент изменения фокус строки сместился.
Если в вашей конфигурации присутствует множество однотипных документов, дублирование кода расчета сумм в модуле каждой формы считается плохим тоном разработки (нарушение архитектурного принципа DRY — Don't Repeat Yourself). Мы рекомендуем создать один клиентский Общий модуль (например, назвать его РаботаСДокументамиКлиент), установить для него свойства Клиент (управляемое приложение) и поместить туда процедуру расчета строки:
Процедура РассчитатьСуммуСтрокиТаблицы(СтрокаТаблицы) Экспорт
Если СтрокаТаблицы <> Неопределено Тогда
СтрокаТаблицы.Сумма = СтрокаТаблицы.Количество * СтрокаТаблицы.Цена;
КонецЕсли;
КонецПроцедуры
После этого код в модуле управляемой формы вашего документа существенно упростится и станет легко читаемым:
&НаКлиенте
Процедура СписокТоваровКоличествоПриИзменении(Элемент)
СтрокаТаблицы = Элементы.СписокТоваров.ТекущиеДанные;
РаботаСДокументамиКлиент.РассчитатьСуммуСтрокиТаблицы(СтрокаТаблицы);
РассчитатьОбщийИтогДокумента();
КонецПроцедуры
&НаКлиенте
Процедура СписокТоваровЦенаПриИзменении(Элемент)
СтрокаТаблицы = Элементы.СписокТоваров.ТекущиеДанные;
РаботаСДокументамиКлиент.РассчитатьСуммуСтрокиТаблицы(СтрокаТаблицы);
РассчитатьОбщийИтогДокумента();
КонецПроцедуры
Использование метода Итог() на клиенте работает превосходно для простых суммовых полей. Однако в реальной практике расчеты могут усложняться:
В этих случаях производить расчеты непосредственно на клиенте крайне не рекомендуется, а иногда и технически невозможно из-за отсутствия доступа к серверным объектам базы данных. Решением является перенос сложных расчетов на сторону сервера с использованием неконтекстных вызовов &НаСервереБезКонтекста. Это позволяет экономить трафик между клиентом и сервером и значительно повышает быстродействие всей системы.
Мы рассмотрели все основные подходы к автоматическому заполнению реквизитов формы из табличных частей в платформе 1С:Предприятие 8. Применяйте эти паттерны программирования для создания надежных, быстрых и удобных интерфейсов для ваших пользователей!