Каждый разработчик на платформе 1С рано или поздно сталкивается с неприятной особенностью Системы компоновки данных (СКД): при одной структуре группировок отчет показывает абсолютно верные данные, но стоит пользователю перетащить группировку (например, переместить «Заказ» или «Счет» выше по иерархии), как в итогах начинают «вылезать» странные, ничем не обоснованные долги или некорректные конечные остатки. При этом простые математические проверки показывают, что физически в регистре таких остатков нет.
Давайте подробно разберем, почему возникает эта проблема, как работает механизм расчета остатков в СКД и как раз и навсегда решить эту задачу стандартными средствами платформы без написания сложных обработок исправления исторических данных.
Для вычисления остатков на разные моменты времени СКД использует специальный внутренний алгоритм компоновки запросов. Когда мы меняем настройки отчета (группировки, отборы, детальные записи), СКД динамически перестраивает результирующий SQL-запрос к СУБД.
Если в схеме компоновки данных допущены ошибки в описании ролей полей или неверно выбрана периодичность виртуальной таблицы, СКД теряет временной контекст. Система перестает понимать, в какой хронологической последовательности шли движения, и вместо правильного расчета по формуле Начальный остаток + Приход - Расход начинает просто суммировать остатки по разным аналитическим разрезам или регистраторам. Это и приводит к двоению итогов и возникновению фантомных долгов.
Рассмотрим по шагам правила и настройки, которые гарантируют абсолютно точный расчет остатков в СКД при любых манипуляциях пользователя со структурой отчета.
Это одна из самых частых ошибок программистов 1С. В запросах к виртуальным таблицам остатков и оборотов часто выбирают поле Регистратор (чтобы показать детальный документ движения), но при этом игнорируют служебные поля периодов.
Запомните правило: если в схеме компоновки данных выбирается поле Регистратор, то в списке выбора обязательно должно присутствовать поле ПериодСекунда. Сам по себе Регистратор не определяет точную позицию на оси времени в рамках одной секунды (в одну секунду может быть записано несколько документов). Именно ПериодСекунда позволяет СКД выстроить однозначную хронологическую цепочку движений.
Даже если вам не нужно выводить поле ПериодСекунда в самом отчете, оно обязательно должно быть выбрано в вашем запросе. Посмотрим на пример правильного построения запроса к регистру накопления:
ВЫБРАТЬ
ВзаиморасчетыОстаткиИОбороты.Контрагент КАК Контрагент,
ВзаиморасчетыОстаткиИОбороты.Заказ КАК Заказ,
ВзаиморасчетыОстаткиИОбороты.ПериодСекунда КАК ПериодСекунда,
ВзаиморасчетыОстаткиИОбороты.Регистратор КАК Регистратор,
ВзаиморасчетыОстаткиИОбороты.СуммаНачальныйОстаток КАК СуммаНачальныйОстаток,
ВзаиморасчетыОстаткиИОбороты.СуммаКонечныйОстаток КАК СуммаКонечныйОстаток,
ВзаиморасчетыОстаткиИОбороты.СуммаПриход КАК СуммаПриход,
ВзаиморасчетыОстаткиИОбороты.СуммаРасход КАК СуммаРасход
ИЗ
РегистрНакопления.Взаиморасчеты.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, Авто, , ) КАК ВзаиморасчетыОстаткиИОбороты
Обратите внимание на третий параметр виртуальной таблицы ОстаткиИОбороты в приведенном выше запросе. Там указано ключевое слово Авто.
Ни в коем случае не указывайте жесткую периодичность (например, День или Месяц), если планируете группировать данные по регистратору или детальным записям. Установка периодичности Авто позволяет СКД самостоятельно и динамически определять необходимый уровень детализации времени в зависимости от того, какие поля выбрал конечный пользователь в настройках компоновщика.
Если вы используете ручной запрос (а не автозаполнение СКД), или если ваш запрос содержит объединения (UNION) и левые соединения, СКД не сможет автоматически определить роли для полей остатков и периодов. Нам необходимо настроить их вручную.
Перейдем на вкладку Наборы данных в конструкторе схемы компоновки данных и заполним свойства полей в колонке Роль:
ПериодСекунда укажем роль Период. В параметрах роли обязательно установим флажок «Обязательное» и зададим порядковый номер периода (для секунды это будет 1, так как это самый детальный период).Регистратор укажем роль Период со значением порядка 2.СуммаНачальныйОстаток зададим роль Остаток. В свойствах роли выберем тип «Начальный», укажем имя группы (например, Сумма) и обязательно заполним связь с оборотными полями и полем конечного остатка.СуммаКонечныйОстаток зададим роль Остаток с типом «Конечный». Укажем точно такое же имя группы Сумма, чтобы СКД понимала, что эти два поля образуют единую пару остатков для расчета.Важное примечание: СКД рассчитывает остатки попарно. Вы всегда должны выбирать в запросе и начальный, и конечный остаток ресурса, даже если пользователю на форме нужен только один из них. Если выбрать только конечный остаток без начального, СКД лишится математической базы для расчета промежуточных итогов по группировкам.
Среди разработчиков часто возникает дискуссия: достаточно ли просто включить флажок Автозаполнение в СКД, чтобы платформа сама решила все проблемы с остатками?
Проанализируем эту ситуацию подробнее. Действительно, при включенном автозаполнении для простых запросов СКД автоматически добавляет скрытые поля периодов и корректно расставляет им роли. Однако на практике реальные отчеты редко строятся по одной таблице. Как только в вашем запросе появляются:
ОБЪЕДИНИТЬ ВСЕ);ГДЕ, наложенные на регистратор или аналитику;механизм автозаполнения СКД начинает сбоить. Поля периодов «теряются», роли сбрасываются или назначаются неверно. Именно поэтому профессиональным стандартом разработки в 1С является явное указание поля ПериодСекунда в запросе и ручной контроль ролей всех полей периодов и остатков в настройках набора данных.
Применяя эти правила, вы гарантируете корректную работу отчета при любых изменениях иерархии группировок, и пользователям больше не придется сталкиваться с неверными конечными остатками.