В практике разработки на платформе 1С:Предприятие 8.3 часто возникает задача вывода расчетных данных в табличную часть (ТЧ). Например, когда нам нужно в реальном времени показывать результат перемножения цены на количество в каждой строке, но при этом мы не хотим или не можем хранить этот результат в базе данных. В этой статье мы подробно разберем, как правильно инициализировать такие расчеты при открытии документа и как поддерживать их актуальность при редактировании строк.
Рассмотрим ситуацию, с которой часто сталкиваются начинающие разработчики. Попытка обратиться к данным табличной части в процедуре ПриОткрытии на клиенте через Элементы.Товары.ТекущиеДанные часто приводит к ошибке "Значение не является значением объектного типа". Проанализируем причину:
ПриОткрытии текущая строка таблицы еще не определена платформой, поэтому ТекущиеДанные возвращают Неопределено.Чтобы при открытии существующего документа колонки с итогами сразу были заполнены, нам необходимо выполнить обход всей табличной части на стороне сервера. Наилучшим местом для этого является процедура ПриЧтенииНаСервере или ПриСозданииНаСервере.
Разберем, почему ПриЧтенииНаСервере предпочтительнее: это событие срабатывает каждый раз, когда объект перечитывается из базы данных (например, после записи или при нажатии кнопки "Перечитать"). Если ваша колонка "Итого" является реквизитом формы (не хранится в базе), то после любого перечитывания объекта она будет обнуляться, и её нужно заполнять заново.
Рассмотрим пример кода для массового пересчета строк:
&НаСервере
Процедура ЗаполнитьРасчетныеПоказателиНаСервере()
// Перебираем все строки табличной части "Товары"
Для Каждого СтрокаТЧ Из Объект.Товары Цикл
// Выполняем расчет. Используем функцию Окр для точности копеек.
СтрокаТЧ.Итого = СтрокаТЧ.Количество * СтрокаТЧ.Тариф;
КонецЦикла;
КонецПроцедуры
&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
// Вызываем нашу процедуру пересчета при открытии существующего документа
ЗаполнитьРасчетныеПоказателиНаСервере();
КонецПроцедуры
После того как мы обеспечили заполнение данных при открытии, нужно сделать так, чтобы при изменении пользователем количества или цены сумма пересчитывалась мгновенно. Для этого мы будем использовать клиентские события элементов формы.
Выясним, как оптимизировать этот процесс. Чтобы не дублировать код в обработчиках разных колонок, создадим одну общую процедуру РассчитатьСтрокуТЧ.
&НаКлиенте
Процедура РассчитатьСтрокуТЧ(СтрокаТЧ)
// Проверяем, что строка существует
Если СтрокаТЧ <> Неопределено Тогда
СтрокаТЧ.Итого = СтрокаТЧ.Количество * СтрокаТЧ.Тариф;
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ТоварыКоличествоПриИзменении(Элемент)
// Передаем текущую строку в общую процедуру расчета
СтрокаДанных = Элементы.Товары.ТекущиеДанные;
РассчитатьСтрокуТЧ(СтрокаДанных);
КонецПроцедуры
&НаКлиенте
Процедура ТоварыТарифПриИзменении(Элемент)
// Аналогично для изменения тарифа
СтрокаДанных = Элементы.Товары.ТекущиеДанные;
РассчитатьСтрокуТЧ(СтрокаДанных);
КонецПроцедуры
При работе с вычислениями в 1С важно учитывать несколько критических моментов, которые помогут избежать "падения" программы и логических ошибок.
1. Проверка на Null и Неопределено. Всегда проверяйте, что реквизиты, участвующие в математических операциях, содержат числа. Если Количество или Тариф окажутся пустыми, система может выдать ошибку или некорректный результат.
2. Округление. При расчете денежных показателей всегда используйте функцию Окр(Значение, 2). Это предотвратит появление "хвостов" из множества знаков после запятой, которые могут возникнуть из-за особенностей хранения дробных чисел в памяти ПК.
3. Использование общего модуля. Если логика расчета суммы сложная (например, учитывает скидки, наценки и НДС), рекомендуется вынести её в общий модуль с флагами "Клиент" и "Сервер". Это позволит вызывать одну и ту же функцию пересчета и из серверного цикла при открытии, и из клиентского обработчика при вводе данных. Это гарантирует единство алгоритмов во всей системе.
Пример вызова из общего модуля:
// В общем модуле (например, РасчетыКлиентСервер)
Процедура РассчитатьСуммуСтроки(СтрокаТаблицы) Экспорт
СтрокаТаблицы.Итого = Окр(СтрокаТаблицы.Количество * СтрокаТаблицы.Тариф, 2);
КонецПроцедуры
Проанализируем ситуацию: мы рассчитали значения в каждой строке, но итоговая сумма по всей колонке (в подвале таблицы) может не обновиться автоматически. Чтобы 1С пересчитала итог внизу списка, убедитесь, что:
Объект.Товары.ИтогИтого (если реквизит в базе) или используется автоподсчет.ЭтаФорма.ОбновитьОтображениеДанных(), хотя обычно изменение реквизита строки ТЧ вызывает автоматический пересчет итогов подвала платформой.Подведем итог: для корректного расчета "Итого" мы используем связку из серверного обхода в ПриЧтенииНаСервере (для старых данных) и клиентских обработчиков ПриИзменении (для интерактивной работы). Такой подход обеспечивает высокую производительность и корректное отображение данных в любой ситуации.