Как в отчете СКД формировать данные по реквизиту документа, а не по периоду регистра?

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

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

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

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

Первая мысль, которая возникает при попытке решить такую задачу — подменить период прямо в запросе СКД с помощью конструкции ВЫБОР КОГДА. На форуме пользователи часто предлагают убрать ограничения по датам в параметрах виртуальной таблицы ОстаткиИОбороты (сделать их пустыми) и перенести условие выборки в секцию ГДЕ основного запроса. Посмотрим на пример такого подхода:


ВЫБРАТЬ
    ТоварыНаСкладахОстаткиИОбороты.Номенклатура,
    ТоварыНаСкладахОстаткиИОбороты.НачальныйОстаток,
    ТоварыНаСкладахОстаткиИОбороты.КонечныйОстаток,
    ВЫБОР 
        КОГДА ТоварыНаСкладахОстаткиИОбороты.Регистратор ССЫЛКА Документ.ОсобыйДокумент 
        ТОГДА ВЫРАЗИТЬ(ТоварыНаСкладахОстаткиИОбороты.Регистратор КАК Документ.ОсобыйДокумент).ДатаДляРегистра 
        ИНАЧЕ ТоварыНаСкладахОстаткиИОбороты.Период 
    КОНЕЦ КАК ВычисляемыйПериод
ИЗ
    РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты(&ПустаяДата, &ПустаяДата, Авто, , ) КАК ТоварыНаСкладахОстаткиИОбороты
ГДЕ
    (ВЫБОР 
        КОГДА ТоварыНаСкладахОстаткиИОбороты.Регистратор ССЫЛКА Документ.ОсобыйДокумент 
        ТОГДА ВЫРАЗИТЬ(ТоварыНаСкладахОстаткиИОбороты.Регистратор КАК Документ.ОсобыйДокумент).ДатаДляРегистра 
        ИНАЧЕ ТоварыНаСкладахОстаткиИОбороты.Период 
    КОНЕЦ) МЕЖДУ &Дата1 И &Дата2

Почему этот вариант приведет к катастрофическим последствиям?

  1. Искажение остатков: Фундаментальная особенность платформы 1С заключается в том, что механизмы виртуальных таблиц работают строго по физическому полю Период. Если мы передаем пустые даты начала и конца в параметры таблицы ОстаткиИОбороты, система рассчитывает начальный и конечный остатки за весь период существования информационной базы. Наложение условия в секции ГДЕ отфильтрует и скроет часть итоговых строк, но сами цифры остатков на границы вашего "вычисляемого" периода пересчитаны не будут. Математика отчета полностью разрушится.
  2. Проблемы с производительностью: Отказ от передачи параметров &НачалоПериода и &КонецПериода заставляет СУБД поднимать всю историю движений по регистру. Кроме того, использование конструкции через точку для обращения к реквизиту документа вызывает неявное левое соединение с таблицей документов. На рабочих базах с большим объемом данных такой отчет будет выполняться крайне долго.

Решение 1: Архитектурно правильный подход (Best Practice)

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

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


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

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

Решение 2: Расчет остатков силами СКД (без изменения логики проведения)

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

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

Разберем по шагам, как это реализовать:

  1. В запросе СКД обращаемся к таблице Обороты (чтобы получить приходы и расходы).
  2. С помощью конструкции ВЫБОР КОГДА... вычисляем итоговую дату: подставляем либо стандартный Период, либо наш реквизит ДатаДляРегистра.
  3. Выводим данные по ресурсам (Приход, Расход).

ВЫБРАТЬ
    ТоварыНаСкладахОбороты.Номенклатура КАК Номенклатура,
    ВЫБОР 
        КОГДА ТоварыНаСкладахОбороты.Регистратор ССЫЛКА Документ.ОсобыйДокумент 
        ТОГДА ВЫРАЗИТЬ(ТоварыНаСкладахОбороты.Регистратор КАК Документ.ОсобыйДокумент).ДатаДляРегистра 
        ИНАЧЕ ТоварыНаСкладахОбороты.ПериодСекунда 
    КОНЕЦ КАК ПериодРасчета,
    ТоварыНаСкладахОбороты.КоличествоПриход КАК КоличествоПриход,
    ТоварыНаСкладахОбороты.КоличествоРасход КАК КоличествоРасход
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Обороты(, , Авто, ) КАК ТоварыНаСкладахОбороты

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

Решение 3: Использование временных таблиц

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

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

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

← На главную