Как исправить некорректные остатки в отчете «Ведомость по товарам в ценах номенклатуры» при совпадении времени документов?

Программист 1С v8.3 (Управляемые формы) 1С:Комплексная автоматизация Управленческий учет Торговля и дистрибуция
← На главную

В конфигурациях «1С:Комплексная автоматизация 2.2» и «1С:ERP 2.2» пользователи часто сталкиваются с неприятной ситуацией: отчет «Ведомость по товарам в ценах номенклатуры» показывает завышенные итоговые суммы. При детальном анализе выясняется, что виной всему — документы, созданные в одну и ту же секунду. Система компоновки данных (СКД), на которой построен отчет, не может однозначно определить, какая запись является последней, и просто суммирует остатки по обоим документам. Разберем по шагам, почему это происходит и как исправить запрос, чтобы отчет работал корректно.

Анализ проблемы: почему двоятся итоги?

Проанализируем стандартный алгоритм работы отчета. В его основе лежит вложенный запрос, который собирает данные из регистров накопления (например, ТоварыНаСкладах). Для расчета конечного остатка в ценах номенклатуры отчет должен сопоставить количество товара на складе с ценой из регистра сведений. Чтобы получить актуальное количество на конец периода, запрос ищет записи с максимальным значением поля Период.

Рассмотрим ситуацию: на одну и ту же дату и время (например, 01.10.2023 12:00:00) введены два приходных ордера. Первый документ фиксирует приход 200 единиц, второй — еще 25 единиц. Логически, конечный остаток должен составлять 225 единиц. Однако СКД во вложенном запросе видит две записи с одинаковым «максимальным периодом» и считает каждую из них конечным состоянием. При расчете ресурса Сумма(КоличествоКонечныйОстаток) система складывает 200 и 225, получая в итоге 425. Это в корне неверно и приводит к расхождениям с другими управленческими отчетами.

Способ 1: Использование максимального регистратора (программное решение)

Самый надежный способ решения — это доработка схемы компоновки данных. Нам необходимо заставить систему выбирать не просто максимальный период, а максимальный регистратор внутри этого периода. Поскольку ссылка на документ в 1С уникальна и имеет внутренний порядок, это позволит однозначно определить «последний» документ в секунде.

Выполним следующие шаги для корректировки запроса в СКД:

  1. Создадим временную таблицу (ВТ), в которой для каждой номенклатуры (и других разрезов, таких как склад или характеристика) найдем МАКСИМУМ(Период).
  2. На основании первой ВТ создадим вторую временную таблицу, где найдем МАКСИМУМ(Регистратор) для тех записей, период которых равен найденному максимуму.
  3. В основном пакете запроса свяжем таблицу остатков с полученной ВТ по полям Номенклатура и Регистратор через ЛЕВОЕ СОЕДИНЕНИЕ.

Рассмотрим пример кода, который реализует эту логику внутри пакета запросов:


// Шаг 1: Получаем максимальный период для каждой номенклатуры
ВЫБРАТЬ
    ТоварыНаСкладах.Номенклатура КАК Номенклатура,
    МАКСИМУМ(ТоварыНаСкладах.Период) КАК МаксимальныйПериод
ПОМЕСТИТЬ ВТ_МаксПериод
ИЗ
    РегистрНакопления.ТоварыНаСкладах КАК ТоварыНаСкладах
ГДЕ
    ТоварыНаСкладах.Период МЕЖДУ &НачалоПериода И &КонецПериода
СГРУППИРОВАТЬ ПО
    ТоварыНаСкладах.Номенклатура;

////////////////////////////////////////////////////////////////////////////////
// Шаг 2: Находим максимальный регистратор для этого периода
ВЫБРАТЬ
    ТоварыНаСкладах.Номенклатура КАК Номенклатура,
    МАКСИМУМ(ТоварыНаСкладах.Регистратор) КАК МаксимальныйРегистратор
ПОМЕСТИТЬ ВТ_РегистраторМаксимум
ИЗ
    РегистрНакопления.ТоварыНаСкладах КАК ТоварыНаСкладах
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_МаксПериод КАК ВТ_МаксПериод
    ПО ТоварыНаСкладах.Номенклатура = ВТ_МаксПериод.Номенклатура
    И ТоварыНаСкладах.Период = ВТ_МаксПериод.МаксимальныйПериод
СГРУППИРОВАТЬ ПО
    ТоварыНаСкладах.Номенклатура;

После этого в результирующем запросе ТаблицаТоварыНаСкладахМаксимальныйПериод должна соединяться с ВТ_РегистраторМаксимум. Это отсечет «промежуточные» документы внутри одной секунды и оставит только одну запись с корректным остатком.

Способ 2: Использование Момента Времени

Более продвинутый метод — использование объекта МоментВремени. Момент времени в 1С включает в себя и дату, и ссылку на документ, что автоматически решает проблему сортировки документов внутри одной секунды. Однако в СКД не всегда удобно работать с объектом МоментВремени напрямую в группировках, поэтому часто прибегают к сравнению ссылок.

Проанализируем, как это можно внедрить: в условиях связи таблиц вместо Период = Период мы можем использовать условие, отсекающее все записи, кроме последней по моменту времени. В современных версиях ERP (начиная с 2.4/2.5) разработчики 1С уже перешли на эту логику, добавив в пакеты запросов выборку по МАКСИМУМ(Регистратор).

Способ 3: Административное решение (без изменения кода)

Если у вас нет возможности редактировать схему отчета (например, конфигурация находится на поддержке и изменения запрещены), можно воспользоваться методическим решением. Проблема возникает только тогда, когда документы имеют идентичное время до секунды.

Выясним, как предотвратить появление таких данных:

  1. Используйте стандартную обработку «Расстановка документов в хронологической последовательности». Она позволяет разнести документы, созданные в одну секунду, увеличив время каждого последующего на 1 секунду.
  2. При ручном вводе документов следите за тем, чтобы складские ордера и накладные не записывались на одно и то же время. Разница даже в 1 секунду полностью устранит проблему дублирования итогов в отчете.
  3. Проверьте настройки обмена данными. Если документы загружаются из внешних систем, убедитесь, что алгоритм загрузки не присваивает всем документам «пустое» время (00:00:00).

Важный нюанс: Стык с ценами номенклатуры

Обратите внимание, что отчет называется «Ведомость в ценах номенклатуры». Это означает, что помимо остатка по регистру ТоварыНаСкладах, запрос обращается к регистру сведений ЦеныНоменклатуры через метод СрезПоследних. Важно помнить: если документ установки цен имеет то же время, что и приходный документ, может возникнуть аналогичная коллизия. Поэтому при исправлении запроса убедитесь, что левое соединение с ценами происходит в самый последний момент, когда таблица остатков уже гарантированно содержит только уникальные записи по номенклатуре.

Для корректного умножения используйте конструкцию ЕСТЬNULL, чтобы избежать пустых значений в итогах:


ЕСТЬNULL(ЦеныНоменклатуры.Цена, 0) * ТаблицаТоварыНаСкладах.КоличествоКонечныйОстаток

Это выражение гарантирует, что даже при отсутствии цены на конкретную дату отчет выведет нулевую сумму, а не ошибку или пустую строку, которая может «сломать» расчет итогов по группировке склада.

Резюме

Мы выяснили, что корень проблемы — в архитектурной особенности СКД, которая не может выбрать единственную запись из нескольких при совпадении периода. Решением является усложнение логики запроса путем добавления группировки по Регистратору. Рассмотрим это как стандарт «хорошего тона» при написании сложных отчетов: всегда предполагайте, что данных может быть больше одного в секунду, и используйте МАКСИМУМ(Регистратор) или МоментВремени для однозначного определения итогов.

← На главную