Как правильно рассчитать итоги за неделю и вывести их в табличный документ 1С

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

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

Анализ логической ошибки в программном коде

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


Пока ТекДата <= НачалоДня(КонецПериода) Цикл
    Если ТекДата = СтрНоменклатура.ДатаОтгрузки Тогда
        СтрокаПериода.Параметры.Количество = СтрНоменклатура.Количество;
    Иначе
        СтрокаПериода.Параметры.Количество = 0;
    КонецЕсли;
    
    ТабДок.Присоединить(СтрокаПериода);
    
    СуммаНедели = 0; // ОШИБКА: Обнуление происходит на каждом шаге цикла (каждый день)
    СуммаНедели = СуммаНедели + СтрНоменклатура.Количество;
    
    Если ДеньНедели(ТекДата) = 7 Тогда 
        ОбластьИтог.Параметры.ИтогЗаНеделю = СуммаНедели;     
        ТабДок.Присоединить(ОбластьИтог);
        СуммаНедели = 0;
    КонецЕсли;
    
    ТекДата = ТекДата + 60*60*24;
КонецЦикла;

Выясним причину, по которой данный код работает некорректно. На каждой итерации цикла (каждый день) программа заново устанавливает СуммаНедели в значение 0, а затем прибавляет к нему количество текущего дня. В результате, когда наступает воскресенье (ДеньНедели(ТекДата) = 7), в переменной СуммаНедели оказывается не сумма за 7 дней, а только значение за последний день недели. Важно помнить: инициализация переменной итога должна происходить за пределами цикла накопления или строго после вывода результата за предыдущий период.

Исправление алгоритма ручного накопления итогов

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

  1. Обнуляем переменную СуммаНедели перед началом цикла по датам.
  2. Внутри цикла ежедневно прибавляем текущее значение к накопленной сумме.
  3. При достижении контрольной точки (воскресенье или конец периода) выводим накопленное значение в макет.
  4. Только после вывода области в табличный документ обнуляем переменную для расчета следующей недели.

Посмотрим на пример правильной реализации:


СуммаНедели = 0; // Инициализация перед циклом
ТекДата = НачалоДня(НачалоПериода);

Пока ТекДата <= НачалоДня(КонецПериода) Цикл
    // Логика определения количества за день
    КоличествоЗаДень = 0;
    Если ТекДата = СтрНоменклатура.ДатаОтгрузки Тогда
        КоличествоЗаДень = СтрНоменклатура.Количество;
    КонецЕсли;
    
    СтрокаПериода.Параметры.Количество = КоличествоЗаДень;
    ТабДок.Присоединить(СтрокаПериода);
    
    // Накопление итога
    СуммаНедели = СуммаНедели + КоличествоЗаДень;
    
    // Проверка на конец недели или конец всего периода отчета
    Если ДеньНедели(ТекДата) = 7 Или ТекДата = НачалоДня(КонецПериода) Тогда 
        ОбластьИтог.Параметры.ИтогЗаНеделю = СуммаНедели;     
        ТабДок.Присоединить(ОбластьИтог);
        
        // Обнуление ТОЛЬКО после вывода итога
        СуммаНедели = 0; 
    КонецЕсли;
    
    ТекДата = ТекДата + 86400; // Добавляем сутки
КонецЦикла;

Использование Системы компоновки данных (СКД)

Хотя ручной вывод через ТабДок.Присоединить() понятен начинающим разработчикам, он крайне неэффективен при работе с большими объемами данных и сложными группировками. В современной практике 1С для таких задач следует использовать СКД. Она автоматически берет на себя расчет итогов, дополнение периодов (вывод нулей в пустые дни) и формирование структуры колонок.

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

  1. Создание запроса: В схеме компоновки данных создаем запрос к табличной части или регистру. Обязательно добавляем поле периода, приведенное к началу недели с помощью функции НАЧАЛОПЕРИОДА(ДатаОтгрузки, НЕДЕЛЯ).
  2. Настройка ресурсов: Добавляем поле Количество в список ресурсов с функцией СУММА(Количество).
  3. Настройка структуры: В закладке "Настройки" создаем структуру типа "Таблица".
    • В Строки добавляем группировки: Грузополучатель, Заказ, Номенклатура.
    • В Колонки добавляем группировку по полю ДатаОтгрузки (с типом дополнения "День", чтобы видеть пустые даты).
    • Выше или ниже группировки по дням в колонках добавляем группировку по полю НеделяОтгрузки (созданному в запросе).

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

Оптимизация запросов и производительности

В исходном коде автора часто встречается метод ТабДанных.Скопировать() и Свернуть() внутри вложенных циклов. Это является "антипаттерном" программирования в 1С. Каждый раз, когда вы вызываете Свернуть() для большой таблицы в цикле, вы заставляете сервер выполнять колоссальный объем лишней работы.

Проанализируем ситуацию: если у вас 100 грузополучателей, по каждому 10 заказов, а в каждом заказе 10 товаров — внутренний код выполнится 10 000 раз. Копирование таблицы из 1000 строк 10 000 раз приведет к зависанию отчета.

Рекомендация: Используйте возможности языка запросов. Группируйте данные прямо в запросе или используйте обход результата запроса по группировкам:


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

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

Работа с "пустыми" периодами

Часто заказчики просят выводить в отчете все дни месяца, даже если в эти дни не было отгрузок. Если просто делать запрос к документам, пустые дни не попадут в выборку. Чтобы решить эту проблему в ручном коде, мы используем цикл Пока ТекДата <= КонецПериода, как это сделал автор. Однако, чтобы найти данные для конкретной даты, лучше заранее подготовить Соответствие или Индексированную таблицу значений, где ключом будет дата, а значением — структура с данными.

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

Заключение

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

← На главную