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