Как правильно передать период "конец дня" в виртуальную таблицу Остатки в 1С?

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

Разработчики 1С при создании отчетов или механизмов проведения документов регулярно сталкиваются с необходимостью получить остатки по регистру накопления на "конец дня". На первый взгляд задача кажется тривиальной: достаточно передать в параметры виртуальной таблицы КонецДня(УказаннаяДата). Однако на практике это приводит к классической ошибке — движения документов, проведенных в самую последнюю секунду дня (в 23:59:59), просто не попадают в расчет.

В этой статье мы подробно разберем, почему платформа 1С ведет себя именно так, проанализируем внутренние механизмы работы виртуальных таблиц и рассмотрим наиболее эффективные способы решения этой проблемы с помощью объекта Граница и функции запроса ДОБАВИТЬКДАТЕ.

Архитектурная причина: почему "Остатки" не включают граничную секунду?

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

По умолчанию виртуальная таблица Остатки рассчитывает данные на начало указанной секунды (то есть строго по условию "меньше", а не "меньше или равно"). Если мы передаем в запрос дату 27.10.2023 23:59:59, платформа рассчитает итоги ровно на начало этой секунды. Любые движения документов, которые были записаны в эту же секунду 23:59:59, учтены не будут.

Это архитектурное решение принято для корректной работы оперативного проведения документов. Когда документ проводится "в режиме реального времени", он должен проверить остатки в базе данных прямо перед своим проведением, не учитывая свои собственные движения. Если бы виртуальная таблица по умолчанию включала движения граничной секунды, это привело бы к логическим коллизиям и невозможности корректного контроля отрицательных остатков.

Однако при формировании отчетов за день нам, наоборот, жизненно важно увидеть остатки, актуальные на самый конец дня, с учетом всех проведенных документов. Рассмотрим, как решить эту задачу.

Решение 1. Использование объекта "Граница" в коде

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

Конструктор объекта принимает два параметра:

  1. Значение периода — в нашем случае это будет конец дня от нашей даты.
  2. Вид границы — системное перечисление ВидГраницы.Включая или ВидГраницы.Исключая.

Рассмотрим, как правильно применить этот объект при передаче параметров в запрос:


// Подготавливаем дату конца дня
КонецДняПараметр = КонецДня(ТекущаяДата());

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

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

// Передаем объект Граница вместо обычной даты
Запрос.УстановитьПараметр("Период", ГраницаПериода);
Запрос.УстановитьПараметр("Склад", СкладПараметр);

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

При таком подходе СУБД выполнит запрос с условием "меньше или равно" (<=) для указанного времени, и все документы, попавшие на 23:59:59, будут успешно учтены в остатках.

Решение 2. Обходной путь в самом запросе (ДОБАВИТЬКДАТЕ)

Иногда у разработчика нет возможности передать параметр из внешнего кода (например, при динамическом построении запроса в сложных универсальных обработках, консолях запросов или когда параметр "прилетает" в чистом виде как тип Дата).

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

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

Посмотрим, как выглядит такой запрос:


ВЫБРАТЬ
	ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
	ТоварыНаСкладахОстатки.КоличествоОстаток КАК КоличествоОстаток
ИЗ
	РегистрНакопления.ТоварыНаСкладах.Остатки(
		ДОБАВИТЬКДАТЕ(КОНЕЦПЕРИОДА(&ДатаТек, ДЕНЬ), СЕКУНДА, 1), 
		Склад = &Склад
	) КАК ТоварыНаСкладахОстатки

Этот метод полностью эквивалентен использованию объекта Граница с видом Включая. Он чист с точки зрения СУБД и не требует сложных манипуляций в коде перед выполнением запроса.

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

Среди начинающих разработчиков часто бытует миф о том, что СКД "сама все понимает" и автоматически преобразует конечную дату в границу. Это не совсем так. Если вы просто привяжете стандартный параметр виртуальной таблицы к полю компоновщика с типом Дата, СКД транслирует в СУБД обычный запрос, и движения последней секунды будут потеряны.

Чтобы решить эту проблему в СКД без написания кода, выполните следующие шаги:

  1. Перейдите на вкладку Параметры в схеме компоновки данных.
  2. Найдите ваш параметр периода (например, КонецПериода).
  3. В колонке Выражение пропишите формулу автоматического прибавления секунды:

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

Если же вы формируете отчет программно, то перед выводом отчета вы можете перехватить параметры в компоновщике настроек и принудительно установить туда объект Граница:


ПараметрПериод = КомпоновщикНастроек.Настройки.ПараметрыДанных.Элементы.Найти(Новый ПараметрКомпоновкиДанных("Период"));
Если ПараметрПериод <> Неопределено Тогда
    ПараметрПериод.Значение = Новый Граница(КонецДня(РабочаяДата), ВидГраницы.Включая);
КонецЕсли;

Различия в поведении других виртуальных таблиц

Важно помнить, что логика исключения границы периода относится в основном к таблице Остатки регистров накопления и регистров бухгалтерии. Другие виртуальные таблицы работают по-другому:

Резюме

Для корректного получения остатков на конец дня в 1С используйте один из двух проверенных подходов:

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