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