Работа с виртуальными таблицами остатков и оборотов в Системе компоновки данных (СКД) часто преподносит сюрпризы начинающим разработчикам. Особенно остро проблемы проявляются при попытке пересчитать показатели по курсу валюты на лету или при детальной группировке по регистраторам. В этой статье мы подробно разберем, почему искажаются кастомные остатки, откуда берется пустая строка в отчете, в чем заключается смертельная опасность популярных форумных решений и как сделать все архитектурно правильно.
Рассмотрим классическую ситуацию: у нас есть стандартные поля остатков из виртуальной таблицы регистра накопления, а также новые поля, в которых мы пересчитываем стоимость по курсу валюты. Нам кажется логичным написать простую формулу пересчета стоимости в выражении поля. Однако при выводе отчета мы замечаем странную картину: стандартные остатки выводятся верно (например, равны нулю), а пересчитанные по формуле поля показывают огромные, явно завышенные суммы.
Выясним причину такого поведения компоновщика. Дело в том, что СКД — это интеллектуальный механизм, который по-разному рассчитывает итоги для обычных ресурсов и полей остатков:
СУММА(ВычисляемоеПоле).Чтобы решить эту проблему, перейдем на вкладку «Набор данных» в СКД. Найдем наши вычисляемые валютные поля остатков и настроим для них роли так же, как это сделано для стандартных полей:
НачальныйОстаток.КонечныйОстаток.ВалютнаяСтоимость) для обеих ролей. Это укажет СКД, что данные поля являются связанной парой остатков.При выводе детальных записей с группировкой по регистратору пользователи часто видят в самом начале отчета пустую строку. В ней нет названия документа, но при этом могут быть заполнены начальные остатки.
Разберем физику этого процесса. Виртуальная таблица ОстаткиИОбороты при получении данных с детализацией по регистратору обязана вернуть так называемые «граничные строки». У этих строк поле Регистратор содержит значение НЕОПРЕДЕЛЕНО (или NULL). Именно через эти технические строки СКД получает информацию о начальном остатке на самую раннюю границу отчета и конечном остатке на самую позднюю границу.
Эта строка жизненно необходима СКД для того, чтобы корректно собрать общие итоги по отчету и вывести начальный остаток по группировкам выше регистратора.
На форумах часто советуют «простое и быстрое» решение проблемы пустой строки: добавить в условие связи или секцию ГДЕ запроса ограничение вида ГДЕ Таблица.Регистратор <> Значение(Документ.ИмяДокумента.ПустаяСсылка) или ГДЕ Таблица.Регистратор НЕ ЕСТЬ NULL.
Внимание: это опаснейший «костыль» и мина замедленного действия! Рассмотрим, к каким катастрофическим последствиям в реальном учете это приведет:
Разберем два методологически верных способа убрать пустую строку из отчета, не ломая при этом механизм расчета итогов СКД.
Способ 1. Использование свойств ролей полей (Рекомендуемый)
Мы можем преобразовать техническое значение НЕОПРЕДЕЛЕНО в полноценный NULL прямо в тексте запроса, а затем дать команду СКД игнорировать его при выводе.
Напишем в запросе следующее выражение для поля регистратора:
ВЫБОР
КОГДА ОстаткиПоКурсу.Регистратор = НЕОПРЕДЕЛЕНО ТОГДА NULL
ИНАЧЕ ОстаткиПоКурсу.Регистратор
КОНЕЦ КАК Регистратор
После этого на вкладке «Набор данных» откроем роли для поля Регистратор и установим флаг «Игнорировать значения NULL». В этом случае СКД использует данную строку для внутренних расчетов итогов, но визуально в отчет ее выводить не будет.
Способ 2. Отбор на уровне группировки компоновки
Этот способ не требует изменения текста запроса. Мы оставляем выборку как есть, но скрываем пустую строку средствами настроек вывода:
Регистратор Заполнен (или Регистратор <> Неопределено).В результате на уровне итогов данные будут учтены, но сама строка с пустым регистратором скроется из детальных записей.
Способ 3. Творческий подход с представлением
Иногда полезно не скрывать строку, а сделать ее понятной для пользователя. Мы можем задать выражение представления для поля Регистратор. В свойствах поля укажем выражение:
ВЫБОР
КОГДА Регистратор ЕСТЬ NULL ИЛИ Регистратор = НЕОПРЕДЕЛЕНО
ТОГДА "Начальный остаток на " + Формат(НачалоПериода, "ДФ=dd.MM.yyyy")
ИНАЧЕ Представление(Регистратор)
КОНЕЦ
Проанализируем формулу пересчета остатков, которую часто пытаются применить разработчики:
СтоимостьНачальныйОстаток * (КурсУчета / КурсПоДатам.Курс)
В этой формуле кроется серьезная математическая проблема. В технической строке начального остатка (где нет регистратора и, соответственно, нет даты документа) левое соединение по дате вернет NULL для поля КурсПоДатам.Курс. Наш пересчитанный начальный остаток в этой строке станет равен нулю или запишется некорректно.
Как делать правильно в валютных отчетах?
Вариант А. Динамический расчет остатков на основе оборотов. По курсу на дату документа нужно пересчитывать только обороты (приход и расход). Начальные и конечные остатки в валюте отчета должны рассчитываться СКД динамически как накопительный итог (для этого у полей остатков настраиваются соответствующие роли связи с оборотами).
Вариант Б. Жесткий пересчет по курсу на границы периода. Если методология учета требует пересчитать остатки напрямую:
НачальныйОстаток должно умножаться на курс на Дату начала периода отчета.КонечныйОстаток должно умножаться на курс на Дату окончания периода отчета.Соединение с регистром курсов валют для остатков в таком случае должно идти не по дате документа, а по параметрам начала и конца периода отчета.
Чтобы СКД безошибочно сопоставляла начальные и конечные остатки при детализации до регистратора, необходимо соблюдать строгие правила настройки периодов в наборе данных:
ПериодСекунда и Регистратор. Набор данных должен иметь периодичность Авто.ПериодСекунда — Роль: Период, Номер: 1 (родительский, более общий период).Регистратор — Роль: Период, Номер: 2 (дочерний, уточняющий период).Соблюдение этих правил гарантирует, что ваши отчеты в СКД будут работать быстро, а главное — показывать абсолютно достоверные данные по остаткам в любых разрезах.