Почему отчет на СКД с измененным запросом корректно работает только со второго раза?

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

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

Анализ проблемы: жизненный цикл схемы и настроек СКД

Чтобы понять корень проблемы, нужно четко разделять два ключевых объекта в механизме СКД: схему компоновки данных и настройки компоновщика.

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

Проблема возникает из-за их жизненного цикла на форме:

Шаг 1. Инициализация. Когда форма отчета или обработки открывается, КомпоновщикНастроек инициализируется на основе исходной схемы компоновки данных. В этот момент он "запоминает", какие параметры, поля и отборы ему доступны. Например, в исходном запросе еще нет параметров &ДатаПереоценки и &ВидЦены.

Шаг 2. Динамическое изменение схемы. По действию пользователя (например, по установке флага) выполняется код, который меняет текст запроса напрямую в схеме. Давайте посмотрим на пример такого кода:


// Получаем текст запроса из схемы
ТекстЗапроса = СхемаКомпоновкиДанных.НаборыДанных.НаборДанных.Запрос;

// Добавляем к нему новое условие с вложенным запросом
ТекстЗапросаВложенный = "
|ВЫБРАТЬ
|    ЦеныНоменклатуры25.Номенклатура КАК Номенклатура
|ИЗ
|    РегистрСведений.ЦеныНоменклатуры25 КАК ЦеныНоменклатуры25
|ГДЕ
|    НАЧАЛОПЕРИОДА(ЦеныНоменклатуры25.Период, ДЕНЬ) = &ДатаПереоценки
|    И ЦеныНоменклатуры25.ВидЦены = &ВидЦены
|";

ТекстЗапроса = ТекстЗапроса + "
|  И (Номенклатура.Ссылка В (" +  ТекстЗапросаВложенный + "))";
	
// Записываем измененный запрос обратно в схему
СхемаКомпоновкиДанных.НаборыДанных.НаборДанных.Запрос = ТекстЗапроса;

На этом этапе мы изменили "чертеж" (схему), но КомпоновщикНастроек об этом еще не знает. Его внутреннее состояние по-прежнему основано на старой версии схемы, в которой не было новых параметров.

Шаг 3. Первое выполнение. Далее мы пытаемся установить значения новых параметров и выполнить отчет:


Настройки = КомпоновщикНастроек.Настройки;
	
Параметр = Настройки.ПараметрыДанных.Элементы.Найти("ДатаПереоценки");
Параметр.Значение = ДатаПереоценки;
Параметр.Использование = Истина;

Хотя в отладчике мы видим, что значения параметрам присваиваются, для компоновщика макета эти параметры — "фантомы". Он компонует макет, используя новую схему, но со старыми настройками, в которых эти параметры не описаны как доступные. В итоге запрос выполняется без корректно переданных значений параметров &ДатаПереоценки и &ВидЦены, что и приводит к пустому результату.

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

Правильное решение: принудительное обновление настроек

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

Способ 1: Метод `ЗагрузитьНастройки` (Рекомендуемый)

Это самый правильный и семантически верный способ. Метод ЗагрузитьНастройки заставляет компоновщик перечитать метаданные из связанной с ним схемы, обнаружить новые поля и параметры, и после этого применить текущие настройки (сохраненные отборы, сортировки и т.д.).

Посмотрим на полный и правильный алгоритм действий:


// 1. Модифицируем текст запроса в схеме
ТекстЗапроса = СхемаКомпоновкиДанных.НаборыДанных.НаборДанных.Запрос;
ТекстЗапроса = ТекстЗапроса + " |  И (Номенклатура.Ссылка В (" +  ТекстЗапросаВложенный + "))";
СхемаКомпоновкиДанных.НаборыДанных.НаборДанных.Запрос = ТекстЗапроса;

// 2. !!! КЛЮЧЕВОЙ ШАГ: Обновляем компоновщик настроек !!!
// Он перечитает измененную схему и "увидит" новые параметры
КомпоновщикНастроек.ЗагрузитьНастройки(КомпоновщикНастроек.Настройки);

// 3. Теперь можно безопасно устанавливать значения для новых параметров
Настройки = КомпоновщикНастроек.Настройки;
	
ПараметрДата = Настройки.ПараметрыДанных.Элементы.Найти("ДатаПереоценки");
ПараметрДата.Значение = ДатаПереоценки;
ПараметрДата.Использование = Истина;

ПараметрВидЦены = Настройки.ПараметрыДанных.Элементы.Найти("ВидЦены");
ПараметрВидЦены.Значение = ВидЦены;
ПараметрВидЦены.Использование = Истина;

// 4. Выполняем компоновку
// ...

После вызова ЗагрузитьНастройки компоновщик уже знает о существовании &ДатаПереоценки и &ВидЦены, и установка их значений приведет к корректной передаче в запрос.

Способ 2: Использование `ПолучитьНастройки`

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

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

Итоговый алгоритм и рекомендации

Чтобы избежать "магии второго клика" при динамической работе с СКД, всегда придерживайтесь следующего порядка действий:

  1. Программно измените схему компоновки данных (например, текст запроса).
  2. Обязательно вызовите метод КомпоновщикНастроек.ЗагрузитьНастройки(КомпоновщикНастроек.Настройки), чтобы синхронизировать компоновщик с обновленной схемой.
  3. Получите доступ к настройкам и установите значения для новых параметров в коллекции Настройки.ПараметрыДанных.
  4. Выполняйте дальнейшие действия по формированию отчета.

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

← На главную