Как получить значение из предыдущей строки или рассчитать нарастающий итог в СКД 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке отчетов в Системе компоновки данных (СКД) программисты часто сталкиваются с необходимостью производить вычисления, которые выходят за рамки одной текущей строки. Типичные примеры таких задач — расчет нарастающего итога (когда к сумме текущего дня прибавляются суммы всех предыдущих дней) или получение значения из предыдущей строки (например, чтобы узнать разницу курсов валют, динамику изменения цен или остатки на начало периода). В этой статье мы подробно и по шагам разберем, как решать такие задачи элегантно и быстро с помощью встроенных механизмов СКД, не перегружая запрос к базе данных.

Постановка задачи и анализ проблемы

Представим ситуацию, описанную пользователем на форуме. У нас есть регистр накопления (например, продажи или обороты по счетам), из которого мы получаем ежедневные обороты. При обычном выводе детальных записей по дням мы видим следующую картину:

Наша цель — получить нарастающий итог, где значение каждого последующего дня включает в себя сумму всех предыдущих периодов:

Для решения этой задачи в платформе 1С предусмотрена мощная и гибкая функция ВычислитьВыражение() (в англоязычной версии — EvalExpression). Она позволяет производить вычисления над набором данных в контексте группировок, обращаться к предыдущим и последующим записям, а также задавать произвольные границы расчета.


Разбор синтаксиса функции ВычислитьВыражение()

Прежде чем перейти к практической реализации, детально проанализируем синтаксис этой функции. Это поможет понять, как именно система управляет выборкой строк при расчете.

Полный синтаксис функции выглядит следующим образом:

ВычислитьВыражение(Выражение, Группировка, Область, Начало, Конец, Сортировка, Иерархия, Формат)

Рассмотрим ключевые параметры, которые нам понадобятся:

  1. Выражение — строка, содержащая выражение, которое необходимо вычислить. Обычно это агрегатная функция, например, "Сумма(СуммаОборот)" или просто имя поля, если мы хотим получить конкретное значение.
  2. Группировка — строка, указывающая имя группировки, в контексте которой производится расчет. Если оставить этот параметр пустым (передать пустую строку или неопределено), расчет будет выполняться в контексте текущей группировки.
  3. Область — определяет правила поиска строк. Обычно для простых таблиц этот параметр оставляют пустым.
  4. Начало — указывает, с какой строки начинать расчет. Может принимать строковые значения, такие как "Первая", "Текущая", "Предыдущая", "Следующая". Также можно задавать смещение числом (например, -1 или 1).
  5. Конец — указывает строку, на которой вычисления должны завершиться. Принимает те же ключевые слова, что и параметр Начало.

Сценарий 1. Расчет классического нарастающего итога по вертикали

Выясним, как настроить расчет нарастающего итога в СКД по шагам. Для этого мы будем использовать вкладки вычисляемых полей и ресурсов компоновки данных.

Шаг 1. Создание вычисляемого поля

Перейдем на вкладку «Вычисляемые поля» нашей схемы компоновки данных. Добавим новую строку:

Шаг 2. Настройка ресурса

Перейдем на вкладку «Ресурсы». Добавим наше только что созданное поле СуммаНарастающимИтогом в список ресурсов. В колонке «Выражение» напишем следующую формулу:

ВычислитьВыражение("Сумма(СуммаОборот)", , , "Первая", "Текущая")

Проанализируем работу этого выражения:

Шаг 3. Настройка структуры отчета

Перейдем на вкладку «Настройки». Добавим группировку по полю ПериодДень (или любому другому вашему полю периода). В выбранные поля добавим детальное поле СуммаОборот и наше вычисляемое поле-ресурс СуммаНарастающимИтогом. При запуске отчета мы увидим корректно рассчитанный нарастающий итог.


Сценарий 2. Как взять значение именно из предыдущей строки

Теперь рассмотрим другую популярную задачу: нам не нужно суммировать все предыдущие строки, нам нужно получить значение конкретно из предыдущей строки. Например, мы хотим вывести цену предыдущего дня рядом с ценой текущего дня, чтобы рассчитать разницу.

Выполним следующие настройки по шагам:

  1. На вкладке «Вычисляемые поля» добавим новое поле с именем ПредыдущееЗначение.
  2. В колонке «Выражение» для этого поля напишем формулу:

    ВычислитьВыражение("ИмяВашегоПоля", , , "Предыдущая", "Предыдущая")

    *(где ИмяВашегоПоля — это реквизит или ресурс, значение которого нам нужно получить).*

Важный нюанс настройки: Обратите внимание, что мы указали значение "Предыдущая" как для начала диапазона расчета, так и для его конца. Благодаря этому система берет ровно одну строку, стоящую перед текущей. Для самой первой строки отчета, у которой нет предыдущей, функция вернет значение NULL (пустое значение), что является абсолютно корректным поведением и не приведет к дублированию текущего значения в первой строке.


Сценарий 3. Расчет нарастающего итога по горизонтали (в кросс-таблицах)

Ситуация усложняется, когда отчет строится не сверху вниз (вертикальный список дней), а слева направо (кросс-таблица, где периоды выводятся в колонках). В этом случае обычная формула нарастающего итога может отрабатывать некорректно, поскольку по умолчанию вычисления производятся по строкам группировки, а не по колонкам.

Чтобы решить эту задачу, нам необходимо во втором параметре функции явно указать имя группировки колонок, по которой должен идти расчет. Выясним, как это сделать:

  1. Посмотрим в настройках структуры отчета имя группировки колонок (например, это группировка по полю Период, и в структуре она названа так же — Период).
  2. На вкладке «Ресурсы» для нашего поля нарастающего итога укажем формулу:

    ВычислитьВыражение("Сумма(СуммаОборот)", "Период", , "Первая", "Текущая")

Благодаря явному указанию группировки "Период" во втором параметре, СКД поймет, что суммирование нужно вести последовательно по колонкам слева направо, а не по строкам сверху вниз.


Альтернативный вариант: расчет на уровне SQL-запроса

Иногда разработчики пытаются решить задачу нарастающего итога непосредственно в тексте запроса, избегая использования механизмов СКД. Разберем этот подход, проанализируем его достоинства и недостатки.

Для реализации нарастающего итога в запросе применяется классический прием соединения таблицы самой с собой по условию «дата текущей строки больше или равна дате предыдущих строк».

Посмотрим на пример такого запроса:


ВЫБРАТЬ
    Таблица1.Период КАК Период,
    Таблица1.СуммаОборот КАК СуммаЗаДень,
    СУММА(Таблица2.СуммаОборот) КАК СуммаНарастающимИтогом
ИЗ
    РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, День, ) КАК Таблица1
ВНУТРЕННЕЕ СОЕДИНЕНИЕ 
    РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, День, ) КАК Таблица2
    ПО Таблица1.Период >= Таблица2.Period
СГРУППИРОВАТЬ ПО
    Таблица1.Период,
    Таблица1.СуммаОборот
УПОРЯДОЧИТЬ ПО
    Период

Почему этот метод не рекомендуется использовать в СКД?

Проанализируем минусы реализации нарастающего итога через запрос:

Таким образом, использование встроенной функции СКД ВычислитьВыражение() является стандартом разработки в 1С. Этот метод выполняется на уровне процессора компоновки данных в оперативной памяти после получения плоской таблицы результатов из базы данных, что работает в десятки раз быстрее сложных SQL-соединений.


Резюме и рекомендации

Мы рассмотрели все основные аспекты работы с нарастающими итогами и смежными строками в СКД. Подведем итоги:

← На главную