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