Как сложить значения из документов и вывести Итого в отчете 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

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

Разбор типичных ошибок в исходном коде

Для начала давайте детально проанализируем код, который часто пытаются использовать новички, и выясним, почему он не работает или работает неэффективно:


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

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

В этом фрагменте допущено сразу несколько критических ошибок:

  1. Обход объектов вместо использования запроса. Конструкция Документы.РасходнаяНакладная.Выбрать() заставляет платформу считывать из базы данных абсолютно все реквизиты каждого документа (включая табличные части, комментарии, служебные свойства). Если в базе данных накопятся тысячи накладных, этот код заблокирует работу системы и вызовет критическую нехватку оперативной памяти. В 1С действует жесткое правило: любые отчеты должны строиться исключительно на основе запросов.
  2. Некорректный синтаксис запроса. Строка Запрос.Текст = ("Выбрать") является синтаксически неверной. Платформа 1С выдаст ошибку на этапе выполнения, так как оператор ВЫБРАТЬ (или SELECT) требует указания конкретных полей и источников данных (таблиц).
  3. Попытка получить итоги из пустого запроса. Цикл обхода по переменной Итог не имеет смысла, поскольку сам запрос не содержит ни группировок, ни агрегатных функций, способных посчитать сумму.
  4. Проблема с выводом области макета. Вывод области «Итого» в цикле приведет к тому, что строка итогов будет дублироваться многократно, вместо того чтобы появиться один раз в самом конце отчета.

Теперь рассмотрим правильные варианты реализации этой задачи.

Решение №1. Быстрый ручной подсчет в цикле (для простых отчетов)

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

Давайте реализуем этот алгоритм шаг за шагом:


&НаСервереБезКонтекста
Процедура ЗаполнитьТабДок(ТабДок)
    ТабДок.Очистить();
    
    // Получаем макет отчета
    Макет = Отчеты.ОтчетПоЗаказам.ПолучитьМакет("Отчёт");
    ОблШапка   = Макет.ПолучитьОбласть("Шапка");
    ОблЭлемент = Макет.ПолучитьОбласть("Элемент");
    ОблИтого   = Макет.ПолучитьОбласть("Итого");
    
    // Выводим шапку отчета
    ОблШапка.Параметры.ДатаОтчёта = Формат(ТекущаяДата(), "ДЛФ=DD");
    ТабДок.Вывести(ОблШапка);
    
    // Инициализируем переменную для накопления суммы заявок
    ВсегоЗаявок = 0;
    
    // Выбираем документы
    Выборка = Документы.РасходнаяНакладная.Выбрать();
    Пока Выборка.Следующий() Цикл
        // Заполняем параметры области макета данными текущего документа
        ОблЭлемент.Параметры.Заполнить(Выборка);
        ТабДок.Вывести(ОблЭлемент);
        
        // Накапливаем сумму. Предположим, что количество заявок хранится в реквизите "КоличествоЗаявок"
        ВсегоЗаявок = ВсегоЗаявок + Выборка.КоличествоЗаявок;
    КонецЦикла;
    
    // Заполняем параметр в области "Итого" накопленным значением
    // Обратите внимание: вывод происходит строго ПОСЛЕ окончания цикла!
    ОблИтого.Параметры.ИтогоЗаявок = ВсегоЗаявок;
    ТабДок.Вывести(ОблИтого);
КонецПроцедуры

Преимущество метода: предельная простота реализации и отсутствие необходимости разбираться в синтаксисе запросов.

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

Решение №2. Использование правильного SQL-запроса с расчетом итогов

Это профессиональный подход. Мы переносим всю вычислительную нагрузку на СУБД. База данных сама отфильтрует, сгруппирует и сложит нужные числа, вернув серверу 1С уже готовый сжатый результат.

Для создания запроса воспользуемся встроенным инструментом — Конструктором запроса. Чтобы запустить его, выполните следующие действия:

  1. В модуле конфигуратора напишите заготовку кода: Запрос.Текст = "";.
  2. Поставьте курсор внутрь кавычек, нажмите правую кнопку мыши и выберите пункт «Конструктор запроса...».
  3. В открывшемся окне перетащите таблицу документа РасходнаяНакладная в поле «Таблицы».
  4. Выберите нужные поля (например, Водитель и КоличествоЗаявок).
  5. Перейдите на вкладку «Итоги», добавьте поле КоличествоЗаявок в список итоговых полей и укажите функцию СУММА.
  6. Нажмите «ОК». Платформа автоматически сгенерирует правильный текст запроса.

Давайте посмотрим на пример программного кода, использующего такой запрос:


&НаСервереБезКонтекста
Процедура ЗаполнитьТабДок(ТабДок)
    ТабДок.Очистить();
    
    Макет = Отчеты.ОтчетПоЗаказам.ПолучитьМакет("Отчёт");
    ОблШапка   = Макет.ПолучитьОбласть("Шапка");
    ОблЭлемент = Макет.ПолучитьОбласть("Элемент");
    ОблИтого   = Макет.ПолучитьОбласть("Итого");
    
    ОблШапка.Параметры.ДатаОтчёта = Формат(ТекущаяДата(), "ДЛФ=DD");
    ТабДок.Вывести(ОблШапка);
    
    // Создаем и настраиваем запрос
    Запрос = Новый Запрос;
    Запрос.Text = 
        "ВЫБРАТЬ
        |	РасходнаяНакладная.Водитель КАК Водитель,
        |	РасходнаяНакладная.КоличествоЗаявок КАК КоличествоЗаявок
        |ИЗ
        |	Документ.РасходнаяНакладная КАК РасходнаяНакладная
        |ИТОГИ
        |	СУММА(КоличествоЗаявок)
        |ПО
        |	ОБЩИЕ";
    
    РезультатЗапроса = Запрос.Выполнить();
    
    // Сначала получаем общие итоги по всему запросу
    ВыборкаОбщиеИтоги = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
    ОбщаяСуммаЗаявок = 0;
    
    Если ВыборкаОбщиеИтоги.Следующий() Тогда
        // Извлекаем заранее посчитанную СУБД сумму
        ОбщаяСуммаЗаявок = ВыборкаОбщиеИтоги.КоличествоЗаявок;
    КонецЕсли;
    
    // Теперь выбираем детальные записи для вывода строк отчета
    ВыборкаДетали = РезультатЗапроса.Выбрать();
    Пока ВыборкаДетали.Следующий() Цикл
        ОблЭлемент.Параметры.Заполнить(ВыборкаДетали);
        ТабДок.Вывести(ОблЭлемент);
    КонецЦикла;
    
    // Выводим строку Итого с готовой суммой
    ОблИтого.Параметры.ИтогоЗаявок = ОбщаяСуммаЗаявок;
    ТабДок.Вывести(ОблИтого);
КонецПроцедуры

Преимущество метода: высокая скорость работы, минимальное использование ресурсов, соответствие стандартам разработки фирмы «1С».

Решение №3. Использование метода выгрузки результата в Таблицу значений

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

Давайте проанализируем этот пример:


&НаСервереБезКонтекста
Процедура ЗаполнитьТабДок(ТабДок)
    ТабДок.Очистить();
    
    Макет = Отчеты.ОтчетПоЗаказам.ПолучитьМакет("Отчёт");
    ОблШапка   = Макет.ПолучитьОбласть("Шапка");
    ОблЭлемент = Макет.ПолучитьОбласть("Элемент");
    ОблИтого   = Макет.ПолучитьОбласть("Итого");
    
    ОблШапка.Параметры.ДатаОтчёта = Формат(ТекущаяДата(), "ДЛФ=DD");
    ТабДок.Вывести(ОблШапка);
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	РасходнаяНакладная.Водитель КАК Водитель,
        |	РасходнаяНакладная.КоличествоЗаявок КАК КоличествоЗаявок
        |ИЗ
        |	Документ.РасходнаяНакладная КАК РасходнаяНакладная";
    
    // Выгружаем результат выполнения запроса в Таблицу Значений
    ТаблицаРезультата = Запрос.Выполнить().Выгрузить();
    
    // Выводим строки отчета путем обхода таблицы значений
    Для Каждого СтрокаДанных Из ТаблицаРезультата Цикл
        ОблЭлемент.Параметры.Заполнить(СтрокаДанных);
        ТабДок.Вывести(ОблЭлемент);
    КонецЦикла;
    
    // Получаем сумму по колонке в одно действие с помощью встроенного метода Итог()
    ОблИтого.Параметры.ИтогоЗаявок = ТаблицаРезультата.Итог("КоличествоЗаявок");
    ТабДок.Вывести(ОблИтого);
КонецПроцедуры

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

Решение №4. Создание отчета на Системе компоновки данных (СКД)

В современной практике разработки на платформе «1С:Предприятие 8.3» ручное рисование макетов и написание кода вывода табличных документов используется только в специфических задачах (например, при формировании печатных форм документов). Для построения отчетов применяется Система компоновки данных (СКД).

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

  1. В дереве конфигурации добавьте новый объект типа Отчет.
  2. Нажмите кнопку «Открыть схему компоновки данных» и оставьте имя по умолчанию.
  3. На вкладке «Наборы данных» добавьте Набор данных - запрос.
  4. С помощью Конструктора запроса составьте простой запрос к документам:
    
    ВЫБРАТЬ
    	РасходнаяНакладная.Водитель КАК Водитель,
    	РасходнаяНакладная.КоличествоЗаявок КАК КоличествоЗаявок
    ИЗ
    	Документ.РасходнаяНакладная КАК РасходнаяНакладная
    
  5. Перейдите на вкладку «Ресурсы». Перенесите поле КоличествоЗаявок в правое окно. В колонке «Выражение» автоматически установится функция СУММА(КоличествоЗаявок). Это укажет системе на необходимость расчета итогов по этому полю.
  6. Перейдите на вкладку «Настройки». Добавьте в структуру отчета новую группировку (например, по полю Водитель) или просто добавьте структуру «Детальные записи» для вывода плоского списка.

Запустите отчет в режиме «1С:Предприятие». Система автоматически сгенерирует форму, выведет данные в красивую таблицу и самостоятельно добавит итоговую строку со всеми рассчитанными суммами внизу страницы. При этом вам не пришлось писать ни одной строчки программного кода!

Используйте эти подходы в своей практике, избегайте прямых выборок объектов в циклах и доверяйте сложные расчеты механизму запросов платформы 1С.

← На главную