Как реализовать расчет итогов в строках табличной части при открытии формы и изменении данных?

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

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

Почему расчет в процедуре ПриОткрытии на клиенте не работает?

Рассмотрим ситуацию, с которой часто сталкиваются начинающие разработчики. Попытка обратиться к данным табличной части в процедуре ПриОткрытии на клиенте через Элементы.Товары.ТекущиеДанные часто приводит к ошибке "Значение не является значением объектного типа". Проанализируем причину:

  1. Отсутствие фокуса: В момент выполнения ПриОткрытии текущая строка таблицы еще не определена платформой, поэтому ТекущиеДанные возвращают Неопределено.
  2. Ограниченность контекста: Клиентская процедура видит только то, что уже загружено в элементы формы. Для массового пересчета всех строк документа клиентский контекст — не самое эффективное место.
  3. Пропуск данных: Если мы рассчитываем только "текущую" строку, остальные строки в таблице останутся с пустыми значениями итогов, пока пользователь по ним не кликнет.

Этап 1: Инициализация данных на сервере при чтении документа

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

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

Рассмотрим пример кода для массового пересчета строк:


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

&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
    // Вызываем нашу процедуру пересчета при открытии существующего документа
    ЗаполнитьРасчетныеПоказателиНаСервере();
КонецПроцедуры

Этап 2: Динамический пересчет при изменении данных (Клиент)

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

Выясним, как оптимизировать этот процесс. Чтобы не дублировать код в обработчиках разных колонок, создадим одну общую процедуру РассчитатьСтрокуТЧ.


&НаКлиенте
Процедура РассчитатьСтрокуТЧ(СтрокаТЧ)
    
    // Проверяем, что строка существует
    Если СтрокаТЧ <> Неопределено Тогда
        СтрокаТЧ.Итого = СтрокаТЧ.Количество * СтрокаТЧ.Тариф;
    КонецЕсли;
    
КонецПроцедуры

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

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

Этап 3: Важные нюансы и обработка ошибок

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

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

2. Округление. При расчете денежных показателей всегда используйте функцию Окр(Значение, 2). Это предотвратит появление "хвостов" из множества знаков после запятой, которые могут возникнуть из-за особенностей хранения дробных чисел в памяти ПК.

3. Использование общего модуля. Если логика расчета суммы сложная (например, учитывает скидки, наценки и НДС), рекомендуется вынести её в общий модуль с флагами "Клиент" и "Сервер". Это позволит вызывать одну и ту же функцию пересчета и из серверного цикла при открытии, и из клиентского обработчика при вводе данных. Это гарантирует единство алгоритмов во всей системе.

Пример вызова из общего модуля:


// В общем модуле (например, РасчетыКлиентСервер)
Процедура РассчитатьСуммуСтроки(СтрокаТаблицы) Экспорт
    СтрокаТаблицы.Итого = Окр(СтрокаТаблицы.Количество * СтрокаТаблицы.Тариф, 2);
КонецПроцедуры

Как обеспечить обновление итогов в подвале таблицы?

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

  1. У элемента формы "Таблица" включено свойство Подвал (Footer).
  2. Для конкретной колонки "Итого" в свойстве ПутьДанныхПодвала указано значение, например, Объект.Товары.ИтогИтого (если реквизит в базе) или используется автоподсчет.
  3. Для мгновенного визуального обновления в управляемых формах иногда полезно использовать метод ЭтаФорма.ОбновитьОтображениеДанных(), хотя обычно изменение реквизита строки ТЧ вызывает автоматический пересчет итогов подвала платформой.

Подведем итог: для корректного расчета "Итого" мы используем связку из серверного обхода в ПриЧтенииНаСервере (для старых данных) и клиентских обработчиков ПриИзменении (для интерактивной работы). Такой подход обеспечивает высокую производительность и корректное отображение данных в любой ситуации.

← На главную