Работа с регистрами накопления — это фундамент оперативного учета в системе 1С:Предприятие. Однако начинающие (а иногда и опытные) разработчики часто сталкиваются с ситуацией, когда запрос к виртуальной таблице Остатки возвращает пустой результат или нулевое значение, хотя в базе данных движения по регистру присутствуют. В этой статье мы подробно разберем, как правильно извлекать остатки, проанализируем типичные ошибки при передаче параметров и выясним, как работают внутренние механизмы платформы.
Рассмотрим типовой пример запроса, который часто вызывает затруднения. Разберем код, представленный в обсуждении:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЗапасыНаСкладахОстатки.КоличествоОстаток
|ИЗ
| РегистрНакопления.ЗапасыНаСкладах.Остатки(&ВыбДата, Номенклатура = &ВыбНоменклатура) КАК ЗапасыНаСкладахОстатки";
Запрос.УстановитьПараметр("ВыбДата", Новый Граница(ТекущаяДата(), ВидГраницы.Включая));
Запрос.УстановитьПараметр("ВыбНоменклатура", Справочники.Номенклатура.НайтиПоКоду(Объект.Код));
Результат = Запрос.Выполнить();
Записи = Результат.Выбрать();
Пока Записи.Следующий() Цикл
Остаток = Записи.КоличествоОстаток;
КонецЦикла;
На первый взгляд код выглядит корректным, но именно в деталях кроются причины "нулевого" результата. Давайте проанализируем каждый элемент по отдельности.
Одной из ключевых особенностей запросов к остаткам является параметр &ВыбДата. В 1С:Предприятии 8 остатки всегда получаются на начало указанного момента времени. Если мы передаем просто ТекущаяДата(), то документы, записанные в ту же секунду, не попадут в расчет.
Для решения этой проблемы мы используем объект Граница. Проанализируем, как он работает:
ВидГраницы.Исключая — остатки будут получены "на пороге" указанного времени. Все, что произошло в эту секунду, не учитывается.ВидГраницы.Включая — остатки включают все движения, совершенные вплоть до указанной секунды включительно.Важный нюанс: если вам необходимо получить самый актуальный текущий остаток (на "сейчас"), в параметр периода виртуальной таблицы лучше вообще ничего не передавать. Если параметр Период не задан (равен Неопределено), система 1С обращается к таблице итогов на самый последний рассчитанный момент. Это работает быстрее и гарантирует получение актуальных данных без лишних вычислений.
В примере автора используется конструкция Справочники.Номенклатура.НайтиПоКоду(Объект.Код). Это классический пример избыточного кода, который может привести к ошибкам:
Номенклатура отключена уникальность кодов или используются разные префиксы, НайтиПоКоду может вернуть не тот элемент, который вы ожидаете.Объект является данными формы. Если вы находитесь в модуле формы, у вас уже есть доступ к Объект.Ссылка. Использование ссылки напрямую гораздо надежнее и быстрее, так как исключает лишний поиск в базе данных.Рассмотрим, как правильно передавать условия в виртуальную таблицу. Вместо обращения к реквизитам через точку (например, Номенклатура.Код = &Код), что заставляет систему выполнять неявное соединение таблиц, всегда старайтесь передавать готовую Ссылку.
Часто разработчики говорят: "В консоли запросов я вижу 5 штук, а программно возвращается 0". Проанализируем возможные причины этого феномена:
Для глубокого понимания работы системы важно знать, что 1С не считает остатки "на лету" по всем записям регистра с момента начала учета. Это было бы слишком медленно. Система использует таблицы итогов, которые хранят рассчитанные значения на начало каждого месяца.
Когда мы запрашиваем остатки на произвольную дату (например, 15-е число), платформа выполняет следующие действия:
1. Берет итоги на начало месяца.
2. Добавляет или вычитает движения, произошедшие с 1-го по 15-е число.
Если запрос возвращает пустоту, это значит, что в системе вообще нет записей, удовлетворяющих вашему фильтру (склад, номенклатура, характеристика). Важно помнить: виртуальная таблица Остатки не возвращает строку с нулевым значением. Если товара нет, запрос просто не вернет ни одной строки. Именно поэтому цикл Пока Записи.Следующий() в коде автора может просто не начаться.
Выясним причину ошибки, следуя алгоритму:
Шаг 1. Проверка параметров фильтрации.
Убедитесь, что в регистре накопления измерения заполнены именно теми значениями, которые вы передаете. Например, если в регистре учет ведется в разрезе складов, а вы не указали склад в параметрах, система выдаст общий остаток по всем складам. Но если вы передали пустую ссылку склада, а движения были по конкретному складу — вы получите ноль.
Шаг 2. Использование отладчика.
Остановите выполнение кода на строке Результат = Запрос.Выполнить();. В табло отладчика вычислите выражение Запрос.Параметры. Проверьте тип и значение каждого параметра. Особенно внимательно посмотрите на ВыбНоменклатура — действительно ли это та ссылка, которую вы ожидали увидеть?
Шаг 3. Упрощение запроса.
Попробуйте временно убрать фильтр по номенклатуре. Если запрос начал возвращать данные (общий остаток по всем позициям), значит, проблема точно в значении параметра &ВыбНоменклатура.
Шаг 4. Анализ актуальности итогов.
Зайдите в "Сервис" — "Управление итогами" и убедитесь, что итоги по вашему регистру рассчитаны по текущую дату. Хотя виртуальные таблицы должны работать и с нерассчитанными итогами, на практике это может приводить к неожиданным результатам в сложных запросах.
Проанализировав все ошибки, мы можем составить наиболее надежный вариант кода для получения остатков:
// Получаем актуальный остаток (на текущий момент времени)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЗапасыОстатки.КоличествоОстаток КАК Остаток
|ИЗ
| РегистрНакопления.ЗапасыНаСкладах.Остатки(, Номенклатура = &Номенклатура) КАК ЗапасыОстатки";
// Передаем ссылку напрямую, без поиска по коду
Запрос.УстановитьПараметр("Номенклатура", Объект.Ссылка);
Результат = Запрос.Выполнить();
Если Не Результат.Пустой() Тогда
Выборка = Результат.Выбрать();
Выборка.Следующий();
АктуальныйОстаток = Выборка.Остаток;
Иначе
АктуальныйОстаток = 0;
КонецЕсли;
Почему этот вариант лучше?
Результат.Пустой(), что корректно обрабатывает ситуацию отсутствия товара на складе.Объект.Ссылка, что является самым надежным способом идентификации элемента в базе данных.В завершение отметим, что в ситуации автора форума проблема оказалась банальной: данные в базу были введены по одной номенклатуре, а поиск велся по другой из-за путаницы в кодах. Это еще раз доказывает, что визуальная схожесть данных не гарантирует их идентичность в базе. Всегда используйте ссылки и проверяйте данные через консоль запросов, максимально точно копируя параметры из кода.