При работе с периодическими регистрами сведений в 1С разработчики часто сталкиваются с ситуацией, когда при получении данных через виртуальную таблицу СрезПоследних на определенную дату в результат запроса попадают записи, датированные более ранним числом. Начинающим разработчикам это кажется ошибкой работы платформы, однако такое поведение абсолютно логично и заложено в саму суть механизмов работы с регистрами. Разберем по шагам, почему так происходит, как правильно составить запрос и почему архитектурный выбор регистра сведений для хранения остатков изначально является ошибочным.
Для начала проанализируем поведение виртуальной таблицы СрезПоследних. Главная задача этой таблицы — вернуть последнее актуальное состояние (значение ресурсов) для каждого уникального набора измерений на указанную в параметрах дату (включительно).
Представим структуру регистра сведений из примера автора темы:
Организация, Банк, ВидСчётаСуммаПредположим, что в базе данных у нас хранятся следующие физические записи по организации "Наша Фирма":
После этих дат никаких новых движений по регистру не производилось. Теперь мы хотим получить остатки по счетам на дату 01.03.2019. Мы передаем эту дату в параметры виртуальной таблицы:
РегистрСведений.ОстаткиПоСчетам.СрезПоследних(&ДатаКонца, Организация = &Организация)
Давайте посмотрим, что делает платформа 1С в этот момент для каждого уникального набора измерений:
Таким образом, в результат среза на 01.03.2019 попадают данные от 25.02.2019 и 27.02.2019. И это абсолютно правильное поведение системы: она показывает актуальные остатки на указанную вами дату.
Если логика работы среза верна, то почему автор темы видит в отчете строки, разбитые по "чужим" датам? Давайте проанализируем исходный запрос:
ВЫБРАТЬ
ОстаткиПоСчетамСрезПоследних.Период КАК Период,
ОстаткиПоСчетамСрезПоследних.Организация КАК Организация,
СУММА(ОстаткиПоСчетамСрезПоследних.Сумма) КАК Сумма
ИЗ
РегистрСведений.ОстаткиПоСчетам.СрезПоследних(&ДатаКонца, Организация = &Организация) КАК ОстаткиПоСчетамСрезПоследних
СГРУППИРОВАТЬ ПО
ОстаткиПоСчетамСрезПоследних.Период,
ОстаткиПоСчетамСрезПоследних.Организация
Основная ошибка здесь кроется в выборе поля Период в секции ВЫБРАТЬ и последующей группировке по нему. В виртуальной таблице СрезПоследних поле Период содержит фактическую дату совершения записи, а вовсе не ту дату, которую вы передали в параметр &ДатаКонца.
Поскольку фактические даты записей у разных банков отличаются (25.02 и 27.02), группировка по полю Период разделяет наш результат на несколько строк. В итоге мы получаем отчет вида:
Для конечного пользователя или разработчика, не знакомого с деталями работы платформы, это выглядит так, будто в запрос на 01.03.2019 "просочились" данные других дней. На самом же деле это срез на 01.03.2019, просто детализированный по фактическим датам изменения данных.
Если наша задача — получить консолидированную сумму остатка по организации на конкретную дату &ДатаКонца, то поле Период (как реквизит таблицы) нам вообще не нужно в выборке. Мы должны полностью исключить его из полей запроса и группировки. Если же нам обязательно нужно вывести в отчете дату, на которую получены остатки, мы можем просто вернуть значение самого параметра &ДатаКонца.
Рассмотрим правильный вариант написания запроса:
ВЫБРАТЬ
&ДатаКонца КАК Период,
ОстаткиПоСчетамСрезПоследних.Организация КАК Организация,
СУММА(ОстаткиПоСчетамСрезПоследних.Сумма) КАК Сумма
ИЗ
РегистрСведений.ОстаткиПоСчетам.СрезПоследних(&ДатаКонца, Организация = &Организация) КАК ОстаткиПоСчетамСрезПоследних
СГРУППИРОВАТЬ ПО
ОстаткиПоСчетамСрезПоследних.Организация
При таком подходе запрос вернет одну красивую строку с общим остатком на указанную дату:
Участники обсуждения на форуме не зря обратили внимание на само название регистра: ОстаткиПоСчетам, реализованного как Регистр Сведений (РС). Использование РС для хранения остатков и балансовых показателей — это классический антипаттерн разработки в среде 1С:Предприятие.
Давайте разберем подробнее, почему для этих целей необходимо использовать исключительно Регистры Накопления с видом "Остатки":
Часто перед разработчиком встает смежная и более сложная задача: получить срез последних не на одну конкретную дату, а построить таблицу остатков на каждый день выбранного периода (или на даты документов из списка).
Поскольку в параметры виртуальной таблицы СрезПоследних нельзя напрямую передать массив дат или связать параметры со строками другой таблицы, стандартное соединение в запросе "в лоб" работать не будет (оно вернет некорректные данные или приведет к сильному падению скорости работы).
Давайте разберем по шагам классический и наиболее производительный алгоритм решения этой задачи через пакетный запрос:
Шаг 1. Подготовка сетки дат. Нам необходимо получить временную таблицу, содержащую уникальный список дат, на которые мы хотим рассчитать срез последних.
// Предположим, ВТ_Даты уже создана и содержит колонку "Дата"
Шаг 2. Получение максимальных дат движений. Мы связываем нашу таблицу дат с физической таблицей регистра сведений по условию "период регистра меньше или равен дате из нашей сетки". Сгруппировав результат, мы находим максимальный физический период записи для каждого измерения.
ВЫБРАТЬ
СеткаДат.Дата КАК ПериодСреза,
РегистрФизический.Организация КАК Организация,
МАКСИМУМ(РегистрФизический.Период) КАК МаксПериод
ПОМЕСТИТЬ ВТ_МаксимальныеДаты
ИЗ
ВТ_Даты КАК СеткаДат
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ОстаткиПоСчетам КАК РегистрФизический
ПО РегистрФизический.Период <= СеткаДат.Дата
СГРУППИРОВАТЬ ПО
СеткаДат.Дата,
РегистрФизический.Организация
;
Шаг 3. Получение значений ресурсов. Теперь мы берем нашу таблицу с рассчитанными максимальными датами и соединяем ее с физической таблицей регистра сведений, чтобы получить актуальные значения ресурсов на эти моменты времени.
ВЫБРАТЬ
ТаблицаМаксДат.ПериодСреза КАК Период,
ТаблицаМаксДат.Организация КАК Организация,
РегистрФизическийЗначения.Сумма КАК Сумма
ИЗ
ВТ_МаксимальныеДаты КАК ТаблицаМаксДат
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ОстаткиПоСчетам КАК РегистрФизическийЗначения
ПО ТаблицаМаксДат.Организация = РегистрФизическийЗначения.Организация
И ТаблицаМаксДат.МаксПериод = РегистрФизическийЗначения.Period
Этот шаблон запроса является стандартом разработки в 1С при работе с периодическими регистрами сведений и позволяет быстро и корректно получать динамику изменений на любые произвольные даты.