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