Проблема получения остатков на «самую последнюю секунду» дня (23:59:59) является классической для платформы 1С. Часто программисты сталкиваются с ситуацией, когда документ, созданный в 23:59:59, упорно игнорируется виртуальными таблицами регистров накопления, даже если в параметрах запроса указан конец дня и тип границы Включая. В этой статье мы подробно разберем, почему это происходит, и как правильно настроить запрос, чтобы данные учитывались корректно.
Разберем ситуацию: в системе есть документ начисления (например, аренды), который делает движения в регистр накопления в последнюю секунду месяца — 28.02.2021 23:59:59. При формировании запроса к виртуальной таблице ОстаткиИОбороты или Остатки программист передает дату конца дня. Однако документ «выпадает» из выборки. Почему так происходит?
Во-первых, важно понимать разницу между типом Дата и позицией документа на оси времени. В 1С документы, имеющие одинаковое время (вплоть до секунды), выстраиваются в строгую последовательность. Эта последовательность определяется моментом времени, который включает в себя не только время, но и ссылку на сам документ. Если мы передаем в запрос просто дату со временем 23:59:59, платформа может интерпретировать эту точку как «начало» этой секунды или как точку, после которой в этой же секунде могут существовать другие записи.
Во-вторых, если используется виртуальная таблица ОстаткиИОбороты с периодичностью Запись, расчет остатков идет на каждый момент движения. Если документ записан в ту же самую секунду, которая является границей периода, возникает неопределенность, которую платформа разрешает не всегда так, как ожидает разработчик.
Рассмотрим стандартный, но не всегда очевидный подход. Для того чтобы гарантированно включить документы последней секунды в запрос, необходимо использовать объект Граница. Однако крайне важно, что именно передается в конструктор этого объекта.
Если мы передаем в Границу только дату конца дня, этого может быть недостаточно. Проанализируем пример кода:
ПараметрыГраницы = Новый Массив(2);
ПараметрыГраницы[0] = КонецДня(МоментВремени); // Здесь МоментВремени — это Дата
ПараметрыГраницы[1] = ВидГраницы.Включая;
Граница = Новый(Тип("Граница"), ПараметрыГраницы);
Запрос.УстановитьПараметр("МоментНачисленияПени", Граница);
Если такой подход не работает, это означает, что документы внутри секунды 23:59:59 расположены «после» того логического момента, который сформировала Граница по дате. Чтобы решить эту проблему, попробуем использовать МоментВремени конкретного документа, если расчет привязан к нему, либо использовать специальное смещение.
Это один из самых надежных «практических» методов. Вместо того чтобы пытаться поймать последнюю секунду текущего дня, мы устанавливаем границу на начало следующего дня, но с видом границы Исключая. Либо просто используем дату начала следующего дня без объекта граница (по умолчанию в запросах для параметров конца периода используется вид границы Исключая).
Разберем, как это реализовать в коде:
// Вместо КонецДня(НужнаяДата)
НужнаяДата = '20210228235959';
ГраницаСледующегоДня = НачалоДня(НужнаяДата + 86400); // Получаем 01.03.2021 00:00:00
// В запросе используем эту дату как параметр
Запрос.УстановитьПараметр("КонецПериода", ГраницаСледующегоДня);
Важный нюанс: при таком подходе нужно быть уверенным, что в 00:00:00 следующего дня нет документов, которые не должны попасть в отчет. Если такие документы есть, они будут исключены, так как стандартное поведение виртуальных таблиц — не включать движения, период которых равен дате конца периода (если не указана Граница.Включая).
Проанализируем ситуацию, когда используется таблица ОстаткиИОбороты. В этой таблице расчет остатков сильно зависит от наложенных фильтров. Рассмотрим фрагмент запроса из обращения пользователя:
ИЗ
РегистрНакопления.АР_ВзаиморасчетыПоАренде.ОстаткиИОбороты(
&НачалоПериода,
&МоментНачисления,
Запись,
,
ДоговорКонтрагента = &ДоговорКонтрагента
И НЕ Депозит
И НЕ Пени) КАК АР_ВзаиморасчетыПоАрендеОстатки
Выясним причину возможной ошибки: если документ начисления в 23:59:59 сам по себе не соответствует условиям фильтра (например, у него стоит признак "Пени"), но при этом он должен влиять на остаток по другим измерениям, то в таблице ОстаткиИОбороты может возникнуть «провал». При периодичности Запись система анализирует движения. Если движений, подходящих под фильтр, в эту секунду нет, конечный остаток может не рассчитаться так, как вы ожидаете.
Для получения чистого остатка на конец дня всегда выгоднее и надежнее использовать виртуальную таблицу .Остатки вместо .ОстаткиИОбороты, если вам не нужны данные об оборотах в этом же запросе.
Иногда документ игнорируется не из-за виртуальной таблицы, а из-за последующих соединений в тексте запроса. Посмотрим на условие соединения из примера:
ПО (АР_ВзаиморасчетыПоАрендеОстатки.Период > КОНЕЦПЕРИОДА(ДатаДоговора.ДатаЗаключенияДоговора, МЕСЯЦ))
Проанализируем ситуацию: функция КОНЕЦПЕРИОДА(..., МЕСЯЦ) возвращает дату с временем 23:59:59. Если период документа в регистре также равен 23:59:59 того же дня, то условие «строго больше» (>) отсечет этот документ. Это критическая ошибка проектирования запроса. В 1С сравнение дат происходит с точностью до секунды. Если две даты равны 23:59:59, то одна не может быть «больше» другой.
Рекомендация: Всегда проверяйте операторы сравнения при работе с датами конца периода. Вместо > используйте >= или приводите сравниваемые величины к началу дня, если время не имеет значения для логики расчета.
Новый Граница(КонецДня(НужнаяДата), ВидГраницы.Включая) для передачи в параметры виртуальной таблицы.НачалоДня(НужнаяДата + 86400) (следующий день) в качестве границы.ГДЕ или в условиях соединения оператор > (строго больше) по отношению к дате документа.ОстаткиИОбороты фильтры не отсекают сам документ, который должен зафиксировать остаток на конец периода.ОстаткиИОбороты на таблицу Остатки — она работает с границами времени более предсказуемо.Применяя эти методы, мы обеспечим точный учет всех документов, независимо от того, в какую секунду дня они были проведены.