Часто разработчики сталкиваются с ситуацией, когда, казалось бы, простой запрос к виртуальной таблице остатков регистра накопления не возвращает ожидаемые данные. Например, мы знаем, что документ-основание (скажем, "Заказ клиента") сделал движения по номенклатуре, но в результате запроса к остаткам по этому заказу некоторых позиций нет. При этом прямой запрос к физической таблице регистра показывает все записи. Давайте разберемся, почему так происходит и как правильно диагностировать подобные проблемы.
Проанализируем типичный сценарий: есть документ "ЗаказИсходящий", который делает приходные движения в регистр накопления ЗаказыИсходящие. На его основании вводится "ПриходнаяНакладная", которая должна контролировать количество по заказу, но при проведении выдает ошибку о превышении, хотя, по нашему мнению, остатки по заказу еще есть. Запрос, который используется для проверки, выглядит так:
ВЫБРАТЬ
ЗаказыИсходящиеОстатки.Номенклатура КАК Номенклатура,
ЗаказыИсходящиеОстатки.Характеристика КАК Характеристика,
ЗаказыИсходящиеОстатки.КоличествоОстаток КАК Количество
ИЗ
РегистрНакопления.ЗаказыИсходящие.Остатки(, ПоДокументу = &ПоДокументу) КАК ЗаказыИсходящиеОстатки
В параметре &ПоДокументу передается ссылка на исходный заказ, но результат запроса оказывается неполным. Выясним причину.
Самая распространенная и фундаментальная причина такого поведения — логика работы виртуальной таблицы Остатки. Эта таблица предназначена для того, чтобы показывать, что есть в наличии на определенный момент времени. Если остаток по какому-либо ресурсу (в нашем случае, "Количество") в разрезе всех измерений равен нулю, платформа 1С считает, что показывать такую запись не нужно. Ведь если остатка нет, то и информации для анализа нет.
В нашем примере это означает, что по "пропавшим" номенклатурным позициям уже был выполнен расход в полном объеме. Например, по заказу было заказано 10 штук товара, и ранее уже была оформлена приходная накладная на эти 10 штук. В регистре ЗаказыИсходящие произошло следующее:
В итоге, итоговый остаток по этой номенклатуре в разрезе данного заказа стал равен 0. Поэтому виртуальная таблица Остатки справедливо не включает эту строку в результат. Это не ошибка, а штатное поведение системы.
Чтобы глубже понять проблему, важно различать, как мы получаем данные из регистров.
Физическая таблица (РегистрНакопления.ЗаказыИсходящие) — это основная таблица в базе данных, где хранятся абсолютно все записи о движениях: и приход, и расход. Каждая запись содержит ссылку на документ-регистратор, период, вид движения и значения измерений и ресурсов. Запрос к этой таблице покажет вам полную историю операций.
Виртуальные таблицы (.Остатки, .Обороты, .ОстаткиИОбороты) — это не реальные таблицы в базе данных. Платформа 1С формирует их "на лету", используя для этого специальные, заранее рассчитанные агрегированные данные — итоги. Итоги — это служебные таблицы, в которых хранятся подсчитанные остатки на начало каждого месяца (или другого периода). Благодаря этому, получение остатков на любую дату происходит очень быстро, так как системе не нужно каждый раз пересчитывать всю историю движений из физической таблицы. Именно из-за этой оптимизации и возникает разница в поведении: физическая таблица показывает историю, а виртуальная — рассчитанный результат.
Когда вы столкнулись с "пропажей" записей в остатках, необходимо провести последовательную диагностику. Рассмотрим по шагам, что нужно сделать.
Самый мощный инструмент для анализа таких ситуаций — виртуальная таблица ОстаткиИОбороты. Она позволяет получить полную картину: начальный остаток, все приходы, все расходы и конечный остаток за указанный период. Чтобы увидеть, какие документы повлияли на остаток, можно детализировать отчет по регистратору.
Посмотрим на пример. Сделаем запрос, который покажет всю динамику по нашему заказу и проблемной номенклатуре:
ВЫБРАТЬ
ЗаказыОстаткиИОбороты.Номенклатура,
ЗаказыОстаткиИОбороты.Регистратор,
ЗаказыОстаткиИОбороты.КоличествоНачальныйОстаток,
ЗаказыОстаткиИОбороты.КоличествоПриход,
ЗаказыОстаткиИОбороты.КоличествоРасход,
ЗаказыОстаткиИОбороты.КоличествоКонечныйОстаток
ИЗ
РегистрНакопления.ЗаказыИсходящие.ОстаткиИОбороты(, , Регистратор, , ПоДокументу = &ПоДокументу) КАК ЗаказыОстаткиИОбороты
Выполнив такой запрос в консоли, вы увидите всю цепочку документов: вот "Заказ" сделал приход, а вот "ПриходнаяНакладная" сделала расход. Сразу станет ясно, почему конечный остаток равен нулю. Для интерактивного выполнения и отладки подобных запросов можно использовать инженерную консоль запросов для 1С.
Чтобы на 100% убедиться в расчетах, можно написать запрос, который эмулирует получение остатков, но обращается напрямую к физической таблице. Этот метод также является отличным способом проверки, если вы подозреваете, что итоги регистра могли быть повреждены.
Запрос будет суммировать все движения, учитывая их вид: приходные с плюсом, расходные с минусом.
ВЫБРАТЬ
ЗаказыИсходящие.Номенклатура КАК Номенклатура,
ЗаказыИсходящие.Характеристика КАК Характеристика,
СУММА(ВЫБОР
КОГДА ЗаказыИсходящие.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)
ТОГДА ЗаказыИсходящие.Количество
ИНАЧЕ -ЗаказыИсходящие.Количество
КОНЕЦ) КАК Количество
ИЗ
РегистрНакопления.ЗаказыИсходящие КАК ЗаказыИсходящие
ГДЕ
ЗаказыИсходящие.ПоДокументу = &ПоДокументу
СГРУППИРОВАТЬ ПО
ЗаказыИсходящие.Номенклатура,
ЗаказыИсходящие.Характеристика
ИМЕЮЩИЕ
СУММА(ВЫБОР
КОГДА ЗаказыИсходящие.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)
ТОГДА ЗаказыИсходящие.Количество
ИНАЧЕ -ЗаказыИсходящие.Количество
КОНЕЦ) <> 0
Этот запрос должен вернуть точно такой же результат, как и запрос к виртуальной таблице Остатки. Если результаты различаются (например, этот запрос показывает ненулевой остаток, а таблица остатков — ничего), то это явный признак следующей проблемы.
Иногда из-за сбоев (аварийное завершение работы сервера, проблемы с электропитанием) таблицы итогов могут повредиться. В этом случае они перестают соответствовать данным в физической таблице, и виртуальные таблицы начинают возвращать неверные данные.
Решение — пересчет итогов.
После пересчета итогов данные в виртуальной таблице должны снова стать корректными. Проверьте результат запроса еще раз.
Еще одна возможная причина — "схлопывание" данных из-за неучтенных измерений. Представьте, что в регистре есть измерение "Склад". Если по заказу было +10 штук на "Основном складе" и -10 штук на "Складе брака", то при запросе остатков без группировки по складам итоговый остаток будет 0. Всегда убеждайтесь, что в вашем запросе или в отборах учтены все аналитические разрезы (измерения), которые важны для правильного расчета остатков.
У каждой записи регистра есть флаг Активность. Если он снят (равен "Ложь"), то запись физически присутствует в таблице движений, но полностью игнорируется при расчете итогов. Соответственно, она никак не влияет на остатки, получаемые через виртуальные таблицы. Это используется, например, для сторнирующих движений или для отложенного проведения. Проверьте, не являются ли "приходные" записи по вашему заказу неактивными.
В заключение, "пропажа" записей из таблицы остатков — это почти всегда не ошибка, а логичное следствие работы механизмов 1С. Ваша задача как разработчика — научиться правильно диагностировать причину, используя мощные инструменты анализа, которые предоставляет платформа, в первую очередь — виртуальную таблицу ОстаткиИОбороты.