При разработке отчетов в системе 1С:Предприятие программисты часто сталкиваются с необходимостью убрать из ведомости движения по конкретным документам. Например, когда из-за особенностей бизнес-процесса или временных ошибок автоматизации в базу записываются "паразитные" перемещения между складами. Эти документы искусственно завышают обороты (приход и расход), хотя на реальные конечные остатки они могут не влиять.
Попытка решить эту задачу "в лоб" — наложением простого фильтра ГДЕ НЕ Регистратор В (...) к виртуальной таблице ОстаткиИОбороты — приводит к серьезной технической проблеме. Обороты действительно уменьшаются на сумму исключенных документов, но показатели начального и конечного остатков начинают вести себя непредсказуемо. Они многократно увеличиваются в группировках по регистраторам, порождая эффект декартова произведения. Разберем подробно, почему так происходит, и рассмотрим три надежных способа решения этой проблемы.
Для начала проанализируем причину сбоя. Виртуальная таблица ОстаткиИОбороты устроена сложным образом. База данных физически хранит остатки только на контрольные точки (обычно на начало месяца). Когда мы запрашиваем остатки на произвольную дату или в разрезе регистратора, платформа 1С рассчитывает их динамически. Она берет ближайший сохраненный остаток и математически "докручивает" его, прибавляя и вычитая обороты из таблицы движений.
Если мы применяем условие ГДЕ к регистратору во внешнем запросе, мы грубо вмешиваемся в этот механизм. Платформа отсекает записи регистраторов, нарушая непрерывность временной шкалы. При построении отчета в Системе компоновки данных (СКД) механизм расчета итогов теряет ориентиры периодов. СКД больше не может сопоставить остатки с конкретными датами движений и начинает складывать остатки по каждой детальной записи. Вместо вывода актуального остатка на контрольную точку мы получаем просуммированные остатки за каждый день или секунду, где было хоть какое-то движение.
Рассмотрим, как решить эту проблему правильно.
Если исключаемые документы оформлялись внутри одного склада (как в случае автора темы, где перемещения шли "по сути по одному складу"), то общего влияния на остаток этого склада они не оказали. Приход по документу равен его расходу. Физический остаток на складе верный, а проблема заключается исключительно в некорректной работе механизма компоновки данных при расчете итогов.
Чтобы решить эту проблему штатными средствами СКД, нам необходимо обязательно вытащить в запрос поля периодов и правильно настроить их роли. Разберем этот процесс по шагам:
ПериодСекунда (или минимальный период, по которому рассчитываются итоги) и само поле Регистратор. Без поля секундного периода СКД не сможет выстроить правильную хронологическую цепочку для расчета остатков.Посмотрим на пример правильного запроса для схемы компоновки данных:
ВЫБРАТЬ
ТоварыНаСкладахОстаткиИОбороты.ПериодСекунда КАК ПериодСекунда,
ТоварыНаСкладахОстаткиИОбороты.Регистратор КАК Регистратор,
ТоварыНаСкладахОстаткиИОбороты.Склад КАК Склад,
ТоварыНаСкладахОстаткиИОбороты.Номенклатура КАК Номенклатура,
ТоварыНаСкладахОстаткиИОбороты.КоличествоНачальныйОстаток КАК КоличествоНачальныйОстаток,
ТоварыНаСкладахОстаткиИОбороты.КоличествоКонечныйОстаток КАК КоличествоКонечныйОстаток,
ТоварыНаСкладахОстаткиИОбороты.КоличествоПриход КАК КоличествоПриход,
ТоварыНаСкладахОстаткиИОбороты.КоличествоРасход КАК КоличествоРасход
ИЗ
РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, Авто, , ) КАК ТоварыНаСкладахОстаткиИОбороты
ГДЕ
НЕ ТоварыНаСкладахОстаткиИОбороты.Регистратор В (&ИсключаемыеДокументы)
ПериодСекунда указываем роль Период, тип — Секунда, порядковый номер — 1, а также ставим флаг Обязательное.Регистратор указываем роль Период, тип — Регистратор, порядковый номер — 2.КоличествоНачальныйОстаток задаем роль Начальный остаток. В поле "Имя группы" пишем, например, Количество, а в поле "Связь по типу" выбираем ПериодСекунда.КоличествоКонечныйОстаток задаем роль Конечный остаток. Указываем то же имя группы Количество и ту же связь по типу ПериодСекунда.Регистратор в СКД следует установить флаг "Игнорировать значения NULL".При такой настройке СКД сама распределит движения по временной шкале. Она поймет, что остаток нужно брать на начало секунды и на конец секунды, а исключенные регистраторы просто не будут суммироваться в обороты, при этом не ломая общую цепочку расчета остатков.
Бывают ситуации, когда исключаемые документы действительно изменили конечный остаток на складе (например, это были фиктивные списания или необоснованные оприходования), и нам нужно получить картину, как будто этих документов никогда не существовало в базе данных. В этом случае просто скрыть документы в СКД нельзя — остатки на конец периода все равно останутся неверными, так как база хранит их с учетом этих "лишних" документов.
Здесь нам поможет только чистая математика и расчет остатков вручную через временные таблицы по формуле: КонечныйОстаток = НачальныйОстаток + Приход - Расход.
Проанализируем алгоритм решения этой задачи в запросе:
Остатки.Обороты, где в параметрах или в секции ГДЕ отсекаем паразитные документы.СкорректированныйНачальныйОстаток = РеальныйНачальныйОстаток - ОборотыПаразитныхДокументовДоНачалаСкорректированныйКонечныйОстаток = СкорректированныйНачальныйОстаток + ЧистыйПриход - ЧистыйРасходРассмотрим пример реализации такого запроса:
// Шаг 1: Получаем реальный начальный остаток
ВЫБРАТЬ
ТоварыНаСкладахОстатки.Склад КАК Склад,
ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
ТоварыНаСкладахОстатки.КоличествоОстаток КАК НачальныйОстаток,
0 КАК Приход,
0 КАК Расход
ПОМЕСТИТЬ ВТ_Данные
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(&НачалоПериода, ) КАК ТоварыНаСкладахОстатки
ОБЪЕДИНИТЬ ВСЕ
// Шаг 2: Получаем чистые обороты за период без учета "паразитных" документов
ВЫБРАТЬ
ТоварыНаСкладахОбороты.Склад,
ТоварыНаСкладахОбороты.Номенклатура,
0,
ТоварыНаСкладахОбороты.КоличествоПриход,
ТоварыНаСкладахОбороты.КоличествоРасход
ИЗ
РегистрНакопления.ТоварыНаСкладах.Обороты(&НачалоПериода, &КонецПериода, Авто, ) КАК ТоварыНаСкладахОбороты
ГДЕ
НЕ ТоварыНаСкладахОбороты.Регистратор В (&ИсключаемыеДокументы)
ОБЪЕДИНИТЬ ВСЕ
// Шаг 3: Корректировка движений "паразитных" документов до начала периода отчета
ВЫБРАТЬ
ТоварыНаСкладахОборотыДо.Склад,
ТоварыНаСкладахОборотыДо.Номенклатура,
// Вычитаем приходы этих документов и прибавляем их расходы, чтобы "откатить" остаток назад
-ТоварыНаСкладахОборотыДо.КоличествоПриход + ТоварыНаСкладахОборотыДо.КоличествоРасход,
0,
0
ИЗ
РегистрНакопления.ТоварыНаСкладах.Обороты(, &НачалоПериода, Авто, ) КАК ТоварыНаСкладахОборотыДо
ГДЕ
ТоварыНаСкладахОборотыДо.Регистратор В (&ИсключаемыеДокументы)
;
////////////////////////////////////////////////////////////////////////////////
// Шаг 4: Итоговый расчет остатков и оборотов
ВЫБРАТЬ
ВТ_Данные.Склад КАК Склад,
ВТ_Данные.Номенклатура КАК Номенклатура,
СУММА(ВТ_Данные.НачальныйОстаток) КАК КоличествоНачальныйОстаток,
СУММА(ВТ_Данные.Приход) КАК КоличествоПриход,
СУММА(ВТ_Данные.Расход) КАК КоличествоРасход,
СУММА(ВТ_Данные.НачальныйОстаток + ВТ_Данные.Приход - ВТ_Данные.Расход) КАК КоличествоКонечныйОстаток
ИЗ
ВТ_Данные КАК ВТ_Данные
СГРУППИРОВАТЬ ПО
ВТ_Данные.Склад,
ВТ_Данные.Номенклатура
Этот метод гарантирует получение математически точных альтернативных остатков. Однако стоит учитывать, что при больших объемах данных за всю историю базы данных расчет корректировок "от начала времен" (Шаг 3) может выполняться достаточно медленно. Настоятельно рекомендуется ограничивать период поиска паразитных документов разумными рамками, например, датой начала ведения учета в системе.
Иногда лучшим решением технической проблемы является изменение методологии учета. Если бизнес-процесс требует создания документов, которые совершают "виртуальные" перемещения, искажающие реальную картину по складу, стоит локализовать эти движения в учете.
Для этого в системе заводится отдельный виртуальный склад (например, "Буфер комплектации" или "Служебный склад"). Все проблемные перемещения оформляются не внутри основного склада, а между основным и виртуальным складом. В этом случае:
Мы проанализировали три различных подхода к решению проблемы искажения остатков при фильтрации движений. Выбор конкретного метода зависит от масштаба базы данных, архитектуры вашей конфигурации и того, влияют ли исключаемые документы на конечный финансовый и количественный результат склада.