Как автоматически сложить значения двух реквизитов для получения суммы в третьем в 1С 8.3

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

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

В этой статье мы подробно разберем, как правильно реализовать эту логику, почему типичные попытки написать запрос к базе данных в этот момент приводят к ошибкам, и как сделать код чистым, безопасным и оптимизированным в соответствии со стандартами разработки 1С.

Типичная ошибка новичков: попытка использовать запрос

Часто начинающие разработчики пытаются решить задачу "в лоб" через создание команды, кнопки или написание запроса. Посмотрим на пример кода, который часто вызывает сбои:


|Выбрать
|ЦенаА(Авто.ЦенаАренды) КАК ЦенаА
|ЦенаС(Авто.ЦенаСтраховки) КАК ЦенаС
|ИЗ
|Справочник.Автомобили КАК Авто

Выясним причину, почему этот подход ошибочен:

  1. Данные еще не записаны. Когда пользователь только открыл карточку создания нового автомобиля (или любого другого объекта) и ввел цифры в поля ввода, эти данные существуют только в оперативной памяти компьютера — в так называемых данных формы. В самой базе данных (в таблицах СУБД) этой записи еще физически нет. Запрос же обращается напрямую к таблицам базы данных, поэтому он либо вернет пустой результат, либо вернет старые данные, которые были сохранены до редактирования.
  2. Избыточность. Использовать тяжеловесный механизм запросов для простого сложения двух чисел на форме — это крайне неэффективно с точки зрения производительности системы.

Решение этой задачи должно происходить исключительно на клиенте "на лету" с помощью обработки событий элементов формы.

Вариант 1. Сложение реквизитов в шапке объекта

Если ваши реквизиты ЦенаАренды, ЦенаСтраховки и Сумма находятся непосредственно в самом объекте (в шапке справочника или документа), то расчет выполняется очень просто.

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

  1. Откройте управляемую форму вашего справочника или документа в конфигураторе.
  2. Найдите на форме поле ввода ЦенаАренды, откройте его палитру свойств (нажатием правой кнопки мыши или клавиши F4).
  3. В самом низу списка свойств найдите событие ПриИзменении и нажмите на лупу для создания клиентской процедуры.
  4. Повторите аналогичное действие для поля ЦенаСтраховки.

В модуле формы будут созданы две процедуры. Чтобы система автоматически рассчитывала результат, обратимся к реквизитам через ключевое слово Объект:


&НаКлиенте
Процедура ЦенаАрендыПриИзменении(Элемент)
    Объект.Сумма = Объект.ЦенаАренды + Объект.ЦенаСтраховки;
КонецПроцедуры

&НаКлиенте
Процедура ЦенаСтраховкиПриИзменении(Элемент)
    Объект.Сумма = Объект.ЦенаАренды + Объект.ЦенаСтраховки;
КонецПроцедуры

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

Вариант 2. Оптимизация кода и исключение дублирования (Принцип DRY)

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

Проанализируем ситуацию и оптимизируем код. Мы можем выделить расчет в отдельную вспомогательную процедуру:


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

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

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

Но можно пойти еще дальше и назначить **один и тот же** обработчик для обоих элементов управления. Для этого в палитре свойств поля ЦенаСтраховки в строке события ПриИзменении не создавайте новую процедуру, а выберите из выпадающего списка уже созданную процедуру ЦенаАрендыПриИзменении. Переименуем её для понятности:


&НаКлиенте
Процедура ПересчитатьСуммуПриИзменении(Элемент)
    Объект.Сумма = Объект.ЦенаАренды + Объект.ЦенаСтраховки;
КонецПроцедуры

Такой подход делает модуль формы лаконичным, легко читаемым и простым в поддержке.

Вариант 3. Если реквизиты находятся в табличной части (ТЧ)

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

При работе с табличной частью обращаться напрямую к свойству Объект нельзя, так как платформа не поймет, в какой именно строке пользователь редактирует данные. В этом случае нам нужно получить контекст текущей строки через свойство формы ТекущиеДанные.

Рассмотрим пример реализации для табличной части с именем СписокАвтомобилей:


&НаКлиенте
Процедура СписокАвтомобилейЦенаАрендыПриИзменении(Элемент)
    
    // Получаем строку, в которой сейчас находится пользователь
    СтрокаТЧ = Элементы.СписокАвтомобилей.ТекущиеДанные;
    
    // Обязательно делаем проверку на существование строки
    Если СтрокаТЧ <> Неопределено Тогда
        СтрокаТЧ.Сумма = СтрокаТЧ.ЦенаАренды + СтрокаТЧ.ЦенаСтраховки;
    КонецЕсли;
    
КонецПроцедуры

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

Вариант 4. Обеспечение безопасности типов (Защита от ошибок)

В управляемом приложении 1С числовые реквизиты по умолчанию инициализируются нулем (0). При сложении чисел ошибок не возникает. Однако при программном заполнении или интеграции с внешними системами значения реквизитов могут принять значение Неопределено или Null. Попытка сложить число со значением другого типа приведет к аварийному завершению работы программы.

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


&НаКлиенте
Процедура ПересчитатьСуммуБезопасно(Элемент)
    
    Аренда = ?(ЗначениеЗаполнено(Объект.ЦенаАренды), Объект.ЦенаАренды, 0);
    Страховка = ?(ЗначениеЗаполнено(Объект.ЦенаСтраховки), Объект.ЦенаСтраховки, 0);
    
    Объект.Сумма = Аренда + Страховка;
    
КонецПроцедуры

Использование тернарного оператора ?() гарантирует, что даже если одно из полей окажется незаполненным или пустым, в расчете будет использован ноль, и программа продолжит стабильную работу.

← На главную