Почему сбиваются начальные и конечные остатки в СКД при отборе по регистратору и как это исправить

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

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

Почему стандартный отбор «ломает» остатки?

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

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

  1. Получает все движения и рассчитывает остатки по всем правилам.
  2. Применяет условие ГДЕ ко всему результату запроса.

Проанализируем последствия: условие ГДЕ Регистратор.Контрагент = &НужныйКонтрагент просто отсекает все строки, где условие не выполняется. Но для расчета корректного остатка на начало октября системе нужны все предыдущие движения, вне зависимости от того, какой там был контрагент. Отсекая «чужих» контрагентов, мы лишаем алгоритм возможности вычислить накопленный итог, и получаем остаток, сформированный только документами текущего отбора, что в корне неверно.

Способ 1. Настройка ролей полей и периодов

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

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

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

Способ 2. Использование двух наборов данных (Связи наборов)

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

Шаг 1. Создаем первый набор данных (Остатки). В нем мы обращаемся к ВТ ОстаткиИОбороты, выбираем измерения (Номенклатура) и ресурсы (Остатки). Мы не выбираем здесь поле Регистратор. В этом наборе остатки всегда будут правильными, так как на них не будут влиять фильтры по документам.

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

Шаг 3. Настраиваем связи. На закладке «Связи наборов данных» связываем Набор 1 и Набор 2 по измерению Номенклатура.


Источник связи: НаборОстатки
Приемник связи: НаборДвижения
Условие связи: Номенклатура = Номенклатура
Параметры: НачалоПериода = &НачалоПериода, КонецПериода = &КонецПериода

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

Способ 3. Виртуальное исключение строк через условное оформление

Иногда задача стоит так: «Покажите мне движения только этого контрагента, но остатки должны сходиться с общими данными». Если мы не хотим усложнять запрос связями, можно прибегнуть к хитрости с визуализацией. Посмотрим на этот алгоритм:

  1. Убираем жесткий отбор по полю Регистратор.Контрагент из настроек «Отбор».
  2. Вместо этого добавляем параметр отчета ВыбранныйКонтрагент.
  3. Переходим на закладку «Условное оформление».
  4. Создаем новое оформление: ВысотаСтроки = 0 (или Видимость = Ложь).
  5. В условии оформления указываем: Регистратор.Контрагент Не Равно &ВыбранныйКонтрагент.

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

Способ 4. Правильная настройка дополнения периодов

Если проблема в том, что «начальный остаток должен быть 0, а он пустой» или документы попадают не в те группировки, проанализируем настройки параметров виртуальной таблицы. В параметрах ВТ ОстаткиИОбороты есть возможность указать метод дополнения периодов.

Использование метода ДвиженияИГраницыПериода заставляет систему генерировать записи даже на те моменты времени, когда движений не было. Это критично для того, чтобы зафиксировать «точку отсчета» (начало периода) с нулевым остатком. В СКД для этого также важно в роли поля периода указать «Обязательное», чтобы компоновщик не выкинул эту запись при оптимизации запроса.

Способ 5. Вычисление остатка через функции ресурсов

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

Для ресурса КонечныйОстаток можно прописать выражение:


ВычислитьВыражение("Сумма(КоличествоОборот)", "ПериодСекунда", "Группировка", "Первая", "Текущая") 
+ ЗначениеПараметра("НачальныйОстатокНаДату")

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

Резюме

Подведем итог. Если вы столкнулись с тем, что отбор по Регистратор.Контрагент ломает отчет:

Использование этих методов позволит вам создавать надежные отчеты, которые не пугают бухгалтерию внезапно «исчезающими» остатками при попытке детализировать данные по конкретному партнеру.

← На главную