Как в 1С 8.2 (обычные формы) посчитать сумму значений в таблице, если стандартный метод Итог() возвращает ноль?

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

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

Почему стандартный метод Итог() возвращает 0?

Прежде всего, нам необходимо проанализировать разницу между данными и их визуальным оформлением. В обычных формах ТабличноеПоле часто используется для отображения данных, которые физически не хранятся в основной ТабличнойЧасти объекта, а подтягиваются «на лету» из справочников. Рассмотрим типичный ошибочный код в обработчике ПриВыводеСтроки:


Процедура ТаблицаПриВыводеСтроки(Элемент, ОформлениеСтроки, ДанныеСтроки)
    Если ДанныеСтроки = Неопределенно Тогда 
        Возврат; 
    КонецЕсли;
    // Значение записывается только в оформление ячейки
    ОформлениеСтроки.Ячейки.УЕТ.Значение = ДанныеСтроки.Таблица.Количество; 
КонецПроцедуры

Выясним причину проблемы: когда мы пишем ОформлениеСтроки.Ячейки.ИмяКолонки.Значение = ..., мы меняем только визуальное отображение в конкретной строке в момент её отрисовки на экране. Сама ТабличнаяЧасть документа или справочника при этом остается пустой в данной колонке. Метод Итог("Количество") работает непосредственно с данными источника. Если в источнике данных (в коллекции) в колонке «Количество» стоят нули, то и результат метода будет нулевым, несмотря на то, что пользователь видит цифры на экране.

Решение №1: Ручной обход коллекции в цикле

Если нам нужно посчитать сумму значений, которые являются реквизитами ссылочных полей (например, количество или цена из справочника «Номенклатура», ссылка на который есть в строке), мы можем использовать цикл. Важно помнить, что цикл нужно запускать не в процедуре отрисовки интерфейса, а в отдельной процедуре расчета.

Проанализируем пример кода для ручного расчета:


Процедура РасчитатьОбщийИтог()
    Перем ОбщийИтог;
    ОбщийИтог = 0;
    
    // Перебираем строки именно табличной части (данных), а не элементы формы
    Для Каждого СтрТЧ Из ЭтотОбъект.Услуги Цикл
        // Обращаемся к реквизиту через точку от ссылки
        // Внимание: это может замедлить работу при большом количестве строк!
        ОбщийИтог = ОбщийИтог + СтрТЧ.Услуга.Количество;
    КонецЦикла;
    
    // Выводим результат в декорацию или поле на форме
    ЭлементыФормы.НадписьИтог.Заголовок = Формат(ОбщийИтог, "ЧДЦ=2");
КонецПроцедуры

Рассмотрим важный нюанс: этот метод прост в реализации, но имеет скрытую угрозу производительности. При каждом обращении СтрТЧ.Услуга.Количество система может выполнять отдельный запрос к базе данных для получения значения из справочника. Если в таблице 200–300 строк, форма может начать «подтормаживать».

Решение №2: Использование запроса (Оптимальный способ)

Для того чтобы избежать проблем с производительностью, особенно в сетевых версиях базы данных, рекомендуется использовать объект Запрос. Это позволит получить все необходимые данные из связанных справочников одним махом.

Разберем алгоритм действий:

  1. Выгружаем ссылки из табличной части в массив.
  2. Передаем массив в запрос.
  3. Суммируем значения средствами SQL (через агрегатную функцию СУММА).

Функция ПосчитатьИтогЗапросом()
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   СУММА(Услуги.Количество) КАК ИтогоКоличество
        |ИЗ
        |   Справочник.Услуги КАК Услуги
        |ГДЕ
        |   Услуги.Ссылка В(&МассивСсылок)";
    
    // Получаем массив уникальных ссылок из ТЧ документа
    МассивСсылок = ЭтотОбъект.Услуги.ВыгрузитьКолонку("Услуга");
    Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);
    
    Выборка = Запрос.Выполнить().Выбрать();
    Если Выборка.Следующий() Тогда
        Возврат Выборка.ИтогоКоличество;
    КонецЕсли;
    
    Возврат 0;
КонецФункции

Решение №3: Скрытая колонка для использования метода Итог()

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

Посмотрим, как это работает:

Типичные ошибки при расчете итогов

Проанализируем ситуацию, упомянутую в обсуждении, когда «значения скачут». Это происходит из-за попытки суммирования внутри события ПриВыводеСтроки или ПриПолученииДанных с использованием глобальной переменной.

Никогда не делайте так:


// ОШИБОЧНЫЙ КОД
Процедура ТаблицаПриВыводеСтроки(Элемент, ОформлениеСтроки, ДанныеСтроки)
    // Сумма будет расти бесконечно при каждом скроллинге таблицы!
    ОбщаяСуммаНаФорме = ОбщаяСуммаНаФорме + ДанныеСтроки.Таблица.Количество; 
КонецПроцедуры

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

Подведем итог: для получения суммы значений в цикле в обычных формах 1С 8.2 лучше всего использовать либо прямой перебор строк источника данных (если строк мало), либо запрос (если важна скорость). Главное — помнить, что визуальное содержимое ячеек в ОформлениеСтроки не является данными для агрегатных функций платформы.

← На главную