Разработчики отчетов в системе 1С:Предприятие часто сталкиваются со следующей проблемой: документ делает движения по регистру накопления в самую последнюю секунду дня (23:59:59). При построении отчета на эту же дату и время остатки выводятся без учета движений, сделанных этим документом. Если же сдвинуть время в параметрах отчета на начало следующего дня (00:00:00), данные отображаются корректно. Выясним причину этой проблемы и подробно разберем, как построить отчет в Системе компоновки данных (СКД), который гарантированно учтет все документы, проведенные на границе времени.
Для понимания природы этой проблемы проанализируем, как платформа 1С:Предприятие работает со временем и виртуальными таблицами регистров. Время в системе дискретно, его минимальной единицей является секунда. В пределах одной секунды в базе данных может быть записано и проведено неограниченное количество документов. Для соблюдения строгой последовательности платформа упорядочивает их по уникальному идентификатору (GUID) и моменту времени.
Когда мы обращаемся к виртуальной таблице Остатки регистра накопления и передаем в качестве параметра периода обычную дату (например, тип Дата со значением 31.12.2023 23:59:59), платформа по умолчанию вычисляет остатки на начало этой секунды. Любые движения, совершенные внутри этой конкретной секунды, в выборку просто не попадают. Фактически передача даты работает аналогично использованию границы с исключением (ВидГраницы.Исключая).
Для решения этой проблемы существуют три основных подхода, которые мы разберем далее по шагам.
Самый простой и не требующий сложного программирования метод — это смещение времени расчета остатков на одну секунду вперед. Если нам нужно получить актуальные остатки на конец дня (23:59:59), мы можем запросить остатки на начало следующего дня (00:00:00).
Рассмотрим, почему это абсолютно безопасно для корректности данных. Виртуальная таблица Остатки на момент времени 01.01.2024 00:00:00 вернет остатки на начало этой секунды, не включая движения, которые будут сделаны документами в эту самую секунду (если таковые вообще найдутся). Таким образом, все движения за предыдущий день (включая критическую секунду 23:59:59) будут полностью учтены, а новые документы следующего дня не исказят отчет.
В СКД этот механизм можно настроить декларативно, без написания кода в модулях. Перейдем на вкладку «Параметры» схемы компоновки данных и найдем параметр, который отвечает за конец периода (например, &КонецПериода). В колонке «Выражение» пропишем следующую формулу расчета:
ВЫБОР
КОГДА &КонецПериода = ДатаВремя(1, 1, 1)
ТОГДА ДатаВремя(1, 1, 1)
ИНАЧЕ ДОБАВИТЬКДАТЕ(КОНЕЦПЕРИОДА(&КонецПериода, "ДЕНЬ"), "СЕКУНДА", 1)
КОНЕЦ
Этот способ автоматически сдвигает любую выбранную пользователем дату на начало следующего дня, обеспечивая попадание всех документов финальной секунды в область расчета остатков.
Если смещение даты на следующую секунду по каким-то причинам нежелательно, мы можем использовать специальный тип данных платформы 1С — Граница. Этот объект позволяет точно указать системе, нужно ли включать движения граничного момента времени в расчет.
Для реализации этого подхода выполним программную установку параметров в модуле отчета. Разберем пример реализации через предопределенную процедуру ПриКомпоновкеРезультата:
Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
// Получаем текущие настройки отчета
Настройки = КомпоновщикНастроек.ПолучитьНастройки();
// Ищем параметр конца периода
ПараметрКонецПериода = Настройки.ПараметрыДанных.Элементы.Найти(Новый ПараметрКомпоновкиДанных("КонецПериода"));
Если ПараметрКонецПериода <> Неопределено И ЗначениеЗаполнено(ПараметрКонецПериода.Значение) Тогда
// Создаем объект Граница, включающий движения крайней секунды
ГраницаПериода = Новый Граница(КонецДня(ПараметрКонецПериода.Значение), ВидГраницы.Включая);
// Устанавливаем Границу в качестве значения параметра
ПараметрКонецПериода.Значение = ГраницаПериода;
ПараметрКонецПериода.Использование = Истина;
КонецЕсли;
КонецПроцедуры
Этот код перехватывает момент формирования отчета, берет указанную пользователем дату, приводит ее к концу дня (23:59:59) и оборачивает в объект Граница с видом ВидГраницы.Включая. В результате виртуальная таблица получит точную инструкцию учесть все транзакции этой секунды.
Существует изящный способ передать объект Граница непосредственно из схемы СКД, избегая написания кода в модуле самого отчета. Для этого мы можем задействовать функции общего модуля конфигурации.
Выполним следующие шаги:
ОбщийМодульСКД). В его свойствах обязательно взведем флаги «Клиент (управляемое приложение)» и «Сервер».
Функция ПолучитьГраницуВключая(Дата) Экспорт
Возврат Новый Граница(КонецДня(Дата), ВидГраницы.Включая);
КонецФункции
&Период в поле «Выражение» укажем вызов этой функции:
ОбщийМодульСКД.ПолучитьГраницуВключая(&КонецПериода).Важное примечание: При программном программном выводе такого отчета необходимо убедиться, что у процессора компоновки включено свойство ВозможностьИспользованияВнешнихФункций, иначе платформа выдаст ошибку безопасности.
При использовании объекта Граница (как программно, так и через функции общего модуля) разработчики часто сталкиваются с неприятным визуальным дефектом: при выводе параметров в шапку отчета вместо понятной даты пользователь видит пустое значение или системную строку вида Граница (Граница). Платформа не умеет преобразовывать этот сложный объект в красивую дату для вывода на экран.
Для обхода этой проблемы применим эффективный паттерн проектирования, который называется «Двойной параметр». Реализуем его по шагам:
ПериодОтчета — тип Дата. Именно его мы делаем доступным пользователю. Он будет выводиться в шапке отчета и использоваться для интерактивного выбора.ПериодГраница — тип Граница (или Дата со смещением). Для этого параметра мы устанавливаем флаг «Ограничение доступности», скрывая его от глаз пользователя.ПериодГраница пропишем в поле «Выражение» формулу расчета на основе пользовательского параметра:
ДОБАВИТЬКДАТЕ(КОНЕЦПЕРИОДА(&ПериодОтчета, "ДЕНЬ"), "СЕКУНДА", 1).Остатки укажем именно скрытый параметр &ПериодГраница.Благодаря такой связке пользователь видит в заголовке отчета красивую дату (например, 31.12.2023), а подсистема компоновки данных отправляет в СУБД корректно смещенную на начало следующего дня границу, гарантируя стопроцентную точность выгружаемых остатков.