Несмотря на то, что объект СхемаЗапроса стал доступен довольно-таки давно (в платформе 8.3.5), коллеги продолжают работать с текстом запроса по старинке как с текстом. Попробуем начать с малого и использовать данный объект на нескольких простых примерах, с которыми мне пришлось столкнуться в реальных задачах.
Предистория: 1С:Комплексная автоматизация, добавлена собственная подсистема обмена с внешней WMS-системой (аутсорсинг складских услуг); одним из распоряжением на отгрузку со склада на аутсорсе является расходный ордер на товары. Обмен с внешней системой протоколируется.
Общая постановка задачи: в форму управления отгрузками товаров с ордерных складов добавить дополнительные колонки (внешний статус отгрузки, наличие проблем обмена информацией, визуализация статуса обмена в виде пиктограммы и т.д.).
В целом задача сводится к 2 этапам:
- Подготовка запроса получения данных по внешней WMS-системе
- Объединение типового запроса динамического списка с запросом из п.1
Пункт первый в целом в рамках данной статьи не интересен: текст подготовлен в обычном конструкторе запросов и возвращается функцией общего модуля. Данные запроса помещаются во временную таблицу ВТ_ДанныеWMS
А вот пункт 2 - то, что нам нужно! Если мы будем работать с текстом запроса как с текстом, а не как с объектом, то мы столкнемся со следующими проблемами:
- При обновлении конфигурации с большей долей вероятности столкнемся с тем, что типовой запрос динамического списка будет изменен и это потребует внесение изменений.
- Если мы захотим объединить запросы, сохранив типовой, то нам потребуется придумать и написать некий парсер текста, чтобы "точечно" вставить нужные конструкции.
- Проблемы варианта "для ленивых" - в конструкторе запросов составить итоговый запрос динамического списка - рассматривать даже не будем)
Давайте посмотрим, что можно сделать, используя схему запроса. Контекст процедуры:
Форма - Обработка.УправлениеОтгрузкой.Форма.Форма
Первый этап - "склейка" текстов запросов и инициализация объекта СхемаЗапроса
ТекстЗапроса = Форма.ОрдераВРаботе.ТекстЗапроса;
ТекстЗапроса = wms_ИнтеграцияСервер.ЗапросВТ_ДанныеОрдераWMS() + ТекстЗапроса;
СхемаЗапроса = Новый СхемаЗапроса;
СхемаЗапроса.УстановитьТекстЗапроса(ТекстЗапроса);
В запросе ЗапросВТ_ДанныеОрдераWMS в последнем пакете идет помещение данных во временную таблицу ВТ_ДанныеWMS. Ссылка на расходный ордер в данной ВТ находится в поле с псевдонимом Документ. Последний пакет итогового запроса - это непосредственно данные для вывода в форму отгрузки. Нам остается связать виртуальную таблицу с основным источником данных списка ордеров в работе и добавить в выбранные поля необходимые данные.
У динамического списка назначена основная таблица - ЖурналДокументов.СкладскиеОрдера. Настраиваем связь между таблицами:
// получаем общее количество пакетов
КоличествоПакетов = СхемаЗапроса.ПакетЗапросов.Количество();
// получаем итоговый пакет запроса, который и будем модифицировать
ИтоговыйПакет = СхемаЗапроса.ПакетЗапросов[КоличествоПакетов-1];
ЗапросВыборка = ИтоговыйПакет.Операторы[0];
// В первоого качестве источника связи выбираем временную таблицу ВТ_ДанныеWMS
ИсточникДанныеWMS = ЗапросВыборка.Источники.Добавить("ВТ_ДанныеWMS", "ВТ_ДанныеWMS");
// В качестве "таблицы-приемника" связи выбираем таблицу с основными данными
ПриемникСвязи = ЗапросВыборка.Источники.НайтиПоИмени("ЖурналДокументов.СкладскиеОрдера");
// Создаем соединение
ИсточникДанныеWMS.Соединения.Добавить(ПриемникСвязи);
// задаем условие связи
ИсточникДанныеWMS.Соединения[0].Условие = Новый ВыражениеСхемыЗапроса(ПриемникСвязи.Источник.Псевдоним+".Ссылка = ВТ_ДанныеWMS.Документ");
// и устанавливаем тип соединения
ИсточникДанныеWMS.Соединения[0].ТипСоединения = ТипСоединенияСхемыЗапроса.ПравоеВнешнее;
В данном примере специально оставил
ИсточникДанныеWMS.Соединения.Добавить(ПриемникСвязи);
чтобы можно было показать, как изменить тип соединения. По умолчанию устанавливается левое внешнее соединение. В приведенном примере, чтобы не изменять ТипСоединения можно было бы поменять источник и приемник связи местами:
ПриемникСвязи.Соединения.Добавить(ИсточникДанныеWMS);
Далее добавляем поля из временной таблицы ВТ_ДанныеWMS и назначаем им нужные псевдонимы. Например, добавим в выборку поле внешнего статуса WMS с псевдонимом СтатусWMS
ПолеСтатус = ЗапросВыборка.ВыбираемыеПоля.Добавить("ВТ_ДанныеWMS.СтатусWMS");
КолонкаПакета = ИтоговыйПакет.Колонки.Найти(ПолеСтатус);
КолонкаПакета.Псевдоним = "СтатусWMS";
Теперь остается переопределить текст запроса динамического списка:
Форма.ОрдераВРаботе.ТекстЗапроса = СхемаЗапроса.ПолучитьТекстЗапроса();
Постановка задачи: нужно добавить динамический список в расширении с произвольным текстом запроса.
Сталкиваемся с тем, что для этого нам в расширение нужно перетянуть кучу объектов, так как на данный момент механизм расширения не позволяет редактировать запросы по объектам основной конфигурации, которые не добавлены в расширение (в 8.3.23 такая возможность, наконец, появилась, но платформа все равно предлагает в расширение подтянуть отсутствующие объекты при окончании редактирования текста запроса)
Обойдем это с помощью полностью программного добавления динамического списка на форму. В приведенном примере параметр Форма имеет тип ФормаКлиентсогоПриложения
// добавляем реквизит формы
МассивРеквизитов = Новый Массив;
ДинамическийСписок = Новый РеквизитФормы("ДинамическийСписок", Новый ОписаниеТипов("ДинамическийСписок"));
МассивРеквизитов.Добавить(ДинамическийСписок);
Форма.ИзменитьРеквизиты(МассивРеквизитов);
// устанавливаем текст запроса динамического списка
Форма.ДинамическийСписок.ПроизвольныйЗапрос = Истина;
Форма.ДинамическийСписок.ТекстЗапроса = Функция_ПолучитьТекстДинамическогоСписка();
ТаблицаНаФорме = Форма.Элементы.Добавить("ДинамическийСписок", Тип("ТаблицаФормы"), <РодительЕслиНеобходимо>);
ТаблицаНаФорме.ПутьКДанным = "ДинамическийСписок";
// инициализируем схему запроса
Схема = Новый СхемаЗапроса;
Схема.УстановитьТекстЗапроса(Форма.ДинамическийСписок.ТекстЗапроса);
// обходим поля выборки и создаем колонки на форме
// пусть пакет запросов состоит из единственного запроса
ТекущийПакет = Схема.ПакетЗапросов[0];
Для Каждого Колонка Из ТекущийПакет.Колонки Цикл
ИмяКолонки = Колонка.Псевдоним;
НовЭлемент = Форма.Элементы.Добавить("ДинамическийСписок"+ИмяКолонки, Тип("ПолеФормы"), ТаблицаНаФорме);
НовЭлемент.Вид = ВидПоляФормы.ПолеВвода;
НовЭлемент.ПутьКДанным = "ДинамическийСписок."+ИмяКолонки;
КонецЦикла;
Данный подход также можно использовать в следующих ситуациях:
- Динамический список необходимо вывести на нескольких различных формах
- Когда необходимо сгенерировать по тексту запроса пустую таблицу значений и заполнить ее, построчно обходя выборку из результата запроса.
В одной из своих публикаций рассматривал использование схем компоновки данных в качестве конструктора получения показателей для расчета премий сотрудников. В некоторый момент накопился уже солидный пул вариантов схем компоновки данных, но потребовалось наложить фиксированные отборы на виртуальную таблицу оборотов регистра накопления ВыручкаИСебестоимостьПродаж во всех схемах. Можно было, конечно, вручную отредактировать все существующие схемы и оповестить на будущее всех разработчиков о необходимости реализации таких отборов, но я человек ленивый и не могу гарантировать, что другие разработчики не забудут об этих ограничениях. Поэтому решил динамически наложить эти отборы непосредственно в коде.
// Добавим, например, условия на склад и номенклатуру
МассивОтборов = Новый Массив;
МассивОтборов.Добавить("НЕ Склад В (&УправленческиеСклады)");
МассивОтборов.Добавить("НЕ АналитикаУчетаНоменклатуры.Номенклатура В (&ИсключаемаяНоменклатура)");
// ТекстЗапроса - текст запроса схемы компоновки данных
// Инициализируем схему
СхемаЗапроса = Новый СхемаЗапроса;
СхемаЗапроса.УстановитьТекстЗапроса(ТекстЗапроса);
// начнем обход запросов пакета
Для Каждого Пакет Из СхемаЗапроса.ПакетЗапросов Цикл
// запрос на уничтожение ВТ пропускаем
Если ТипЗнч(Пакет) = Тип("ЗапросУничтоженияТаблицыСхемыЗапроса") Тогда
Продолжить;
КонецЕсли;
Для Каждого Оператор Из Пакет.Операторы Цикл
Для Каждого Источник Из Оператор.Источники Цикл
// Ищем в источниках виртуальную таблицу РегистрНакопления.ВыручкаИСебестоимостьПродаж.Обороты
Если ТипЗнч(Источник.Источник) <> Тип("ТаблицаСхемыЗапроса") Или Источник.Источник.ИмяТаблицы <> "РегистрНакопления.ВыручкаИСебестоимостьПродаж.Обороты" Тогда
Продолжить;
КонецЕсли;
// Параметры: 0 - НачалоПериода, 1 - КонецПериода, 2 - Периодичность, 3 - Условие
// Добавляем в массив условий отборов существующий отбор
МассивОтборов.Добавить(Строка(Источник.Источник.Параметры[3].Выражение));
// формируем итоговую строку условия отбора
Выражение = Новый ВыражениеСхемыЗапроса(СтрСоединить(МассивОтборов, " И "));
// устанавливаем условие отбора на виртуальную таблицу
Источник.Источник.Параметры[3].Выражение = Выражение;
КонецЦикла;
КонецЦикла;
КонецЦикла;
ТекстЗапроса = СхемаЗапроса.ПолучитьТекстЗапроса();
Жду от вас замечаний, вопросов, комментариев и предложений! Надеюсь, в будущем пополню данную статью и другими примерами использования объекта СхемаЗапроса!
Напоследок, конечно же, хотелось бы выразить огромную благодарность Евгении Карук и ее замечательной статье Объектная модель запроса "Схема запроса" - теория и примеры использования, без которой, предполагаю, потратил бы в свое время не один час на разбор данного механизма платформы!
Что ещё почитать по теме:
Объектная модель запроса "Схема запроса" 2