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