Как получить сумму по всем документам таблицы в 1С

Программист 1С v8.3 (Обычные формы) Управленческий учет
← На главную

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

Метод 1: Накопление суммы в переменной в процессе вывода строк

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

Во-первых, проанализируем ситуацию, когда данные берутся из выборки или таблицы значений и выводятся в макет. Нам необходимо инициализировать переменную СуммаИтого нулевым значением. Если этого не сделать внутри процедуры, система может выдать ошибку или прибавить данные к ранее существовавшему значению, если переменная глобальная.

Посмотрим на пример реализации кода:


// Инициализируем переменную перед началом цикла
ПеременнаяИтоговаяСумма = 0;

Для Каждого СтрокаПоДокам Из ТаблицаДанных Цикл
    
    // Получаем область строки из макета
    ОбластьСтрока = Макет.ПолучитьОбласть("Строка");
    
    // Рассчитываем сумму для текущей строки
    СуммаТекущейСтроки = СтрокаПоДокам.ИсходнаяЦена * СтрокаПоДокам.ИсходноеКоличество;
    
    // Заполняем параметры области
    ОбластьСтрока.Параметры.СуммаЗаказаДо = СуммаТекущейСтроки;
    
    // Накапливаем общий итог
    ПеременнаяИтоговаяСумма = ПеременнаяИтоговаяСумма + СуммаТекущейСтроки;
    
    // Выводим строку в табличный документ
    ТабДок.Вывести(ОбластьСтрока);
    
КонецЦикла;

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

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

Метод 2: Предварительная подготовка данных в Таблице Значений

Проанализируем другой подход. Иногда расчет в цикле вывода может быть неудобен, особенно если одни и те же итоги нужно выводить в разных частях отчета (например, в начале и в конце). В таком случае эффективнее подготовить данные в ТаблицаЗначений до начала вывода.

Для этого мы добавим в таблицу значений расчетную колонку Сумма и воспользуемся встроенным методом Итог(). Выясним, как это сделать правильно:

  1. Добавляем колонку в существующую таблицу тз.
  2. Заполняем колонку значениями Цена * Количество.
  3. Вызываем метод тз.Итог("Сумма").

Разберем пример кода:


// 1. Добавляем колонку для хранения рассчитанной суммы
тз.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));

// 2. Рассчитываем сумму для каждой строки один раз
Для Каждого Стр Из тз Цикл
    Стр.Сумма = Стр.ИсходнаяЦена * Стр.ИсходноеКоличество;
КонецЦикла;

// 3. Теперь мы можем в любой момент получить общую сумму по всей таблице
ОбщийИтог = тз.Итог("Сумма");

// Или, если нам нужна сумма только по конкретному документу
СтруктураПоиска = Новый Структура("РегистраторДокументПродажи", ТекущийДокумент);
тзПоТекущемуДоку = тз.Скопировать(СтруктураПоиска);
СуммаПоДокументу = тзПоТекущемуДоку.Итог("Сумма");

Использование метода Итог() значительно ускоряет работу кода и делает его более читаемым по сравнению с ручным перебором строк в нескольких местах программы.

Почему возникают ошибки при сложении сумм (разбор ситуации)

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

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

Сравним неправильный и правильный подходы:

ОШИБКА:


СуммаИтого = 0; // Инициализация где-то в начале процедуры
Для Каждого Документ Из СписокДокументов Цикл
    // СуммаИтого НЕ ОБНУЛЯЕТСЯ
    Для Каждого Строка Из Документ.Товары Цикл
        СуммаИтого = СуммаИтого + Строка.Сумма;
    КонецЦикла;
    // В итоге каждый следующий документ будет содержать суммы всех предыдущих
КонецЦикла;

ПРАВИЛЬНО:


Для Каждого Документ Из СписокДокументов Цикл
    СуммаПоДокументу = 0; // Сбрасываем итог для конкретного документа
    Для Каждого Строка Из Документ.Товары Цикл
        СуммаПоДокументу = СуммаПоДокументу + Строка.Сумма;
    КонецЦикла;
    // Теперь СуммаПоДокументу верна. 
    // Если нужен общий итог по всем документам, используем вторую переменную:
    ОбщийИтог = ОбщийИтог + СуммаПоДокументу; 
КонецЦикла;

Метод 3: Группировка итогов с помощью метода Свернуть()

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

Посмотрим на алгоритм:


// Создаем копию таблицы, чтобы не испортить исходные детальные данные
ТаблицаИтогов = тзДанные.Скопировать();

// Оставляем только нужные колонки: 
// 1. По которой группируем (Документ)
// 2. Которую суммируем (Сумма)
ТаблицаИтогов.Свернуть("РегистраторДокументПродажи", "Сумма");

// Теперь ТаблицаИтогов содержит уникальные документы и их общие суммы.
// Мы можем быстро найти сумму конкретного документа:
СтрокаИтога = ТаблицаИтогов.Найти(НужныйДокумент, "РегистраторДокументПродажи");
Если СтрокаИтога <> Неопределено Тогда
    Результат = СтрокаИтога.Сумма;
КонецЕсли;

Рекомендации по оптимизации

При работе с большими объемами данных в 1С важно учитывать производительность. Посмотрим на несколько советов, которые помогут вашему коду работать быстрее:

  1. Избегайте копирования таблиц в цикле: В сообщении №1 автор пытался делать тз.Скопировать(Структура) внутри цикла по каждой строке. Это очень «дорогая» операция для процессора и памяти. Лучше один раз рассчитать суммы или использовать индексирование.
  2. Индексация: Если вы часто ищете данные в ТаблицаЗначений по какому-то полю (например, по ссылке на документ), обязательно добавьте индекс: тз.Индексы.Добавить("РегистраторДокументПродажи");.
  3. Запросы: Самый быстрый способ получить сумму — это сделать это на уровне СУБД через запрос. Используйте секцию ИТОГИ или агрегатные функции СУММА() в языке запросов 1С.

Таким образом, выбор метода зависит от вашей конкретной задачи. Если нужно просто вывести итог внизу печатной формы — используйте переменную-аккумулятор. Если данные нужны для дальнейшей программной обработки — используйте колонки в ТЗ и метод Итог(). Если важна скорость на больших объемах — используйте Запросы и агрегатные функции.

← На главную