При разработке и отладке механизмов оперативного контроля остатков в 1С (в частности, в конфигурациях на базе «Управление торговлей 10.3») разработчики нередко сталкиваются с ситуацией, когда сложный запрос с левым соединением табличной части документа и виртуальной таблицы остатков возвращает ненулевой остаток, тогда как отдельный изолированный запрос к той же виртуальной таблице РегистрНакопления.ТоварыОрганизаций.Остатки возвращает пустой результат. Рассмотрим подробно причины возникновения подобной проблемы и разберем по шагам методы её диагностики и устранения.
Проанализируем структуру условий связи в первом запросе. В типовом механизме контроля остатков левое соединение с таблицей остатков строится по полному набору измерений регистра:
| ПО Док.Номенклатура = Остатки.Номенклатура
| И Док.ХарактеристикаНоменклатуры = Остатки.ХарактеристикаНоменклатуры
| И Док.СерияНоменклатуры = Остатки.СерияНоменклатуры
| И Док.Качество = Остатки.Качество
Если мы создаем тестовый проверочный запрос и выбираем из виртуальной таблицы только поля Организация, Номенклатура и КоличествоОстаток, без детализации по характеристикам и сериям, результат может вводить в заблуждение. Если в регистре присутствуют движения с заполненными сериями или характеристиками, а в тестовом запросе они не отображаются или отсекаются неявными отборами, мы получим принципиально разные выборки.
Посмотрим на пример правильного запроса для проверки остатков с полным набором измерений:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТоварыОрганизацийОстатки.Организация КАК Организация,
| ТоварыОрганизацийОстатки.Номенклатура КАК Номенклатура,
| ТоварыОрганизацийОстатки.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
| ТоварыОрганизацийОстатки.СерияНоменклатуры КАК СерияНоменклатуры,
| ТоварыОрганизацийОстатки.Качество КАК Качество,
| ТоварыОрганизацийОстатки.КоличествоОстаток КАК КоличествоОстаток
|ИЗ
| РегистрНакопления.ТоварыОрганизаций.Остатки(
| &МоментВремени,
| Номенклатура = &Номенклатура
| И Организация = &Организация
| И Комиссионер = &Комиссионер) КАК ТоварыОрганизацийОстатки";
Запрос.УстановитьПараметр("МоментВремени", ГраницаКонтроля);
Запрос.УстановитьПараметр("Номенклатура", ВыбраннаяНоменклатура);
Запрос.УстановитьПараметр("Организация", ДокументСсылка.Организация);
Запрос.УстановитьПараметр("Комиссионер", Справочники.Контрагенты.ПустаяСсылка());
Результат = Запрос.Выполнить().Выгрузить();
Обратим внимание на конструкцию расчета полей в первом запросе:
| СУММА(ВЫРАЗИТЬ(Док.Количество * Док.Коэффициент / Док.Номенклатура.ЕдиницаХраненияОстатков.Коэффициент КАК ЧИСЛО(15, 3))) КАК ДокументКоличество,
| ЕСТЬNULL(МАКСИМУМ(Остатки.КоличествоОстаток), 0) КАК ОстатокКоличество,
| Остатки.Организация
Использование функции МАКСИМУМ для получения остатка при левом соединении таит в себе риски:
Остатки.Организация включено в секцию СГРУППИРОВАТЬ ПО. Если соединение не находит соответствия в правой таблице, поле принимает значение NULL. Это может разбить результат на непредвиденные группы строк.МАКСИМУМ вернет наибольшее значение из найденных строк, создавая иллюзию наличия требуемого остатка.Когда запрос выдает нелогичные данные только по одной конкретной позиции номенклатуры или конкретному документу, наиболее вероятной причиной является нарушение целостности таблиц итогов регистра накопления.
Регистры накопления в 1С физически состоят из двух основных таблиц в СУБД: таблицы движений (фактических записей) и таблицы агрегированных итогов. При аварийном завершении сеансов, программных сбоях или некорректных транзакциях итоги могут рассинхронизироваться с реальными записями движений.
Разберем по шагам, как устранить рассинхронизацию данных:
РегистрыНакопления.ТоварыОрганизаций.ПересчитатьИтоги();
Чтобы избежать ошибок в алгоритмах контроля остатков, придерживаемся следующих правил:
Номенклатура В (ВЫБРАТЬ РАЗЛИЧНЫЕ ...) внутри параметров таблицы Остатки препятствует эффективной оптимизации плана выполнения на уровне СУБД. Рекомендуется помещать список номенклатуры документа в индексированную временную таблицу и соединять её с виртуальной таблицей остатков через ЛЕВОЕ СОЕДИНЕНИЕ.Новый Граница(ДокументСсылка.МоментВремени(), ВидГраницы.ВключаяГраницу) или ВидГраницы.ИсключаяГраницу в зависимости от учетной логики проведения. Пустой параметр заставляет систему рассчитывать остаток на текущую системную дату.