В разработке на платформе 1С 8.2, особенно при работе с обычными формами, программисты часто сталкиваются с ситуацией, когда стандартные механизмы агрегации данных ведут себя не так, как ожидается. Типичный пример: в табличном поле отображаются данные, но попытка получить их сумму через метод Итог() выдает 0. Разберем детально, почему это происходит и какими способами можно корректно рассчитать сумму значений в цикле и не только.
Прежде всего, нам необходимо проанализировать разницу между данными и их визуальным оформлением. В обычных формах ТабличноеПоле часто используется для отображения данных, которые физически не хранятся в основной ТабличнойЧасти объекта, а подтягиваются «на лету» из справочников. Рассмотрим типичный ошибочный код в обработчике ПриВыводеСтроки:
Процедура ТаблицаПриВыводеСтроки(Элемент, ОформлениеСтроки, ДанныеСтроки)
Если ДанныеСтроки = Неопределенно Тогда
Возврат;
КонецЕсли;
// Значение записывается только в оформление ячейки
ОформлениеСтроки.Ячейки.УЕТ.Значение = ДанныеСтроки.Таблица.Количество;
КонецПроцедуры
Выясним причину проблемы: когда мы пишем ОформлениеСтроки.Ячейки.ИмяКолонки.Значение = ..., мы меняем только визуальное отображение в конкретной строке в момент её отрисовки на экране. Сама ТабличнаяЧасть документа или справочника при этом остается пустой в данной колонке. Метод Итог("Количество") работает непосредственно с данными источника. Если в источнике данных (в коллекции) в колонке «Количество» стоят нули, то и результат метода будет нулевым, несмотря на то, что пользователь видит цифры на экране.
Если нам нужно посчитать сумму значений, которые являются реквизитами ссылочных полей (например, количество или цена из справочника «Номенклатура», ссылка на который есть в строке), мы можем использовать цикл. Важно помнить, что цикл нужно запускать не в процедуре отрисовки интерфейса, а в отдельной процедуре расчета.
Проанализируем пример кода для ручного расчета:
Процедура РасчитатьОбщийИтог()
Перем ОбщийИтог;
ОбщийИтог = 0;
// Перебираем строки именно табличной части (данных), а не элементы формы
Для Каждого СтрТЧ Из ЭтотОбъект.Услуги Цикл
// Обращаемся к реквизиту через точку от ссылки
// Внимание: это может замедлить работу при большом количестве строк!
ОбщийИтог = ОбщийИтог + СтрТЧ.Услуга.Количество;
КонецЦикла;
// Выводим результат в декорацию или поле на форме
ЭлементыФормы.НадписьИтог.Заголовок = Формат(ОбщийИтог, "ЧДЦ=2");
КонецПроцедуры
Рассмотрим важный нюанс: этот метод прост в реализации, но имеет скрытую угрозу производительности. При каждом обращении СтрТЧ.Услуга.Количество система может выполнять отдельный запрос к базе данных для получения значения из справочника. Если в таблице 200–300 строк, форма может начать «подтормаживать».
Для того чтобы избежать проблем с производительностью, особенно в сетевых версиях базы данных, рекомендуется использовать объект Запрос. Это позволит получить все необходимые данные из связанных справочников одним махом.
Разберем алгоритм действий:
СУММА).
Функция ПосчитатьИтогЗапросом()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| СУММА(Услуги.Количество) КАК ИтогоКоличество
|ИЗ
| Справочник.Услуги КАК Услуги
|ГДЕ
| Услуги.Ссылка В(&МассивСсылок)";
// Получаем массив уникальных ссылок из ТЧ документа
МассивСсылок = ЭтотОбъект.Услуги.ВыгрузитьКолонку("Услуга");
Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Возврат Выборка.ИтогоКоличество;
КонецЕсли;
Возврат 0;
КонецФункции
Если вы хотите продолжать использовать удобный метод Таблица.Итог("Колонка"), можно применить техническую хитрость. Создайте в вашей табличной части реальный реквизит (например, КоличествоДляРасчета).
Посмотрим, как это работает:
ЭтотОбъект.ИмяТЧ.Итог("КоличествоДляРасчета") будет возвращать корректное значение мгновенно.Проанализируем ситуацию, упомянутую в обсуждении, когда «значения скачут». Это происходит из-за попытки суммирования внутри события ПриВыводеСтроки или ПриПолученииДанных с использованием глобальной переменной.
Никогда не делайте так:
// ОШИБОЧНЫЙ КОД
Процедура ТаблицаПриВыводеСтроки(Элемент, ОформлениеСтроки, ДанныеСтроки)
// Сумма будет расти бесконечно при каждом скроллинге таблицы!
ОбщаяСуммаНаФорме = ОбщаяСуммаНаФорме + ДанныеСтроки.Таблица.Количество;
КонецПроцедуры
События отрисовки строк вызываются платформой хаотично: при прокрутке, при изменении размеров окна, даже при простом движении мыши над таблицей. Если в этих процедурах изменять глобальные переменные итогов, вы получите непредсказуемый результат. Расчет суммы всегда должен быть событийным: он должен запускаться либо при изменении данных в строке (ПриИзменении), либо при открытии формы, либо по нажатию кнопки «Рассчитать».
Подведем итог: для получения суммы значений в цикле в обычных формах 1С 8.2 лучше всего использовать либо прямой перебор строк источника данных (если строк мало), либо запрос (если важна скорость). Главное — помнить, что визуальное содержимое ячеек в ОформлениеСтроки не является данными для агрегатных функций платформы.