Как получить остатки по регистру накопления на дату, включая последнюю секунду?

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

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

Почему стандартный параметр Даты не включает последнюю секунду?

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

Выясним причину: в системе 1С при передаче в параметры виртуальной таблицы «Остатки» простого значения типа Дата, расчет всегда производится на начало указанной секунды. С точки зрения SQL-запроса, который платформа генерирует «под капотом», условие выглядит как «меньше», а не «меньше или равно». Таким образом, любая запись, имеющая то же самое время, что и переданный параметр, оказывается за бортом выборки. Чтобы включить записи этой секунды, нам нужно явно указать платформе, что граница периода должна быть включительной.

Решение №1: Использование объекта Граница (Рекомендуемый способ)

Наиболее правильным и штатным методом решения проблемы является использование специального объекта Граница. Этот объект позволяет четко определить, должна ли точка времени включаться в интервал или нет. Рассмотрим, как это реализовать в программном коде:


// Определяем конечную дату
КонечнаяДата = КонецДня(Объект.ДатаПериода);

// Создаем объект Граница, который включает указанную секунду
ГраницаКон = Новый Граница(КонечнаяДата, ВидГраницы.Включая);

// Создаем запрос к виртуальной таблице остатков
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   ВзаиморасчетыОстатки.СуммаОстаток КАК Сумма
    |ИЗ
    |   РегистрНакопления.Взаиморасчеты.Остатки(&Период, ) КАК ВзаиморасчетыОстатки";

// Устанавливаем параметр
Запрос.УстановитьПараметр("Период", ГраницаКон);

Результат = Запрос.Выполнить();

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

Решение №2: Использование Момента Времени

Бывают случаи, когда в одну и ту же секунду (например, в ту же 23:59:59) записано сразу несколько документов. Если нам нужно получить остатки не просто «на конец дня», а «после конкретного документа», объект Дата нам не поможет, так как он не обладает достаточной точностью для разделения документов внутри одной секунды. Здесь на помощь приходит МоментВремени.

Проанализируем ситуацию: МоментВремени — это комбинация даты-времени и ссылки на конкретный документ. Он позволяет однозначно определить позицию документа на оси времени. Чтобы получить остатки, включающие конкретный документ, мы комбинируем Границу и МоментВремени:


// Предположим, у нас есть ссылка на документ
СсылкаНаДокумент = Объект.Ссылка;

// Создаем границу на момент конкретного документа включительно
ГраницаНаДокумент = Новый Граница(СсылкаНаДокумент.МоментВремени(), ВидГраницы.Включая);

Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ... ИЗ РегистрНакопления.ИмяРегистра.Остатки(&Момент, ...)";
Запрос.УстановитьПараметр("Момент", ГраницаНаДокумент);

Если же документа нет, а нужно просто захватить конец дня, можно использовать конструкцию Новый Граница(Новый МоментВремени(КонецДня(Дата)), ВидГраницы.Включая), как советовали участники обсуждения. Это избыточно по сравнению с первым способом, но технически корректно.

Почему способ «Дата + 1 секунда» — это плохая практика?

В сообщениях на форуме часто упоминается «лайфхак»: прибавить к дате одну секунду, то есть перенести расчет на 00:00:00 следующего дня. Рассмотрим подробнее, почему профессиональные разработчики 1С избегают этого метода:

  1. Логическая ошибка в данных: Если на первую секунду следующего дня уже назначены какие-то операции (например, регламентные документы или переоценка валюты, которые часто программно записываются на начало дня), они попадут в ваш отчет. Это приведет к искажению данных.
  2. Проблемы с индексами: Прибавление секунды может заставить СУБД просматривать лишние записи в таблице итогов, что в высоконагруженных системах может незначительно снизить производительность.
  3. Неочевидность кода: Другому программисту будет сложно понять, почему отчет за 31 декабря обращается к 1 января. Использование Границы делает намерения разработчика прозрачными.

Особенности работы в Системе компоновки данных (СКД)

Если вы разрабатываете отчет в СКД, платформа берет часть работы на себя. Посмотрим, как это работает. При использовании стандартного периода (начало и конец) в настройках отчета, СКД автоматически преобразует значение параметра «Конец периода» в объект Граница с видом Включая. Однако это происходит только в том случае, если параметр имеет тип СтандартныйПериод.

Если же вы создаете собственный параметр типа Дата в схеме компоновки, вам придется вручную в выражении параметра прописать обработку, чтобы система понимала необходимость включения последней секунды:


// В поле "Выражение" для параметра КонецПериода в СКД можно написать:
&КонецПериода = ДобавитьКДате(КонецПериода(&КонецПериода, "День"), "Секунда", 1) 
// Но лучше использовать стандартные механизмы работы с границами периода СКД.

Подведем итоги

Для гарантированного получения корректных остатков на последнюю секунду периода в 1С необходимо придерживаться следующих правил:

Понимание того, как 1С работает с осью времени и как это транслируется в SQL-запросы, позволяет создавать надежные учетные системы, в которых данные всегда сходятся до копейки, независимо от времени проведения документов.

← На главную