В процессе разработки сложных систем уведомлений или механизмов рассылок часто возникает задача: проверить, подходит ли конкретный объект (например, справочник или документ) под условия, которые пользователь задал в настройках. Один из самых гибких способов реализации такого функционала — использование Системы компоновки данных (СКД). Однако программный вывод СКД не в отчет, а в коллекцию значений (таблицу значений) имеет свои нюансы. Разберем подробно, как это реализовать, проанализируем ошибки и рассмотрим оптимальные подходы.
Представим ситуацию: у нас есть справочник с настройками оповещений. В нем хранится отбор (структура ОтборКомпоновкиДанных), который определяет, при каких условиях нужно отправлять уведомление. Нам нужно написать функцию, которая принимает на вход ссылку на объект и эти настройки, а возвращает Истина, если объект соответствует критериям. Самый простой способ проверить это через СКД — сформировать запрос с фильтром по ссылке и наложенным пользовательским отбором. Если в результате вывода в таблицу значений (ТЗ) появилась хотя бы одна строка — объект прошел проверку.
Рассмотрим наиболее распространенный метод, основанный на динамическом формировании схемы компоновки данных. Этот метод хорош тем, что он универсален и позволяет использовать всю мощь языка запросов.
Шаг 1. Создание схемы и набора данных. Сначала нам нужно создать программный объект СхемаКомпоновкиДанных и добавить в него источник данных и набор данных (запрос).
Функция ПроверитьСоответствиеСсылкиОтбору(СсылкаНаОбъект, Отбор) Экспорт
Схема = Новый СхемаКомпоновкиДанных;
// Создаем источник данных
Источник = Схема.ИсточникиДанных.Добавить;
Источник.Имя = "ИсточникДанных1";
Источник.ТипИсточникаДанных = "Local";
// Создаем набор данных - запрос
НовыйНаборДанных = Схема.НаборыДанных.Добавить(Тип("НаборДанныхЗапросСхемыКомпоновкиДанных"));
НовыйНаборДанных.Имя = "НаборДанных1";
НовыйНаборДанных.ИсточникДанных = Источник.Имя;
// Важный момент: автоматически определяем имя таблицы метаданных
ПолноеИмяМетаданных = СсылкаНаОбъект.Метаданные.ПолноеИмя;
НовыйНаборДанных.Запрос = "ВЫБРАТЬ Ссылка ИЗ " + ПолноеИмяМетаданных + " ГДЕ Ссылка = &Ссылка";
// Включаем автозаполнение, чтобы не прописывать поля вручную
НовыйНаборДанных.АвтоЗаполнениеПолей = Истина;
// Получаем настройки по умолчанию
Настройки = Схема.НастройкиПоУмолчанию;
// Шаг 2. Установка параметров и полей выбора
НовыйПараметр = Настройки.ПараметрыДанных.Элементы.Добавить;
НовыйПараметр.Параметр = Новый ПараметрКомпоновкиДанных("Ссылка");
НовыйПараметр.Значение = СсылкаНаОбъект;
НовыйПараметр.Использование = Истина;
ЭлементВыбранныхПолей = Настройки.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
ЭлементВыбранныхПолей.Поле = Новый ПолеКомпоновкиДанных("Ссылка");
// Шаг 3. Копирование пользовательского отбора
// Используем рекурсивную процедуру для копирования групп и элементов отбора
СкопироватьОтборРекурсивно(Настройки.Отбор, Отбор);
// Шаг 4. Инициализация процессоров и вывод в ТЗ
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
ТипГенератора = Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений");
МакетКомпоновки = КомпоновщикМакета.Выполнить(Схема, Настройки, , , ТипГенератора);
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки);
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ТЗ = Новый ТаблицаЗначений;
ПроцессорВывода.УстановитьОбъект(ТЗ);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
Возврат ТЗ.Количество() > 0;
КонецФункции
Разберем процедуру копирования отбора. Поскольку ОтборКомпоновкиДанных может содержать как отдельные элементы, так и группы (И, ИЛИ, НЕ), необходимо обходить его иерархически.
Процедура СкопироватьОтборРекурсивно(Приемник, Источник)
Для Каждого Элемент Из Источник.Элементы Цикл
ТипЭлемента = ТипЗнч(Элемент);
НовыйЭлемент = Приемник.Элементы.Добавить(ТипЭлемента);
ЗаполнитьЗначенияСвойств(НовыйЭлемент, Элемент);
Если ТипЭлемента = Тип("ГруппаЭлементовОтбораКомпоновкиДанных") Тогда
СкопироватьОтборРекурсивно(НовыйЭлемент, Элемент);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Проанализируем ситуацию: если объект уже находится в памяти (например, мы проверяем его в модуле перед записью), выполнение дополнительного запроса к базе данных через НаборДанныхЗапросСхемыКомпоновкиДанных может быть избыточным. В этом случае эффективнее использовать Набор данных - объект.
Рассмотрим, как изменить подход:
НаборДанныхОбъектСхемыКомпоновкиДанных.Это решение работает значительно быстрее на больших объемах данных, так как не обращается к СУБД повторно.
При работе с программным выводом в таблицу значений следует обратить внимание на следующие технические моменты:
1. Автозаполнение полей. В первом сообщении темы автор пытался вручную перебирать колонки и добавлять поля в ТекущийНаборДанных.Поля. Это трудоемкий процесс, в котором легко ошибиться. Использование свойства АвтоЗаполнениеПолей = Истина позволяет системе самостоятельно определить доступные поля из текста запроса. Это критически важно, если пользователь в отборе захочет использовать реквизиты "через точку" (например, Склад.ТипСклада).
2. Генератор макета. Для вывода в ТЗ крайне важно использовать правильный тип генератора макета: ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений. Если использовать стандартный генератор для табличного документа, структура результата может быть искажена группировками.
3. Использование БСП. Если вы работаете в конфигурации на базе БСП (например, ERP, УТ 11 или КА), стоит проверить наличие типовых функций. Рассмотрим программный интерфейс общего модуля КомпоновкаДанныхКлиентСервер. Часто там уже реализованы методы для установки отборов и инициализации компоновщика, что позволяет сократить ваш код в несколько раз.
4. Оптимизация запроса. Если нам нужно просто проверить факт соответствия, в секции ВЫБРАТЬ достаточно указать только Ссылка или даже константу (например, 1 КАК Поле). Выбирать все поля через * не рекомендуется, так как это увеличивает нагрузку на систему при формировании макета.
Проанализируем причины, по которым ТЗ может оставаться пустой, даже если объект должен проходить отбор:
&Ссылка, но в Настройки.ПараметрыДанных значение не передано или Использование установлено в Ложь.ПутьКДанным не совпадает с именами в запросе.Используя предложенные методы, вы сможете надежно реализовать механизм проверки объектов на соответствие произвольным условиям, обеспечив при этом высокую производительность и гибкость настроек для конечного пользователя.