Формирование и вывод итоговых сумм в 1С 8.3

· 9 мин

Содержание

Сумму в 1С 8.3 выводят четырьмя способами в зависимости от того, где нужен результат: в языке запросов — конструкцией ИТОГИ … ПО …, в отчёте на системе компоновки данных (СКД) — через ресурсы, в произвольном макете табличного документа — через параметры области, а в коде — методом Итог() у таблицы значений. Для простых списков хватает колонки итога под таблицей, для многоуровневых отчётов нужны группировки.

Ниже — каждый механизм с точными путями и примерами кода, который можно вставить в модуль и запустить. Разберём и типичную причину «кривых» итогов: когда сумма задваивается или возвращает пусто.

Итоги прямо в языке запросов

Самый короткий путь — попросить сам запрос посчитать итог. За это отвечает секция ИТОГИ, которая идёт после условий и упорядочивания:

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

Что здесь происходит: СУММА(Сумма) — это итоговая функция (есть ещё КОЛИЧЕСТВО, МАКСИМУМ, МИНИМУМ, СРЕДНЕЕ), а ПО Контрагент задаёт уровень группировки. Платформа добавит в результат отдельные строки-итоги по каждому контрагенту плюс один общий итог по всей выборке.

Группировок можно перечислить несколько через запятую — ПО Контрагент, Номенклатура. Тогда итог будет на каждом уровне: сначала общий, потом по контрагенту, потом по его номенклатуре.

Нужен только общий итог без разбивки по полям — пишут ИТОГИ СУММА(Сумма) ПО ОБЩИЕ. В результат добавится единственная строка с суммой по всей выборке. Этот же общий итог платформа добавляет автоматически, когда перечислены конкретные группировки, — отдельно просить его не нужно.

Для сумм по датам есть удобная конструкция ПЕРИОДАМИ. Она разворачивает итог по равным интервалам, даже если в данных нет операций за какой-то день:

ИТОГИ
    СУММА(Сумма)
ПО
    Дата ПЕРИОДАМИ(ДЕНЬ, &Начало, &Конец)

Так получают ровный ряд по дням, неделям или месяцам без «дыр» — пустые периоды попадут в результат с нулём. Без ПЕРИОДАМИ в итоги войдут только те даты, по которым реально были движения.

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

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

Результат = Запрос.Выполнить();
ВыборкаИтог = Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаИтог.Следующий() Цикл
    Сообщить("Итог по " + ВыборкаИтог.Контрагент + ": "
        + Формат(ВыборкаИтог.Сумма, "ЧГ=0"));
КонецЦикла;

Если обойти результат обычным ОбходРезультатаЗапроса.Прямой, строки итогов в выборку не попадут — это частая причина вопроса «почему итоги не выводятся».

Ресурсы и группировки в системе компоновки данных

Для отчётов, которые видит пользователь, запросы с секцией ИТОГИ почти не нужны — их заменяет СКД. Здесь суммой управляет понятие ресурс.

Порядок настройки в конструкторе схемы компоновки данных:

  1. На вкладке Наборы данных пишете запрос без всякой секции ИТОГИ — только ВЫБРАТЬ … ИЗ ….
  2. Переходите на вкладку Ресурсы и переносите числовое поле (например, Сумма) в правую часть. По умолчанию подставится выражение Сумма(Сумма).
  3. На вкладке Настройки добавляете группировки в структуру отчёта.

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

Группировки в настройках и дают многоуровневые итоги. Структура вида «Контрагент → Договор → Номенклатура» автоматически посчитает промежуточную сумму на каждом уровне и общий итог внизу — без единой строки кода.

Если один и тот же ресурс нужно по-разному считать на разных уровнях (например, остаток — на минимум, а оборот — на сумму), для этого в выражении ресурса есть поле Рассчитывать по…: там указывают конкретную группировку.

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

Пара приёмов, которые постоянно спрашивают по итогам в СКД:

  • Нарастающий итог (сумма с начала периода) — в выражении ресурса применяют функцию ВычислитьВыражениеСГруппировкойМассив или задают накопление в настройках вывода. Для типовых случаев проще добавить вычисляемое поле с накоплением, чем писать это в запросе.
  • Процент от общего — в выражении ресурса делят сумму текущей группировки на сумму по общему итогу через ВычислитьВыражение("Сумма(Сумма)", "ОбщийИтог"). Так в колонке появляется доля каждой строки.
  • Итог сверху, а не снизу. По умолчанию строка итога стоит под группировкой. Если нужно над ней, в свойствах группировки на вкладке настроек меняют расположение итогов — текст итога тогда выводится первым.

Суммы в произвольном макете табличного документа

Если отчёт строится не на СКД, а вручную — через макет и вывод областей — сумму накапливают в переменной и подставляют в параметр области.

Сначала в макете (тип Табличный документ) создают именованные области: шапку, строку детализации и строку итога. В области итога ячейку делают параметром: выделяете ячейку, в свойстве Заполнение ставите Параметр и даёте ему имя, например ВсегоСумма.

Код формирования выглядит так:

Макет = ПолучитьМакет("Макет");
ОбластьСтрока = Макет.ПолучитьОбласть("Строка");
ОбластьИтог = Макет.ПолучитьОбласть("Итог");

ТабДок = Новый ТабличныйДокумент;
ВсегоСумма = 0;

Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
    ОбластьСтрока.Параметры.Заполнить(Выборка);
    ТабДок.Вывести(ОбластьСтрока);
    ВсегоСумма = ВсегоСумма + Выборка.Сумма;
КонецЦикла;

ОбластьИтог.Параметры.ВсегоСумма = ВсегоСумма;
ТабДок.Вывести(ОбластьИтог);

Метод Параметры.Заполнить(Выборка) переносит в область одноимённые поля разом, а итог копится в переменной ВсегоСумма и выводится последней строкой.

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

Вывод итогов в печатной форме документа

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

Сумму в печатной форме получают двумя путями:

  • Из реквизита документа. Если в документе уже есть реквизит СуммаДокумента, его просто кладут в параметр области итога — пересчитывать ничего не надо.
  • Суммированием табличной части. Когда нужен итог по строкам товаров, обходят табличную часть и накапливают сумму. Есть короткий способ без цикла — метод Итог():
ВсегоПоТоварам = Документ.Товары.Итог("Сумма");
ОбластьПодвал.Параметры.ВсегоСумма = ВсегоПоТоварам;

Итог("Сумма") у табличной части возвращает сумму по колонке одной строкой. Тот же метод есть у таблицы значений — о нём ниже.

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

Расчёт суммы программно, без оформления

Иногда сумма нужна не для печати, а для логики — проверить лимит, записать в регистр, сравнить две величины. Тогда оформление лишнее, считаем в чистом коде.

Самый быстрый приём — выгрузить результат запроса в таблицу значений и взять Итог():

Таблица = Запрос.Выполнить().Выгрузить();
Всего = Таблица.Итог("Сумма");

Итог("Сумма") вернёт сумму по всей колонке независимо от числа строк. Если таблица пустая, метод вернёт 0, а не NULL — проверку на пустоту можно не делать.

Когда нужна сумма по условию (только по одному складу, только по проведённым), отберите строки методом НайтиСтроки и просуммируйте их, либо добавьте условие прямо в запрос — это всегда быстрее, чем перебор в коде:

Отобранные = Таблица.НайтиСтроки(Новый Структура("Склад", ВыбранныйСклад));
Сумма = 0;
Для Каждого Строка Из Отобранные Цикл
    Сумма = Сумма + Строка.Сумма;
КонецЦикла;

Когда строк много (десятки тысяч), не выгружайте в таблицу ради одной суммы. Пусть сумму посчитает сервер СУБД: добавьте в запрос СУММА(Сумма) без детальных полей и прочитайте единственную строку результата. Это снимает нагрузку с памяти клиента.

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

Таблица.Свернуть("Склад", "Сумма, Количество");

После этого в таблице останется по одной строке на склад, а в колонках Сумма и Количество будут подытоги. Это быстрее и короче ручного перебора.

Для вывода числа в сообщение или строку всегда применяйте форматирование, иначе получите длинный «хвост» дробной части:

Сообщить("Итого: " + Формат(Всего, "ЧДЦ=2; ЧРД=,; ЧГ= "));

Строка "ЧДЦ=2" задаёт два знака после запятой, "ЧРД=," — запятую как разделитель дробной части, "ЧГ= " — пробел между разрядами. Без этого сумма склеится с текстом в нечитаемом виде.

Почему сумма считается неверно

Три причины дают почти все случаи, когда итог «не тот». Разберём симптом и что проверить.

Диагностика: выберите симптом

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

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

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

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