При работе с регистрами остатков в платформе 1С 7.7 разработчики часто сталкиваются с ситуацией, когда запрос возвращает ненулевой конечный остаток (функция КонОст), хотя по движениям приход полностью закрыт расходом. Проанализируем, почему возникает такая проблема и как правильно строить запросы для получения корректных остатков.
Рассмотрим стандартный пример текста запроса в 1С 7.7, в котором проявляется данное поведение:
выбДатаЗапрос = ПолучитьДатуТА();
Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса =
"//{{ЗАПРОС(Сформировать)
|Период с выбДатаЗапрос по выбДатаЗапрос;
|ДокЗаказа = Регистр.ЗаказыТоваров.ЗаказПокупателя;
|Количество = Регистр.ЗаказыТоваров.Количество;
|Функция КонОстКол = КонОст(Количество);
|Условие(Запрос.КонОстКол > 0);
|Группировка ДокЗаказа;
|";
Запрос.Выполнить(ТекстЗапроса);
ТЗ = СоздатьОбъект("ТаблицаЗначений");
Запрос.Выгрузить(ТЗ);
Главная причина некорректного результата заключается в конструкции Условие(Запрос.КонОстКол > 0). В платформе 1С 7.7 внутренний механизм языка запросов обрабатывает секцию Условие(), наложенную на агрегатную функцию (такую как КонОст или Сумма), нестабильно. Проверка условия может выполняться на этапе промежуточной выборки записей до окончательного свертывания итогов по группировкам. В результате в итоговую выборку попадают искаженные данные, и запрос выводит ненулевой остаток там, где логически должен быть 0.
Разберем по шагам, как исправить запрос. Для получения корректных данных необходимо полностью исключить проверку агрегатной функции из текста запроса и перенести фильтрацию в модуль 1С.
ТекстЗапроса =
"//{{ЗАПРОС(Сформировать)
|Период с выбДатаЗапрос по выбДатаЗапрос;
|ДокЗаказа = Регистр.ЗаказыТоваров.ЗаказПокупателя;
|Количество = Регистр.ЗаказыТоваров.Количество;
|Функция КонОстКол = КонОст(Количество);
|Группировка ДокЗаказа;
|";
Если Запрос.Выполнить(ТекстЗапроса) = 1 Тогда
Пока Запрос.Группировка(1) = 1 Цикл
Если Запрос.КонОстКол > 0 Тогда
// Обработка только положительных остатков
Сообщить("Заказ: " + Запрос.ДокЗаказа + " - Остаток: " + Запрос.КонОстКол);
КонецЕсли;
КонецЦикла;
КонецЕсли;
При таком подходе запрос корректно свернет все движения по указанным измерениям. Если приход равен расходу, функция КонОст вернет 0, и строка просто не попадет в конечную обработку.
Если даже после удаления секции Условие() запрос продолжает выводить ненулевой остаток, выясним причину в структуре регистра:
ЗаказПокупателя и Товар), а в запросе выбрано только одно из них, система сворачивает остатки по всем значениям остальных измерений. Если приход и расход были оформлены с разными значениями скрытого измерения (например, разные номенклатурные позиции), то в разрезе отдельных комбинаций остаток не равен нулю.Количество и Сумма), регистр считает запись активной до тех пор, пока хотя бы один из ресурсов не закрылся в ноль.Обязательно убедимся, что все ключевые измерения регистра указаны в запросе или что движения документа-расхода полностью совпадают с измерениями документа-прихода.
В платформе 1С 7.7 виртуальные остатки хранятся в таблице итогов (RG), а документы записывают движения в таблицу (RA). В случае сбоев работы базы данных или некорректного завершения работы программы данные итогов могут рассинхронизироваться с реальными движениями.
Если универсальный отчет считает остаток по движениям и показывает 0, а объект Запрос берёт данные из таблицы итогов и возвращает неверное значение, необходимо выполнить пересчет итогов:
Если задача заключается в получении остатка по конкретному измерению без построения сложного отчета, эффективнее использовать объект Регистры напрямую, избегая использования механизма запросов.
Рег = СоздатьОбъект("Регистр.ЗаказыТоваров");
Рег.УстановитьФильтр(ВыбранныйЗаказ, ВыбранныйТовар);
ОстатокКол = Рег.СводныйОстаток(ВыбранныйЗаказ, ВыбранныйТовар, "Количество");
Если ОстатокКол > 0 Тогда
Сообщить("Актуальный остаток: " + ОстатокКол);
КонецЕсли;
Использование прямого обращения к регистрам работает быстрее механизма запросов и гарантирует получение точных остатков по заданному набору измерений.