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