Как в отчете СКД объединить остатки товаров и брак с детализацией по дням?

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

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

В рамках этой статьи мы подробно разберем, почему попытки решить эту задачу «в лоб» через сложные SQL-запросы приводят к зависанию системы и вылету платформы 1С, а также рассмотрим правильную архитектуру решения с использованием штатных механизмов СКД: объединения наборов данных, ролей полей и дополнения периодов.

В чем сложность задачи и почему зависает отчет?

Посмотрим на типичные ошибки, которые допускают начинающие разработчики при решении этой задачи:

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

Для создания быстрого и отказоустойчивого отчета мы должны отказаться от расчетов «нарастающего итога» внутри запроса СУБД и переложить эту задачу на встроенный математический процессор СКД.

---

Вариант 1. Объединение данных через ОБЪЕДИНИТЬ ВСЕ в одном запросе (оптимально для простых отчетов)

Если мы хотим использовать один набор данных (запрос), наиболее эффективным способом совместить данные двух регистров является использование конструкции ОБЪЕДИНИТЬ ВСЕ вместо ресурсоемких соединений.

Рассмотрим структуру такого запроса:


ВЫБРАТЬ
    ТоварыОстатки.Номенклатура КАК Номенклатура,
    ТоварыОстатки.Склад КАК Склад,
    ТоварыОстатки.ПериодДень КАК ПериодДень,
    ТоварыОстатки.КоличествоНачальныйОстаток КАК НачальныйОстаток,
    ТоварыОстатки.КоличествоПриход КАК Приход,
    ТоварыОстатки.КоличествоРасход КАК Расход,
    0 КАК Брак,
    ТоварыОстатки.КоличествоКонечныйОстаток КАК КонечныйОстаток
ПОМЕСТИТЬ ВТ_ОбщиеДанные
ИЗ
    РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, День, , ) КАК ТоварыОстатки

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    БракОбороты.Материал,
    БракОбороты.Склад,
    БракОбороты.ПериодДень,
    0,
    0,
    0,
    БракОбороты.КоличествоРасход, // Или Приход в зависимости от логики вашего регистра брака
    0
ИЗ
    РегистрНакопления.БракВПроизводстве.Обороты(&НачалоПериода, &КонецПериода, День, ) КАК БракОбороты
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_ОбщиеДанные.Номенклатура КАК Номенклатура,
    ВТ_ОбщиеДанные.Склад КАК Склад,
    ВТ_ОбщиеДанные.ПериодДень КАК ПериодДень,
    СУММА(ВТ_ОбщиеДанные.НачальныйОстаток) КАК НачальныйОстаток,
    СУММА(ВТ_ОбщиеДанные.Приход) КАК Приход,
    СУММА(ВТ_ОбщиеДанные.Расход) КАК Расход,
    СУММА(ВТ_ОбщиеДанные.Брак) КАК Брак,
    СУММА(ВТ_ОбщиеДанные.КонечныйОстаток) КАК КонечныйОстаток
ИЗ
    ВТ_ОбщиеДанные КАК ВТ_ОбщиеДанные
СГРУППИРОВАТЬ ПО
    ВТ_ОбщиеДанные.Номенклатура,
    ВТ_ОбщиеДанные.Склад,
    ВТ_ОбщиеДанные.ПериодДень

Преимущества этого подхода: Мы получаем плоскую выборку без использования тяжелых связей JOIN. СУБД выполняет этот запрос практически мгновенно.

---

Вариант 2. Использование двух наборов данных и связей в СКД (рекомендуемый метод)

Наиболее профессиональный подход — разделить получение данных на два независимых набора данных (два запроса) прямо в схеме компоновки данных, а затем связать их средствами самой платформы.

Выясним, как это сделать пошагово:

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

---

Настройка ролей полей для автоматического расчета остатков по дням

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

Разберем, как настроить вкладку «Наборы данных» в конструкторе СКД:

  1. Найдем поле ПериодДень. В колонке «Роль» нажмем кнопку изменения и настроим параметры:
    • Поставим флажок «Период» и укажем его порядковый номер (например, 1).
    • Укажем тип дополнительного периода как День.
  2. Найдем поле КоличествоНачальныйОстаток. В роли поля настроим параметры:
    • Поставим флажок «Остатки».
    • Установим тип остатка: «Начальный».
    • Имя группы укажем одинаковое для обоих остатков, например: Количество.
    • Поле связи выберем: ПериодДень.
  3. Найдем поле КоличествоКонечныйОстаток и настроим роль аналогично:
    • Флажок «Остатки».
    • Тип остатка: «Конечный».
    • Имя группы: Количество (такое же, как у начального).
    • Поле связи: ПериодДень.
  4. Для полей измерений Номенклатура и Склад обязательно укажем роль «Измерение».

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

---

Включение функции «Дополнение периодов» (Остатки на каждый день без движений)

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

Выполним следующие настройки:

  1. Перейдем на вкладку «Настройки» в СКД.
  2. В структуре отчета создадим группировку по полю ПериодДень (или добавим его внутрь группировки по Номенклатуре).
  3. Выделим строку группировки ПериодДень и внизу в окне свойств найдем группу параметров «Системные поля / Периоды».
  4. Установим параметр «Тип дополнения» в значение День.
  5. В параметрах «Начало периода дополнения» выберем значение &НачалоПериода.
  6. В параметрах «Конец периода дополнения» выберем значение &КонецПериода.

После выполнения этих настроек СКД автоматически сгенерирует строки для каждого календарного дня в выбранном интервале дат. Если в какой-то из дней движений по складу и браку не было, СКД автоматически перенесет конечный остаток предыдущего дня в качестве начального и конечного остатка текущего дня, а показатели Приход, Расход и Брак заполнит нулями. Наш отчет станет точным, красивым и будет работать молниеносно!

← На главную