Почему запрос к остаткам регистра бухгалтерии возвращает пустой результат?

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

Часто разработчики 1С сталкиваются с ситуацией, когда запрос к виртуальной таблице остатков регистра бухгалтерии возвращает пустой результат, хотя в оборотно-сальдовой ведомости данные за этот период есть. Особенно часто это происходит, если на начало дня не было остатка (сальдо), а все движения (поступление и списание) произошли в течение одного дня. Давайте разберем на конкретном примере, в чем может быть причина и как найти решение.

Исходная задача: получить остатки партий на конец дня

Представим ситуацию: нам нужно получить остатки товаров в разрезе партий на определенном складе на конец дня. Товар в этот день поступил и сразу же должен быть перемещен. Для этого мы пишем запрос к виртуальной таблице РегистрБухгалтерии.Хозрасчетный.Остатки.

Вот пример кода, с которым возникла проблема:


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

ВидыСубконто = Новый Массив;
ВидыСубконто.Добавить(ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.Склад);
ВидыСубконто.Добавить(ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.СтавкаНДС);
ВидыСубконто.Добавить(ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.Номенклатура);
ВидыСубконто.Добавить(ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.Партия);

Запрос.УстановитьПараметр("ВыбСклад", СкладОтправитель.Ссылка);
Запрос.УстановитьПараметр("ВыбНоменклатура", Товар.Ссылка);
Запрос.УстановитьПараметр("ВыбСтавкаНДС", Товар.Ссылка.СтавкаНДС);
Запрос.УстановитьПараметр("ВидыСубконто", ВидыСубконто);
Запрос.УстановитьПараметр("ВыбДата", КонецДня(датаДок));
Запрос.УстановитьПараметр("ВыбСчет", ПланыСчетов.Хозрасчетный.ТоварыНаСкладах);    
РезультатЗапроса = Запрос.Выполнить();

Проблема заключается в том, что если на начало дня, указанного в переменной датаДок, не было остатка по данному товару, то запрос ничего не возвращает, даже если в течение дня было поступление. Если же на начало дня был какой-то остаток, запрос отрабатывает корректно.

Анализ ситуации: "Остатки" vs "ОстаткиИОбороты"

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

Первая мысль при отладке — перейти на использование таблицы ОстаткиИОбороты, чтобы увидеть движения. Это правильный шаг для диагностики.

Подводные камни при работе с датой в запросах

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

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

Пример правильного указания периода:


// Вместо КонецДня(датаДок)
МоментВремени = Новый Граница(КонецДня(датаДок), ВидГраницы.Включая);
Запрос.УстановитьПараметр("ВыбДата", МоментВремени);

Этот синтаксис гарантирует, что все движения до конца дня включительно будут учтены при расчете остатков.

Главная причина проблемы: ошибка в хронологии документов

Несмотря на все тонкости работы с запросами, в рассмотренном случае корень проблемы оказался не в коде, а в данных. После долгой отладки выяснилось следующее:

  1. Документ "Оприходование товаров" был проведен датой 05.05.2020 18:30:00.
  2. Документ "Перемещение товаров", который должен был списать этот же товар, был проведен датой 05.05.2020 15:20:00.

Система 1С строго следит за хронологией операций на основе МоментаВремени документа (дата + время с точностью до секунды). Когда выполнялся код для документа перемещения в 15:20, система запрашивала остатки на этот момент. Естественно, она получала пустой результат, так как товар по документам "приходил" на склад только через три часа, в 18:30.

Это классический пример ошибки в данных, которая маскируется под ошибку в коде запроса. Запрос абсолютно корректно сообщал, что на момент списания товара на складе не было. Использование КонецДня() в параметре запроса не помогало, потому что сам запрос выполнялся в контексте проведения документа "Перемещение" с его собственным, более ранним моментом времени.

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

Итоговый работающий код и выводы

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


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

// ... (установка параметров как в первом примере) ...

Запрос.УстановитьПараметр("ВыбДата", Новый Граница(датаДок, ВидГраницы.Включая)); // Рекомендуется использовать Границу

Из этой ситуации можно сделать несколько важных выводов:

  1. Проверяйте хронологию документов. Перед тем как искать ошибку в сложном запросе, убедитесь, что последовательность документов в базе данных логична. `МоментВремени` — ключевой фактор для правильного расчета остатков.
  2. Понимайте разницу между "Остатками" и "ОстаткамиИОборотами". Для получения среза на момент времени используйте `Остатки`. Для анализа движений за период и отладки — `ОстаткиИОбороты`.
  3. Используйте инструменты отладки. Консоль запросов — незаменимый помощник. Она позволяет выполнить запрос с нужными параметрами и увидеть "сырой" результат, не зависящий от контекста проведения документа.
  4. Используйте объект Граница. Для точного указания периода в запросах, особенно на конец дня, используйте Новый Граница(..., ВидГраницы.Включая), чтобы избежать проблем с граничными значениями времени.
← На главную