Подсчёт суммы по строкам в 1С: все рабочие способы

· 9 мин

Содержание

Самый быстрый способ посчитать сумму по строкам в 1С — выделить нужные ячейки прямо в списке или в табличном документе: внизу окна программа сама покажет сумму, количество и среднее по выделенным числам. Кода при этом писать не надо.

Если сумма нужна в коде — для печатной формы, отчёта или проверки документа — на платформе 8.3 есть четыре инструмента: метод Итог() у таблицы значений, обычный цикл по строкам, ресурсы в системе компоновки данных и функция СУММА в языке запросов. Ниже — когда что применять, рабочий код по каждому способу и сравнение по скорости на больших объёмах.

Сумма выделенных строк без кода

Это то, чем пользуются чаще всего, и о чём почему-то редко пишут. Любой список в интерфейсе «Такси» (журнал документов , табличная часть, результат отчёта в табличном документе) умеет суммировать выделенное.

  1. Откройте список или сформированный отчёт.
  2. Выделите ячейки одной колонки с числами — мышью с зажатой Ctrl или Shift, либо потяните по диапазону в табличном документе.
  3. Посмотрите на нижнюю строку окна (статусную панель): там появятся Сумма, Количество, Среднее, Минимум, Максимум по выделенному.

В динамическом списке можно выделить несколько строк целиком — платформа просуммирует ту числовую колонку, по которой вы кликнули. Для разовой прикидки «сколько всего по этим документам» это быстрее любого отчёта.

Способ не оставляет следов в данных и ничего не меняет в базе. Минус один: результат нельзя сохранить или передать в другой расчёт — для этого уже нужен код.

Итог по колонке таблицы значений и табличной части

Когда данные лежат в таблице значений или в табличной части документа, не пишите цикл — у коллекции есть готовый метод Итог(). Он суммирует всю числовую колонку за один проход.

// Табличная часть документа
СуммаДокумента = Объект.Товары.Итог("Сумма");

// Произвольная таблица значений
Всего = ТаблицаЗначений.Итог("Сумма");

Аргумент — имя колонки строкой. Метод возвращает число; если строк нет, вернётся 0, а не Неопределено.

Когда нужны промежуточные итоги в разрезе — например, суммы по каждой номенклатуре — помогает метод Свернуть(). Он схлопывает строки по колонкам группировки и складывает числовые колонки:

// Группируем по номенклатуре, суммируем количество и сумму
ТаблицаЗначений.Свернуть("Номенклатура", "Количество, Сумма");

// Теперь строк столько, сколько уникальных номенклатур,
// а в колонках Количество и Сумма — итоги по каждой

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

Свернуть() меняет исходную таблицу «на месте». Если она ещё понадобится в прежнем виде, сначала сделайте копию: Копия = ТаблицаЗначений.Скопировать();.

Нужна сумма не по всей таблице, а по части строк — например, только по одному складу? Метод Скопировать() принимает отбор и возвращает новую таблицу, у которой тоже есть Итог():

Отбор = Новый Структура("Склад", ВыбранныйСклад);
СуммаПоСкладу = ТаблицаЗначений.Скопировать(Отбор).Итог("Сумма");

Так итог по условию считается без цикла. Отбор задаётся структурой «имя колонки → значение»; при пустом отборе копируется вся таблица.

Сумма в каждой строке и накопление в цикле

Иногда «сумма по строкам» означает не общий итог, а значение в самой строке — произведение количества на цену. Такой расчёт делают циклом:

Для Каждого Строка Из Объект.Товары Цикл
    Строка.Сумма = Строка.Количество * Строка.Цена;
КонецЦикла;

Цикл же нужен, когда по ходу суммирования требуется дополнительная логика — отфильтровать строки, учесть ставку, собрать несколько итогов сразу:

ИтогоТовары = 0;
ИтогоУслуги = 0;
Для Каждого Строка Из Объект.Товары Цикл
    Если Строка.ЭтоУслуга Тогда
        ИтогоУслуги = ИтогоУслуги + Строка.Сумма;
    Иначе
        ИтогоТовары = ИтогоТовары + Строка.Сумма;
    КонецЕсли;
КонецЦикла;

Накопитель объявляйте числом (= 0) до цикла, иначе первое сложение с Неопределено даст ошибку типов.

Для перебора в прямом порядке используйте Для Каждого … Из … Цикл — он не зависит от количества строк и не сбивается, если коллекция не меняется. Не удаляйте строки из коллекции внутри Для Каждого по той же коллекции — обход собьётся. Если строки надо удалять по условию, идите обратным циклом по индексу:

Для Сч = 1 По Объект.Товары.Количество() Цикл
    Строка = Объект.Товары[Объект.Товары.Количество() - Сч];
    Если Строка.Сумма = 0 Тогда
        Объект.Товары.Удалить(Строка);
    КонецЕсли;
КонецЦикла;

Цикл — самый гибкий способ, но и самый медленный: каждая итерация выполняется интерпретатором. На тысячах строк это заметно, поэтому для чистого суммирования без логики лучше Итог() или запрос.

Итоги в системе компоновки данных

Если сумма нужна в отчёте с группировками и подитогами, не считайте её руками — это работа ресурсов СКД. Ресурс складывает своё поле по каждой группировке и по отчёту в целом автоматически.

  1. В конструкторе схемы компоновки откройте вкладку Ресурсы.
  2. Добавьте поле, по которому нужен итог (например, Сумма).
  3. В колонке выражения оставьте предложенную агрегатную функцию Сумма(Сумма) — так считается общий итог. Доступны также Количество(), Среднее(), Максимум().

В готовом отчёте ресурс выводится в каждой строке-группировке и в строке «Итого» без единой строки кода. Можно задать, по каким группировкам рассчитывать ресурс, — колонка Рассчитывать по… в той же вкладке.

СКД — правильный выбор, когда итоги многоуровневые: сумма по складу, внутри него по номенклатуре, и общий итог по отчёту. Делать то же циклами — долго и хрупко.

Суммирование в запросе: функция СУММА и ИТОГИ

Когда данных много и они лежат в базе, не выгружайте всё в память — пусть суммирует СУБД. В языке запросов за это отвечает агрегатная функция СУММА вместе с СГРУППИРОВАТЬ ПО:

ВЫБРАТЬ
    Товары.Номенклатура КАК Номенклатура,
    СУММА(Товары.Сумма) КАК Сумма,
    СУММА(Товары.Количество) КАК Количество
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК Товары
СГРУППИРОВАТЬ ПО
    Товары.Номенклатура

Правило простое: все поля, которые не завёрнуты в агрегатную функцию, обязаны стоять в СГРУППИРОВАТЬ ПО. Если группировка не нужна и нужен только общий итог — уберите все обычные поля и оставьте одну СУММА.

Для общего итога вместе с детальными строками удобна конструкция ИТОГИ:

ВЫБРАТЬ
    Товары.Номенклатура КАК Номенклатура,
    Товары.Сумма КАК Сумма
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК Товары
ИТОГИ
    СУММА(Сумма)
ПО
    ОБЩИЕ

Общий итог читается первым, если обходить результат по группировкам:

Выборка = Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Выборка.Следующий();
ОбщийИтог = Выборка.Сумма; // итоговая сумма по всем строкам

Многоуровневые итоги строятся перечислением группировок через запятую — платформа посчитает подытог по каждому уровню и общий итог:

ИТОГИ
    СУММА(Сумма)
ПО
    Склад,
    ОБЩИЕ

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

Сумма массива и списка значений

Термин «коллекция значений» объединяет не только таблицу значений. Сложить числа из массива или списка значений нельзя методом Итог() — он есть только у таблицы. Здесь работает цикл:

Итого = 0;
Для Каждого Элемент Из МассивЧисел Цикл
    Итого = Итого + Элемент;
КонецЦикла;

Для списка значений (СписокЗначений) суммируют поле Значение каждого элемента:

Итого = 0;
Для Каждого ЭлементСписка Из СписокСумм Цикл
    Итого = Итого + ЭлементСписка.Значение;
КонецЦикла;

Если числа изначально разрознены, но их много и они приходят из базы, разумнее собрать их не в массив, а сразу в запрос с СУММА — складывать коллекцию в памяти имеет смысл для небольших, уже готовых наборов.

Итоговая строка в печатной форме

Отдельная задача — вывести сумму строкой «Итого» в печатной форме на табличном документе. Здесь итог считается в коде, а в макет попадает уже готовое число.

  1. В макете заведите область, например ИтоговаяСтрока, с ячейкой-параметром (ВсегоСумма).
  2. Перед выводом посчитайте итог по табличной части и положите его в параметры области.
  3. Выведите область в табличный документ.
ОбластьИтог = Макет.ПолучитьОбласть("ИтоговаяСтрока");
ОбластьИтог.Параметры.ВсегоСумма = Объект.Товары.Итог("Сумма");
ТабДок.Вывести(ОбластьИтог);

Не держите строку «Итого» фиксированным текстом в макете — при пустой табличной части она выведет прочерк или ноль. Пусть значение всегда приходит из Итог().

Типы данных и пустые значения

Чаще всего сумма «ломается» не из-за логики, а из-за типов. Несколько проверок, которые экономят время.

  • 🔢 Накопитель — число. Переменная-итог должна быть инициализирована = 0. Сложение с Неопределено или с пустой строкой вызовет ошибку преобразования.
  • ⛔ NULL из запроса. Если таблицу вы получили через Результат.Выгрузить(), в числовой колонке возможны NULL (например, после ЛЕВОЕ СОЕДИНЕНИЕ). Убирайте их ещё в запросе функцией ЕСТЬNULL(Товары.Сумма, 0) — тогда и СУММА, и последующий Итог() отработают корректно.
  • 🔡 Число и строка не складываются. Чтобы вывести итог в сообщение или в макет, приведите его к строке явно:
Текст = СтрШаблон("Итого по документу: %1 руб.", Формат(ОбщийИтог, "ЧДЦ=2"));
Сообщить(Текст);

Не склеивайте число со строкой через + без Формат() — результат зависит от типов операндов и даёт либо ошибку, либо неожиданное значение.

  • 🎯 Разрядность и округление. Формат(Число, "ЧДЦ=2") задаёт два знака после запятой только при выводе, само число не меняется. Для хранения денежной суммы с нужной точностью округляйте явно: Окр(Значение, 2). Третий параметр Окр задаёт режим — округлять ли ровно 0,5 вверх или по правилам банковского округления; для сумм обычно нужен привычный «вверх».
  • 🧮 Порядок округления важен. Складывать неокруглённые строки и округлять только итог — не то же самое, что округлять каждую строку и складывать округлённые. В документах 1С, как правило, сумма строк равна сумме уже посчитанных построчных сумм — повторяйте ту же логику, иначе итог и подитоги разойдутся на копейку.

Какой способ выбрать: сравнение по скорости

Способы не конкурируют — у каждого своя задача. Ориентир по скорости и области применения:

СпособКогда применятьСкорость на больших объёмах
Выделение в спискеРазовая прикидка вручную, без сохраненияМгновенно, кода нет
Итог() / Свернуть()Таблица уже в памяти, нужен быстрый итогВысокая — один проход, реализован платформой
Цикл по строкамНужна логика по каждой строке, несколько итоговНиже — выполняется интерпретатором
Ресурсы СКДОтчёт с многоуровневыми итогамиВысокая, но это вывод, а не расчёт в коде
Запрос СУММАДанные в базе, строк многоСамая высокая — агрегирует СУБД

Практический принцип: если данные ещё в базе — суммируйте запросом; если уже в таблице значений — методом Итог(); цикл берите только там, где без построчной логики не обойтись. Выгружать тысячи строк из запроса в память только ради того, чтобы сложить их циклом, — самая частая причина медленных обработок.

Если сомневаетесь, что быстрее на ваших данных, замерьте. Разница между циклом и Итог() на таблице в несколько сотен тысяч строк видна невооружённым глазом:

Старт = ТекущаяУниверсальнаяДатаВМиллисекундах();
Всего = ТаблицаЗначений.Итог("Сумма");
Длительность = ТекущаяУниверсальнаяДатаВМиллисекундах() - Старт;

Замер на реальном объёме надёжнее общих рассуждений: иногда узкое место не в суммировании, а в том, как таблица попала в память.

Частые вопросы

Чем `Итог()` отличается от `Свернуть()`?
Итог() возвращает одно число — сумму всей колонки. Свернуть() не возвращает значение, а перестраивает саму таблицу: схлопывает строки по группировке и складывает числовые колонки внутри неё.
Как получить сумму по условию, не перебирая все строки?
Скопируйте нужные строки в новую таблицу с отбором и примените к ней Итог(): ТаблицаЗначений.Скопировать(Отбор).Итог("Сумма"). Либо, если данные в базе, добавьте условие ГДЕ в запрос с СУММА. Цикл для этого не нужен.
Почему `СУММА` в запросе возвращает NULL, а не 0?
Если под условие не попало ни одной строки, агрегатная функция даёт NULL. Оберните её в ЕСТЬNULL(СУММА(Сумма), 0) или обработайте пустой результат в коде.
Можно ли сложить колонку динамического списка на форме?
Напрямую у динамического списка метода суммирования нет — он подгружает данные порциями. Для итога либо выделите строки и посмотрите статусную панель, либо постройте отдельный запрос с СУММА по тому же источнику.
Как посчитать сумму произведений количества на цену одной строкой?
Если колонки Количество и Цена уже заполнены, а колонки суммы нет — посчитайте её запросом: СУММА(Количество * Цена). В таблице значений для этого сначала заполните колонку Сумма циклом, затем вызовите Итог("Сумма").