Как получить остатки в СКД на конец секунды и учесть все документы дня

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Разработчики отчетов в системе 1С:Предприятие часто сталкиваются со следующей проблемой: документ делает движения по регистру накопления в самую последнюю секунду дня (23:59:59). При построении отчета на эту же дату и время остатки выводятся без учета движений, сделанных этим документом. Если же сдвинуть время в параметрах отчета на начало следующего дня (00:00:00), данные отображаются корректно. Выясним причину этой проблемы и подробно разберем, как построить отчет в Системе компоновки данных (СКД), который гарантированно учтет все документы, проведенные на границе времени.

Архитектурная суть: дискретность времени и особенности виртуальной таблицы «Остатки»

Для понимания природы этой проблемы проанализируем, как платформа 1С:Предприятие работает со временем и виртуальными таблицами регистров. Время в системе дискретно, его минимальной единицей является секунда. В пределах одной секунды в базе данных может быть записано и проведено неограниченное количество документов. Для соблюдения строгой последовательности платформа упорядочивает их по уникальному идентификатору (GUID) и моменту времени.

Когда мы обращаемся к виртуальной таблице Остатки регистра накопления и передаем в качестве параметра периода обычную дату (например, тип Дата со значением 31.12.2023 23:59:59), платформа по умолчанию вычисляет остатки на начало этой секунды. Любые движения, совершенные внутри этой конкретной секунды, в выборку просто не попадают. Фактически передача даты работает аналогично использованию границы с исключением (ВидГраницы.Исключая).

Для решения этой проблемы существуют три основных подхода, которые мы разберем далее по шагам.

Решение №1: Смещение времени на начало следующего дня (+1 секунда)

Самый простой и не требующий сложного программирования метод — это смещение времени расчета остатков на одну секунду вперед. Если нам нужно получить актуальные остатки на конец дня (23:59:59), мы можем запросить остатки на начало следующего дня (00:00:00).

Рассмотрим, почему это абсолютно безопасно для корректности данных. Виртуальная таблица Остатки на момент времени 01.01.2024 00:00:00 вернет остатки на начало этой секунды, не включая движения, которые будут сделаны документами в эту самую секунду (если таковые вообще найдутся). Таким образом, все движения за предыдущий день (включая критическую секунду 23:59:59) будут полностью учтены, а новые документы следующего дня не исказят отчет.

В СКД этот механизм можно настроить декларативно, без написания кода в модулях. Перейдем на вкладку «Параметры» схемы компоновки данных и найдем параметр, который отвечает за конец периода (например, &КонецПериода). В колонке «Выражение» пропишем следующую формулу расчета:


ВЫБОР 
    КОГДА &КонецПериода = ДатаВремя(1, 1, 1) 
        ТОГДА ДатаВремя(1, 1, 1) 
    ИНАЧЕ ДОБАВИТЬКДАТЕ(КОНЕЦПЕРИОДА(&КонецПериода, "ДЕНЬ"), "СЕКУНДА", 1) 
КОНЕЦ

Этот способ автоматически сдвигает любую выбранную пользователем дату на начало следующего дня, обеспечивая попадание всех документов финальной секунды в область расчета остатков.

Решение №2: Программная установка параметра «Граница»

Если смещение даты на следующую секунду по каким-то причинам нежелательно, мы можем использовать специальный тип данных платформы 1С — Граница. Этот объект позволяет точно указать системе, нужно ли включать движения граничного момента времени в расчет.

Для реализации этого подхода выполним программную установку параметров в модуле отчета. Разберем пример реализации через предопределенную процедуру ПриКомпоновкеРезультата:


Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
    
    // Получаем текущие настройки отчета
    Настройки = КомпоновщикНастроек.ПолучитьНастройки();
    
    // Ищем параметр конца периода
    ПараметрКонецПериода = Настройки.ПараметрыДанных.Элементы.Найти(Новый ПараметрКомпоновкиДанных("КонецПериода"));
    
    Если ПараметрКонецПериода <> Неопределено И ЗначениеЗаполнено(ПараметрКонецПериода.Значение) Тогда
        // Создаем объект Граница, включающий движения крайней секунды
        ГраницаПериода = Новый Граница(КонецДня(ПараметрКонецПериода.Значение), ВидГраницы.Включая);
        
        // Устанавливаем Границу в качестве значения параметра
        ПараметрКонецПериода.Значение = ГраницаПериода;
        ПараметрКонецПериода.Использование = Истина;
    КонецЕсли;
    
КонецПроцедуры

Этот код перехватывает момент формирования отчета, берет указанную пользователем дату, приводит ее к концу дня (23:59:59) и оборачивает в объект Граница с видом ВидГраницы.Включая. В результате виртуальная таблица получит точную инструкцию учесть все транзакции этой секунды.

Решение №3: Передача «Границы» без программирования в модуле отчета

Существует изящный способ передать объект Граница непосредственно из схемы СКД, избегая написания кода в модуле самого отчета. Для этого мы можем задействовать функции общего модуля конфигурации.

Выполним следующие шаги:

  1. Создадим неглобальный общий модуль (например, с именем ОбщийМодульСКД). В его свойствах обязательно взведем флаги «Клиент (управляемое приложение)» и «Сервер».
  2. Напишем в нем простую экспортную функцию:
    
    Функция ПолучитьГраницуВключая(Дата) Экспорт
        Возврат Новый Граница(КонецДня(Дата), ВидГраницы.Включая);
    КонецФункции
    
  3. В схеме компоновки данных на вкладке «Параметры» для нашего параметра &Период в поле «Выражение» укажем вызов этой функции: ОбщийМодульСКД.ПолучитьГраницуВключая(&КонецПериода).

Важное примечание: При программном программном выводе такого отчета необходимо убедиться, что у процессора компоновки включено свойство ВозможностьИспользованияВнешнихФункций, иначе платформа выдаст ошибку безопасности.

Решение проблемы визуализации параметров («Двойной параметр»)

При использовании объекта Граница (как программно, так и через функции общего модуля) разработчики часто сталкиваются с неприятным визуальным дефектом: при выводе параметров в шапку отчета вместо понятной даты пользователь видит пустое значение или системную строку вида Граница (Граница). Платформа не умеет преобразовывать этот сложный объект в красивую дату для вывода на экран.

Для обхода этой проблемы применим эффективный паттерн проектирования, который называется «Двойной параметр». Реализуем его по шагам:

  1. В схеме СКД на вкладке «Параметры» создадим два параметра:
    • ПериодОтчета — тип Дата. Именно его мы делаем доступным пользователю. Он будет выводиться в шапке отчета и использоваться для интерактивного выбора.
    • ПериодГраница — тип Граница (или Дата со смещением). Для этого параметра мы устанавливаем флаг «Ограничение доступности», скрывая его от глаз пользователя.
  2. Для скрытого параметра ПериодГраница пропишем в поле «Выражение» формулу расчета на основе пользовательского параметра: ДОБАВИТЬКДАТЕ(КОНЕЦПЕРИОДА(&ПериодОтчета, "ДЕНЬ"), "СЕКУНДА", 1).
  3. В тексте запроса (или в настройках виртуальной таблицы на вкладке «Наборы данных») в качестве параметра периода для таблицы Остатки укажем именно скрытый параметр &ПериодГраница.

Благодаря такой связке пользователь видит в заголовке отчета красивую дату (например, 31.12.2023), а подсистема компоновки данных отправляет в СУБД корректно смещенную на начало следующего дня границу, гарантируя стопроцентную точность выгружаемых остатков.

← На главную