При работе с Системой компоновки данных (СКД) разработчики часто сталкиваются с ситуацией, когда использование стандартного периода ведет себя не так, как ожидается. Типичный сценарий: в отчете есть параметр Период с типом СтандартныйПериод, и на его основе рассчитываются даты начала и окончания. Однако, если пользователь очищает период в настройках, отчет перестает выводить данные, даже если в выражениях параметров прописана логика подстановки текущей даты. Давайте подробно разберем, почему это происходит и как правильно настроить схему.
Разберем внутреннюю механику. Когда мы объявляем параметр &Период, система автоматически создает два скрытых вложенных значения: &Период.ДатаНачала и &Период.ДатаОкончания. Основная сложность заключается в том, что если пользователь не выбрал значение периода (очистил поле), СКД зачастую считает, что связанные с ним выражения вычислять не нужно.
Проанализируем ситуацию: если в выражении параметра Дата2 указано ВЫБОР КОГДА ЗначениеЗаполнено(&Период.ДатаОкончания) ТОГДА &Период.ДатаОкончания ИНАЧЕ ТекущаяДатаСеанса() КОНЕЦ, то при незаполненном периоде система может просто не запустить это вычисление. В итоге в запрос передается либо пустая дата, либо параметр вовсе исключается, что приводит к пустому результату или ошибке "Не задано значение параметра".
Самый эффективный и правильный способ работы с необязательными периодами в СКД — это использование расширения языка запросов (фигурных скобок). Это позволяет системе гибко управлять текстом SQL-запроса в зависимости от того, установлены параметры или нет. Рассмотрим, как правильно оформить условие ГДЕ.
Часто конструктор запроса объединяет условия, но для корректной работы при частичном заполнении (например, заполнена только дата начала) лучше разносить условия вручную:
{ГДЕ
Дата >= &НачалоПериода,
Дата <= &КонецПериода}
Или, если мы хотим использовать синтаксис МЕЖДУ, можно применить следующую конструкцию:
{(Дата МЕЖДУ &НачалоПериода И &КонецПериода) КАК Дата}
Важный момент: если параметры внутри фигурных скобок не будут заполнены пользователем, СКД просто вырежет это условие из итогового запроса. Это именно то поведение, которое обычно требуется — если период не задан, фильтрация по дате не применяется вовсе, и выбираются все данные.
Если нам всё же необходимо, чтобы при пустом периоде подставлялась конкретная дата (например, текущая), мы должны гарантировать вычисление выражений. Посмотрим на алгоритм настройки:
Период (тип СтандартныйПериод), доступный пользователю.НачалоПериода и КонецПериода (тип Дата).КонецПериода прописываем проверку на пустую дату (для СКД это ДАТАВРЕМЯ(1, 1, 1)):
ВЫБОР
КОГДА &Период.ДатаОкончания = ДАТАВРЕМЯ(1, 1, 1)
ТОГДА ТекущаяДатаСеанса()
ИНАЧЕ &Период.ДатаОкончания
КОНЕЦ
В тексте запроса при этом используем обычное условие (без фигурных скобок), если хотим, чтобы оно было обязательным: ГДЕ Дата МЕЖДУ &НачалоПериода И &КонецПериода. Теперь, благодаря флагу "Использовать всегда", расчет сработает даже при пустом значении &Период.
Если логика определения периода слишком сложная для языка выражений СКД, воспользуемся программным методом. Выясним, как перехватить процесс компоновки. Для этого в модуле объекта отчета используется событие ПриКомпоновкеРезультата.
Рассмотрим пример кода, который проверяет заполнение периода и устанавливает значения напрямую в КомпоновщикНастроек:
Процедура ПриКомпоновкеРезультата(ДанныеРасшифровки, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
// Получаем текущие настройки пользователя
Настройки = КомпоновщикНастроек.ПолучитьНастройки();
// Ищем параметр "Период"
ПараметрПериод = Настройки.ПараметрыДанных.Элементы.Найти("Период");
Если ПараметрПериод <> Неопределено И ПараметрПериод.Использование Тогда
ЗначениеПериода = ПараметрПериод.Значение;
// Проверяем, заполнена ли дата окончания
Если Не ЗначениеЗаполнено(ЗначениеПериода.ДатаОкончания) Тогда
// Устанавливаем принудительно конец сегодняшнего дня
КонецПериодаПараметр = Настройки.ПараметрыДанных.Элементы.Найти("КонецПериода");
Если КонецПериодаПараметр <> Неопределено Тогда
КонецПериодаПараметр.Значение = КонецДня(ТекущаяДата());
КонецПериодаПараметр.Использование = Истина;
КонецЕсли;
КонецЕсли;
КонецЕсли;
// Выполняем компоновку с измененными настройками
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки);
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(Результат);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
КонецПроцедуры
Этот способ является максимально надежным, так как мы полностью контролируем передачу параметров в ПроцессорКомпоновкиДанных и исключаем неоднозначность интерпретации пустых полей системой.
Мы проанализировали ситуацию и выяснили, что "пропадание" данных при пустом периоде чаще всего связано с тем, что СКД оптимизирует запрос и не вычисляет выражения, зависящие от незаполненных параметров.
Основные правила для стабильной работы:
{} в запросе для полей, которые могут быть не заполнены.ДАТАВРЕМЯ(1, 1, 1), а не только через ЗначениеЗаполнено.ПриКомпоновкеРезультата.Применяя эти подходы, мы сделаем наши отчеты более гибкими и устойчивыми к действиям пользователя при настройке периодов.