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

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

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

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

Выясним причину: почему ломается расчет остатков при соединении таблиц в запросе?

Рассмотрим типичную ошибку разработчика, представленную в Сообщении 5 темы. Автор создает две временные таблицы: ТМЦ (на основе регистра товаров) и Касса (на основе регистра денежных средств), а затем соединяет их через ПОЛНОЕ СОЕДИНЕНИЕ по полю Период:


ВЫБРАТЬ
    Касса.СуммаНачальныйОстаток,
    Касса.СуммаКонечныйОстаток,
    ОСт.КоличествоНачальныйОстаток,
    ОСт.КоличествоКонечныйОстаток,
    ОСт.Период
ИЗ
    Касса КАК Касса
    ПОЛНОЕ СОЕДИНЕНИЕ ОСт КАК ОСт ПО Касса.Период = ОСт.Период

На первый взгляд, логика кажется верной. Однако при таком подходе встроенный механизм расчета остатков СКД полностью ломается. Почему это происходит?

  1. Потеря связи с измерениями: Чтобы СКД могла правильно «протягивать» остатки на те дни, когда движений не было, ей необходима жесткая связь между полями остатков, измерениями (номенклатура, склад, касса) и периодом. При полном соединении эта логическая связь разрушается.
  2. Проблема значений NULL: Из-за специфики полного соединения в поле Период неизбежно появляются значения NULL (например, если в конкретный день были движения по кассе, но не было по товарам). Появление пустых значений в ключевом поле периода блокирует механизм дополнения дат в СКД.
  3. Невозможность сопоставления ролей: СКД не может определить, для какого именно ресурса и какого измерения нужно рассчитать промежуточный итог на пустую дату.

Также категорически не рекомендуется использовать ручной расчет остатков путем левого соединения таблицы дат с таблицей движений по условию «меньше или равно» (как показано в Сообщении 0):


ПОЛНОЕ СОЕДИНЕНИЕ РегистрНакопления.Касса.ОстаткиИОбороты(...) КАК Движения
    ПО (Движения.Период <= ДНИ.ДатаКалендаря)

Такой запрос имеет квадратичную сложность. На реальной базе данных при увеличении периода отчета или объема движений СУБД просто перестанет отвечать из-за колоссальной нагрузки.

Разберем по шагам правильные и проверенные методы решения этой задачи с использованием штатных механизмов СКД.

Решение 1. Объединение запросов (UNION ALL) и использование поля «Раздел»

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

Шаг 1. Пишем правильный текст запроса для схемы компоновки данных

Мы используем оператор ОБЪЕДИНИТЬ ВСЕ. В каждом подзапросе мы создаем искусственное текстовое поле Раздел, которое выполняет роль разделителя учета. Поля, уникальные для других регистров, заполняются пустыми ссылками или нулями:


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

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

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

Шаг 2. Настраиваем роли полей в конструкторе СКД

Для того чтобы СКД понимала, как рассчитывать остатки по каждому направлению отдельно, нам необходимо правильно настроить роли на вкладке «Наборы данных»:

Шаг 3. Настраиваем дополнение периодов в настройках отчета

Чтобы пустые дни действительно выводились в отчет, выполним следующие настройки на вкладке «Настройки»:

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

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

Решение 2. Использование нескольких наборов данных и связей в СКД

Если объединение запросов через ОБЪЕДИНИТЬ ВСЕ кажется вам слишком громоздким из-за большого количества уникальных полей в регистрах, можно воспользоваться встроенным механизмом связей наборов данных СКД.

Проанализируем этот подход. Вместо одного общего запроса мы создаем три независимых набора данных:

  1. НаборДанных_Календарь (Источник): Запрос, который возвращает просто список дат за период отчета.
  2. НаборДанных_Товары (Приемник 1): Запрос к виртуальной таблице РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты с периодичностью День.
  3. НаборДанных_Касса (Приемник 2): Запрос к виртуальной таблице РегистрНакопления.Касса.ОстаткиИОбороты с периодичностью День.

Переходим на вкладку «Связи наборов данных» и настраиваем две связи:

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

Важные практические рекомендации

При работе с остатками на каждый день всегда помните о следующих правилах:

Применяя данные методики, мы получаем гибкие, быстродействующие и, главное, корректно работающие отчеты по ежедневным остаткам из любых регистров 1С.

← На главную