В разработке отчетов на системе компоновки данных (СКД) часто возникает задача, когда значение в текущей строке должно зависеть от результата вычислений в предыдущей строке. Классический пример — расчет «входящего» остатка, который равен «исходящему» остатку предыдущего периода или документа. В этой статье мы подробно разберем, как использовать мощную функцию ВычислитьВыражение() для решения подобных задач на примере отчета по расходам и оплатам.
Рассмотрим ситуацию, когда у нас есть три колонки в отчете: ПоступилоРасходов, Оплачено и Неоплачено. Наша цель — реализовать следующую математическую схему:
Неоплачено = ПоступилоРасходов - Оплачено.Проанализируем данные. Если в первой строке мы имели поступление 937 873,61 и оплату 296 283,05, то остаток составит 641 590,56. Этот остаток должен перекочевать в поле поступления следующей строки. Традиционными средствами запроса SQL реализовать такую «шахматку» бывает сложно, поэтому мы обратимся к ресурсам СКД.
Функция ВычислитьВыражение() в СКД позволяет обращаться к группировкам и записям в произвольном порядке. Прежде чем перейти к решению, вспомним основные параметры этой функции:
ВычислитьВыражение(Выражение, Группировка, ОбластьВычисления, Начало, Конец, Сортировка)
Для нашей задачи критически важны параметры Начало и Конец. Они определяют диапазон строк, по которым будет собираться агрегатная функция (например, сумма). Мы можем использовать ключевые слова:
Хотя интуитивно хочется просто взять «значение из предыдущей строки», в СКД более стабильным и правильным методом является расчет через общую сумму от начала. Выясним причину: если мы будем просто ссылаться на предыдущую строку, которая сама является вычисляемой, может возникнуть рекурсия или ошибка последовательности расчета. Надежнее вычитать из общего прихода общую сумму всех предыдущих оплат.
Рассмотрим подробнее формулы для ресурсов. Предположим, у нас есть общий объем поступивших расходов (назовем его НачальныйРасход), который зафиксирован в первой строке или в группировке.
Для поля Оплачено (в контексте входящего остатка) формула будет выглядеть так:
ВычислитьВыражение("Сумма(ПоступилоРасходов)", "ОбщаяГруппировка") - ЕстьNULL(ВычислитьВыражение("Сумма(Оплачено)", , , "Первая", "Предыдущая"), 0)
А для поля Неоплачено (текущий исходящий остаток):
ВычислитьВыражение("Сумма(ПоступилоРасходов)", "ОбщаяГруппировка") - ВычислитьВыражение("Сумма(Оплачено)", , , "Первая", "Текущая")
Проанализируем ситуацию: в первой формуле мы берем общую сумму и вычитаем из нее все оплаты до текущей строки. Это и дает нам «входящий» остаток. Во второй формуле мы вычитаем оплаты включая текущую строку, получая «исходящий» остаток.
Посмотрим на пример настройки ресурсов, который помогает добиться корректного отображения данных, как в сообщениях форума:
ПоступилоРасходов укажите выражение:
ЕстьNull(ВычислитьВыражение("Неоплачено", "ДетальныеЗаписи", , "Предыдущая", "Предыдущая"), Сумма(ПоступилоРасходов))
Здесь мы используем функцию ЕстьNull(), чтобы для самой первой строки, где нет «предыдущей», подставилось исходное значение из запроса.
Неоплачено укажите:
ЕстьNull(ВычислитьВыражение("Неоплачено", "ДетальныеЗаписи", , "Предыдущая", "Предыдущая"), Сумма(ПоступилоРасходов)) - Сумма(Оплачено)
Важное замечание: Чтобы такая схема заработала, необходимо правильно указать второй параметр функции — имя группировки (например, "ДокументОплаты" или "ДетальныеЗаписи"). Если оставить его пустым, расчет будет производиться в контексте текущей строки, что не всегда дает нужный результат при наличии иерархии.
Разберем типичную проблему: первые две записи считаются верно, а далее начинаются ошибки. Это часто происходит из-за сортировки. Функция ВычислитьВыражение() крайне чувствительна к порядку строк. Если в настройках отчета пользователь изменит сортировку (например, по алфавиту вместо даты), логика «предыдущей» строки нарушится.
Чтобы зафиксировать логику расчета, рекомендуется использовать шестой параметр функции — поле сортировки. Например:
ВычислитьВыражение("Сумма(Оплачено)", , , "Первая", "Текущая", "ДатаДокумента Возр")
Это гарантирует, что нарастающий итог будет считаться строго в хронологическом порядке, независимо от того, как строки выведены в самом отчете.
Многие начинающие разработчики пытаются прописать эти формулы в Вычисляемых полях. Однако стоит помнить, что ВычислитьВыражение() наиболее эффективно работает именно в Ресурсах. В вычисляемых полях она выполняется на этапе компоновки результата и имеет доступ к ограниченному набору данных, тогда как в ресурсах она отрабатывает при расчете итогов по группировкам.
Если вам нужно, чтобы расчет велся по детальным записям, убедитесь, что в колонке «Рассчитывать по...» в настройках ресурсов выбраны именно детальные записи или все уровни группировок.
Мы рассмотрели, как реализовать сложную логику переходящих остатков в СКД. Подведем краткий итог по шагам:
ЕстьNULL().Эти методы позволяют создавать интерактивные и сложные финансовые отчеты, не перегружая запрос тяжелыми соединениями таблиц «самих на себя».