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