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