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