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