Почему запрос остатков не показывает отрицательные значения на конкретную дату и как правильно использовать объект Граница?

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

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

Разбор проблемы: Дата против Границы

Проанализируем исходную ситуацию. Разработчик использует запрос к виртуальной таблице Остатки регистра ТоварыНаСкладах. В качестве параметра даты передается КонецДня(ДатаОтчета). На первый взгляд всё верно: мы хотим видеть итоги на 23:59:59. Однако возникает нюанс: если документ, создавший отрицательный остаток, записан ровно в 23:59:59, то обычный запрос с типом Дата может его «не увидеть».

В системе 1С точка времени 23:59:59 — это целая секунда, внутри которой может находиться множество документов. Когда мы передаем в виртуальную таблицу просто значение типа Дата, платформа по умолчанию получает остатки на начало этой секунды, исключая движения, совершенные в это мгновение. Именно поэтому стандартные отчеты показывают одну картину, а ваш запрос — другую.

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

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

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


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

Запрос.УстановитьПараметр("Склад", ЭтаФорма.СкладОтчет);
Запрос.УстановитьПараметр("ДатаГраница", ГраницаОтчета);

Важный момент: Обратите внимание, что объект Граница создается на стороне встроенного языка, а в сам запрос передается уже как готовый параметр. В самом тексте запроса (в конфигураторе или консоли) тип параметра изменить нельзя, это делается только в коде через УстановитьПараметр.

Решение №2: Математический обход (Добавление секунды)

Если по какой-то причине вы не хотите использовать объект Граница (например, при быстрой проверке в консоли запросов), можно прибегнуть к «хитрости» — прибавить к искомой дате одну секунду. Таким образом, мы получим остатки на 00:00:00 следующего дня не включая эту границу.

Проанализируем этот подход: если нам нужны остатки на конец дня 30.09.2023, мы можем запросить остатки на начало дня 01.10.2023. Поскольку параметр &Дата в виртуальной таблице по умолчанию работает в режиме «Исключая», остатки на 01.10.2023 00:00:00 будут фактически являться остатками на 30.09.2023 23:59:59 включая все движения этой секунды.


// Вариант с прибавлением секунды в коде
Запрос.УстановитьПараметр("Дата", КонецДня(ЭтаФорма.ДатаОтчета) + 1);

Однако будьте осторожны: если в вашей базе есть регламентные операции или документы, которые записываются строго в 00:00:00 следующего дня, они могут попасть в отчет и исказить данные. Поэтому использование Границы всегда предпочтительнее.

Особенности работы с виртуальными таблицами

Рассмотрим подробнее, как работают виртуальные таблицы остатков. Процессор запросов 1С не просто суммирует все движения с начала времен. Система хранит физические таблицы итогов (обычно на начало каждого месяца). Когда вы вызываете виртуальную таблицу Остатки на 13.10.2023, платформа выполняет следующие действия:

  1. Берет рассчитанные итоги на начало текущего месяца (01.10.2023 00:00:00).
  2. Прибавляет к ним все движения «Приход» и вычитает «Расход» из таблицы движений за период с 01.10.2023 по 13.10.2023.
  3. Выдает итоговый результат.

Именно на втором этапе возникает критическая важность того, как вы определили конечную точку. Если вы указали дату 30.09.2023 23:59:59, система может просто взять итоги из таблицы на 1-е число следующего месяца, но способ их извлечения будет зависеть от переданного типа (Дата или Граница).

Оптимизация запроса

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


|ИЗ
|    РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата, Склад = &Склад) КАК ТоварыНаСкладахОстатки
|ГДЕ
|    ТоварыНаСкладахОстатки.Склад = &Склад

Это избыточно. Всегда старайтесь передавать фильтры по измерениям (Склад, Номенклатура, Организация) внутрь параметров виртуальной таблицы. Это значительно ускоряет выполнение запроса, так как СУБД фильтрует данные еще на этапе получения итогов, а не после формирования всей выборки в памяти. Условие ГДЕ стоит оставить только для ресурсов (например, для отбора отрицательных остатков), так как остаток — это агрегированное значение, которое вычисляется в момент выполнения запроса.

Как проверить запрос в консоли?

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

  1. Использовать начало следующего дня: Как мы обсуждали выше, вместо 30.09.2023 23:59:59 укажите в параметрах 01.10.2023 00:00:00. В 99% случаев это даст тот же результат, что и Граница.Включая.
  2. Продвинутые консоли: Используйте инструменты (например, консоль запросов из состава «Инструментов разработчика»), которые поддерживают сложные типы параметров. Там можно явно выбрать тип «Граница» и установить флаг «Включая».
  3. Временная таблица: В сложных отчетах можно предварительно рассчитать границы и передать их во временные таблицы, но для простой проверки это излишне.

Итоги и рекомендации

Подводя итог, для получения корректных отрицательных остатков на конец дня, придерживайтесь следующих правил:

  1. Всегда используйте Новый Граница(КонецДня(Дата), ВидГраницы.Включая) при передаче даты в запрос из кода.
  2. Помните, что тип Дата без времени — это начало дня (00:00:00). Всегда используйте КонецДня() для отчетов.
  3. Переносите условия по измерениям из секции ГДЕ в параметры виртуальной таблицы. Это не только «хороший тон», но и залог производительности системы.
  4. Если данные в отчете и в вашем запросе расходятся — первым делом проверьте движения регистра за последнюю секунду периода (23:59:59). Скорее всего, «пропавшие» минусы скрываются именно там.

Правильное управление границами времени — это фундамент разработки качественных отчетов в 1С. Используя объект Граница, вы застрахуете себя от ошибок, связанных с последовательностью записи документов, и обеспечите высокую точность данных в ваших программных решениях.

← На главную