В практике разработки на платформе 1С:Предприятие часто встречается ситуация: написанный запрос идеально отрабатывает в консоли запросов, выводя нужные цифры, но при переносе в программный модуль (например, в процедуру обработки нажатия кнопки) результат оказывается пустым или нулевым. Рассмотрим детально, почему так происходит, и проанализируем основные ошибки на примере работы с регистром накопления Остатки.
Проанализируем фрагмент кода из примера. Разработчик пытается получить остатки, накладывая условия в секции ГДЕ:
ВЫБРАТЬ
МойСкладТашкентОстатки.КоличествоОстаток КАК КоличествоОстаток
ИЗ
РегистрНакопления.МойСкладТашкент.Остатки КАК МойСкладТашкентОстатки
ГДЕ
МойСкладТашкентОстатки.Номенклатура.Код = &ID
И МойСкладТашкентОстатки.Основание.Дата <= &MOMENT
Разберем, почему это в корне неверно. Виртуальные таблицы (такие как Остатки или ОстаткиИОбороты) — это не просто таблицы в базе данных, а сложные механизмы, которые 1С формирует «на лету». Когда мы используем ГДЕ, система сначала рассчитывает вообще все остатки по всем товарам на текущий момент времени, и только потом начинает отсеивать лишнее по вашему условию. Это критически замедляет работу системы.
Правильный подход заключается в передаче фильтров напрямую в параметры виртуальной таблицы. Это позволяет СУБД выбрать только нужные записи еще на этапе формирования выборки. Наш запрос должен выглядеть так:
ВЫБРАТЬ
МойСкладТашкентОстатки.КоличествоОстаток
ИЗ
РегистрНакопления.МойСкладТашкент.Остатки(&Период, Номенклатура = &НоменклатураСсылка) КАК МойСкладТашкентОстатки
Посмотрим на то, как устанавливаются параметры в коде автора: ITEM = ЭлементыФормы.Товар.Значение.Код;. Далее этот строковый код передается в запрос. Это классическая ошибка, которая часто приводит к тому, что «в консоли работает (где мы выбрали товар интерактивно), а в коде — нет».
Рассмотрим причины:
Ссылка).Всегда передавайте в запрос объект типа СправочникСсылка. Выясним, как это сделать правильно в обычных формах:
// Плохо:
Запрос.УстановитьПараметр("ID", ЭлементыФормы.Товар.Значение.Код);
// Хорошо:
Запрос.УстановитьПараметр("НоменклатураСсылка", ЭлементыФормы.Товар.Значение);
В 1С тип Дата включает время до секунды. Если вы передаете в параметр виртуальной таблицы остатков просто дату, например 01.01.2023 00:00:00, система покажет остатки на начало этой секунды. Если документ поступления был записан ровно в это же время, его движения не попадут в остаток.
Консоли запросов часто имеют «умные» настройки, которые незаметно для пользователя включают указанную дату в расчет. В коде же мы должны управлять этим явно. Для получения остатка, включающего движения последней секунды, используем объект Граница:
МоментВремени = ЭлементыФормы.Дата.Значение;
ГраницаПериода = Новый Граница(МоментВремени, ВидГраницы.Включая);
Запрос.УстановитьПараметр("Период", ГраницаПериода);
Проанализируем ситуацию автора: использование MOMENT = Дата(ЭлементыФормы.Дата.Значение); избыточно, если реквизит уже имеет тип Дата, и может привести к непредсказуемым результатам при пустых значениях.
В тексте сообщения упоминается использование поля Основание.Дата в условии. Если поле Основание (например, документ поступления) не является Измерением регистра накопления, то в виртуальной таблице Остатки этого поля просто не будет. Если же оно является измерением, то остатки не будут суммироваться по товару в целом, а будут «висеть» в разрезе каждого конкретного документа.
Выясним причину: если ваша задача — получить общее количество товара на складе, из запроса к остаткам нужно исключить любые ссылки на конкретные документы (Регистраторы/Основания), иначе вы получите детализацию, которая помешает расчету итоговой суммы.
Автор использует в запросе конструкцию ИТОГИ СУММА(КоличествоОстаток) ПО ОБЩИЕ. В этом случае результат запроса представляет собой дерево, где на самом верхнем уровне находится одна строка с итоговой суммой.
Рассмотрим, как правильно прочитать такие данные. При использовании ОбходРезультатаЗапроса.ПоГруппировкам первый вызов Выборка.Следующий() обращается именно к итоговой строке. Если же в самом запросе из-за фильтров (например, по коду) не найдено ни одной записи, Выборка.Следующий() вернет Ложь, и программа просто пропустит блок вывода данных.
Посмотрим на исправленный и оптимизированный алгоритм:
Процедура КнопкаСформироватьНажатие(Кнопка)
// 1. Получаем ссылки на объекты, а не их строковые представления
ТоварСсылка = ЭлементыФормы.Товар.Значение;
ХарактеристикаСсылка = ЭлементыФормы.Диоптрия.Значение;
// 2. Формируем правильную границу времени
МоментРасчета = Новый Граница(ЭлементыФормы.Дата.Значение, ВидГраницы.Включая);
Запрос = Новый Запрос;
// Используем параметры виртуальной таблицы для фильтрации
Запрос.Текст =
"ВЫБРАТЬ
| МойСкладОстатки.КоличествоОстаток КАК Остаток
|ИЗ
| РегистрНакопления.МойСкладТашкент.Остатки(&Период,
| Номенклатура = &Товар И Диоптрия = &Диоптрия) КАК МойСкладОстатки";
Запрос.УстановитьПараметр("Товар", ТоварСсылка);
Запрос.УстановитьПараметр("Диоптрия", ХарактеристикаСсылка);
Запрос.УстановитьПараметр("Период", МоментРасчета);
Результат = Запрос.Выполнить();
Если Не Результат.Пустой() Тогда
Выборка = Результат.Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить("Количество на складе: " + Выборка.Остаток);
КоличествоНаСкладе = Выборка.Остаток;
КонецЦикла;
Иначе
Сообщить("Данные не найдены. Проверьте правильность фильтров.");
КонецЕсли;
КонецПроцедуры
Разберем по шагам, что нужно проверить, если ваш код в 1С выдает не те результаты, что консоль запросов:
Ссылку, а не Код или Наименование. Проверьте это в отладчике, остановившись на строке Запрос.УстановитьПараметр.ГДЕ в скобки виртуальной таблицы.Новый Граница(Дата, ВидГраницы.Включая).ЭлементыФормы.ИмяЭлемента.Значение, но лучше — напрямую к реквизиту объекта или формы, к которому привязано поле.Соблюдение этих правил не только решит проблему расхождения результатов, но и значительно повысит производительность вашей конфигурации.