Как исключить движения по определенным документам в отчете по остаткам и оборотам в 1С без искажения остатков?

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

При разработке отчетов в системе 1С:Предприятие программисты часто сталкиваются с необходимостью убрать из ведомости движения по конкретным документам. Например, когда из-за особенностей бизнес-процесса или временных ошибок автоматизации в базу записываются "паразитные" перемещения между складами. Эти документы искусственно завышают обороты (приход и расход), хотя на реальные конечные остатки они могут не влиять.

Попытка решить эту задачу "в лоб" — наложением простого фильтра ГДЕ НЕ Регистратор В (...) к виртуальной таблице ОстаткиИОбороты — приводит к серьезной технической проблеме. Обороты действительно уменьшаются на сумму исключенных документов, но показатели начального и конечного остатков начинают вести себя непредсказуемо. Они многократно увеличиваются в группировках по регистраторам, порождая эффект декартова произведения. Разберем подробно, почему так происходит, и рассмотрим три надежных способа решения этой проблемы.

Почему ломаются остатки при фильтрации регистратора?

Для начала проанализируем причину сбоя. Виртуальная таблица ОстаткиИОбороты устроена сложным образом. База данных физически хранит остатки только на контрольные точки (обычно на начало месяца). Когда мы запрашиваем остатки на произвольную дату или в разрезе регистратора, платформа 1С рассчитывает их динамически. Она берет ближайший сохраненный остаток и математически "докручивает" его, прибавляя и вычитая обороты из таблицы движений.

Если мы применяем условие ГДЕ к регистратору во внешнем запросе, мы грубо вмешиваемся в этот механизм. Платформа отсекает записи регистраторов, нарушая непрерывность временной шкалы. При построении отчета в Системе компоновки данных (СКД) механизм расчета итогов теряет ориентиры периодов. СКД больше не может сопоставить остатки с конкретными датами движений и начинает складывать остатки по каждой детальной записи. Вместо вывода актуального остатка на контрольную точку мы получаем просуммированные остатки за каждый день или секунду, где было хоть какое-то движение.

Рассмотрим, как решить эту проблему правильно.

Решение 1. Правильная настройка ролей полей в СКД (без изменения логики остатков)

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

Чтобы решить эту проблему штатными средствами СКД, нам необходимо обязательно вытащить в запрос поля периодов и правильно настроить их роли. Разберем этот процесс по шагам:

  1. Модернизируем запрос. Нам обязательно нужно выбрать из виртуальной таблицы поле ПериодСекунда (или минимальный период, по которому рассчитываются итоги) и само поле Регистратор. Без поля секундного периода СКД не сможет выстроить правильную хронологическую цепочку для расчета остатков.

Посмотрим на пример правильного запроса для схемы компоновки данных:


ВЫБРАТЬ
    ТоварыНаСкладахОстаткиИОбороты.ПериодСекунда КАК ПериодСекунда,
    ТоварыНаСкладахОстаткиИОбороты.Регистратор КАК Регистратор,
    ТоварыНаСкладахОстаткиИОбороты.Склад КАК Склад,
    ТоварыНаСкладахОстаткиИОбороты.Номенклатура КАК Номенклатура,
    ТоварыНаСкладахОстаткиИОбороты.КоличествоНачальныйОстаток КАК КоличествоНачальныйОстаток,
    ТоварыНаСкладахОстаткиИОбороты.КоличествоКонечныйОстаток КАК КоличествоКонечныйОстаток,
    ТоварыНаСкладахОстаткиИОбороты.КоличествоПриход КАК КоличествоПриход,
    ТоварыНаСкладахОстаткиИОбороты.КоличествоРасход КАК КоличествоРасход
ИЗ
    РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, Авто, , ) КАК ТоварыНаСкладахОстаткиИОбороты
ГДЕ
    НЕ ТоварыНаСкладахОстаткиИОбороты.Регистратор В (&ИсключаемыеДокументы)
  1. Настраиваем роли полей в СКД. Перейдем на вкладку "Набор данных" в схеме компоновки и вручную укажем роли для ключевых полей, чтобы СКД понимала, как рассчитывать итоги:
    • Для поля ПериодСекунда указываем роль Период, тип — Секунда, порядковый номер — 1, а также ставим флаг Обязательное.
    • Для поля Регистратор указываем роль Период, тип — Регистратор, порядковый номер — 2.
    • Для поля КоличествоНачальныйОстаток задаем роль Начальный остаток. В поле "Имя группы" пишем, например, Количество, а в поле "Связь по типу" выбираем ПериодСекунда.
    • Для поля КоличествоКонечныйОстаток задаем роль Конечный остаток. Указываем то же имя группы Количество и ту же связь по типу ПериодСекунда.
  2. Обрабатываем NULL-значения. Если в отчете используются группировки по регистратору, то для корректного отображения итоговых строк (где регистратор отсутствует) в свойствах поля Регистратор в СКД следует установить флаг "Игнорировать значения NULL".

При такой настройке СКД сама распределит движения по временной шкале. Она поймет, что остаток нужно брать на начало секунды и на конец секунды, а исключенные регистраторы просто не будут суммироваться в обороты, при этом не ломая общую цепочку расчета остатков.

Решение 2. Математический расчет остатков (если документы физически искажают остаток)

Бывают ситуации, когда исключаемые документы действительно изменили конечный остаток на складе (например, это были фиктивные списания или необоснованные оприходования), и нам нужно получить картину, как будто этих документов никогда не существовало в базе данных. В этом случае просто скрыть документы в СКД нельзя — остатки на конец периода все равно останутся неверными, так как база хранит их с учетом этих "лишних" документов.

Здесь нам поможет только чистая математика и расчет остатков вручную через временные таблицы по формуле: КонечныйОстаток = НачальныйОстаток + Приход - Расход.

Проанализируем алгоритм решения этой задачи в запросе:

  1. Получаем реальные остатки на дату начала отчета из таблицы Остатки.
  2. Получаем чистые обороты (приход и расход) строго за период отчета из таблицы Обороты, где в параметрах или в секции ГДЕ отсекаем паразитные документы.
  3. Важный нюанс: если исключаемые документы делали движения до начала периода отчета, их влияние уже заложено в реальный начальный остаток из шага 1. Нам необходимо собрать сумму всех движений по этим паразитным документам от "начала времен" до даты начала отчета. Чтобы скорректировать начальный остаток, нам нужно будет вычесть эти движения из реального начального остатка.
  4. Объединяем полученные данные в итоговой таблице и рассчитываем скорректированные показатели:
    • СкорректированныйНачальныйОстаток = РеальныйНачальныйОстаток - ОборотыПаразитныхДокументовДоНачала
    • СкорректированныйКонечныйОстаток = СкорректированныйНачальныйОстаток + ЧистыйПриход - ЧистыйРасход

Рассмотрим пример реализации такого запроса:


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

ОБЪЕДИНИТЬ ВСЕ

// Шаг 2: Получаем чистые обороты за период без учета "паразитных" документов
ВЫБРАТЬ
    ТоварыНаСкладахОбороты.Склад,
    ТоварыНаСкладахОбороты.Номенклатура,
    0,
    ТоварыНаСкладахОбороты.КоличествоПриход,
    ТоварыНаСкладахОбороты.КоличествоРасход
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Обороты(&НачалоПериода, &КонецПериода, Авто, ) КАК ТоварыНаСкладахОбороты
ГДЕ
    НЕ ТоварыНаСкладахОбороты.Регистратор В (&ИсключаемыеДокументы)

ОБЪЕДИНИТЬ ВСЕ

// Шаг 3: Корректировка движений "паразитных" документов до начала периода отчета
ВЫБРАТЬ
    ТоварыНаСкладахОборотыДо.Склад,
    ТоварыНаСкладахОборотыДо.Номенклатура,
    // Вычитаем приходы этих документов и прибавляем их расходы, чтобы "откатить" остаток назад
    -ТоварыНаСкладахОборотыДо.КоличествоПриход + ТоварыНаСкладахОборотыДо.КоличествоРасход,
    0,
    0
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Обороты(, &НачалоПериода, Авто, ) КАК ТоварыНаСкладахОборотыДо
ГДЕ
    ТоварыНаСкладахОборотыДо.Регистратор В (&ИсключаемыеДокументы)
;

////////////////////////////////////////////////////////////////////////////////
// Шаг 4: Итоговый расчет остатков и оборотов
ВЫБРАТЬ
    ВТ_Данные.Склад КАК Склад,
    ВТ_Данные.Номенклатура КАК Номенклатура,
    СУММА(ВТ_Данные.НачальныйОстаток) КАК КоличествоНачальныйОстаток,
    СУММА(ВТ_Данные.Приход) КАК КоличествоПриход,
    СУММА(ВТ_Данные.Расход) КАК КоличествоРасход,
    СУММА(ВТ_Данные.НачальныйОстаток + ВТ_Данные.Приход - ВТ_Данные.Расход) КАК КоличествоКонечныйОстаток
ИЗ
    ВТ_Данные КАК ВТ_Данные
СГРУППИРОВАТЬ ПО
    ВТ_Данные.Склад,
    ВТ_Данные.Номенклатура

Этот метод гарантирует получение математически точных альтернативных остатков. Однако стоит учитывать, что при больших объемах данных за всю историю базы данных расчет корректировок "от начала времен" (Шаг 3) может выполняться достаточно медленно. Настоятельно рекомендуется ограничивать период поиска паразитных документов разумными рамками, например, датой начала ведения учета в системе.

Решение 3. Административно-архитектурный обход проблемы

Иногда лучшим решением технической проблемы является изменение методологии учета. Если бизнес-процесс требует создания документов, которые совершают "виртуальные" перемещения, искажающие реальную картину по складу, стоит локализовать эти движения в учете.

Для этого в системе заводится отдельный виртуальный склад (например, "Буфер комплектации" или "Служебный склад"). Все проблемные перемещения оформляются не внутри основного склада, а между основным и виртуальным складом. В этом случае:

Мы проанализировали три различных подхода к решению проблемы искажения остатков при фильтрации движений. Выбор конкретного метода зависит от масштаба базы данных, архитектуры вашей конфигурации и того, влияют ли исключаемые документы на конечный финансовый и количественный результат склада.

← На главную