Отчеты это слишком просто!
Всем разработчикам, имеющим дело с платформой 1С:Предприятие 8.x, наверняка знаком хотя бы в общих чертах механизм системы компоновки данных (СКД). В большинстве ситуаций он используется для создания отчетов. Общую информацию о назначении и возможностях СКД Вы можете узнать здесь.
В конфигурациях, работающих в режиме управляемого приложения, практически все отчеты создаются с использованием СКД. И это понятно! Используя систему компоновки, мы сокращаем время на разработку отчета, а также даем пользователям относительно большую свободу действий для "игры" с его настройками.
Однако, СКД может быть использована для решения иных задач, не только разработка отчетов. В настоящей статье это и будет продемонстрировано. Материал больше ориентирован на разработчиков, которые только начинают свой путь в мир СКД. Хотя, может и все разработчики найдут здесь что-то интересное.
Пример задачи
В справочнике "Товары" для группы "Группа - 1" и всех входящих в нее элементов необходимо вести историю изменения. Касаться вопроса почему для этого не подошел журнал регистрации не будем. Для решения задачи нужно создать отдельный регистр сведений с измерением "Товар", с типом ссылки на соответствующий справочник, и ресурсом "Ответственный" с типом "СправочникСсылка.Пользователи".
Самый простой путь решения подобной задачи - это создать подписку на событие "ПриЗаписи" справочника и выполнять запись для текущего элемента в описанный ранее регистр сведений. Так, например, выглядела бы добавленная подписка на события и программный код в обработчике подписки.
Процедура ПриЗаписиТовара(Источник, Отказ) Экспорт
// Запрос на проверку вхождения номенклатуры в группу "Группа - 1"
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Товары.Ссылка
|ИЗ
| Справочник.Товары КАК Товары
|ГДЕ
| Товары.Ссылка В ИЕРАРХИИ (&Ссылка)
| И Товары.Ссылка = &ТекущийЭлемент";
// Устанавливаем условие отбора в запросе по соответствующей группе
Запрос.УстановитьПараметр("Ссылка",
// TODO: НИКОГДА! НИКОГДА ТАК НЕ ДЕЛАЙТЕ!
// Поиск по наименованию - последнее дело при разработке :)
// Тут нужно либо брать значение из настроек (константы или другие таблицы),
// либо на крайний случай использовать предопределенные элементы / поиск по GUID
Справочники.Товары.НайтиПоНаименованию("Группа - 1", Истина));
// Устанавливаем отбор по ссылке текущего элемента
Запрос.УстановитьПараметр("ТекущийЭлемент", Источник.Ссылка);
Результат = Запрос.Выполнить();
// Если результат не пустой, тогда номенклатура входит в группу "Группа - 1"
Если НЕ Результат.Пустой() Тогда
ПериодЗаписи = ТекущаяДатаСеанса();
Набор = РегистрыСведений.ИзмененныеТовары.СоздатьНаборЗаписей();
Набор.Отбор.Период.Установить(ПериодЗаписи);
Набор.Отбор.Товар.Установить(Источник.Ссылка);
Запись = Набор.Добавить();
Запись.Период = ПериодЗаписи;
Запись.Товар = Источник.Ссылка;
Запись.Ответственный = Пользователи.ТекущийПользователь();
Набор.Записать();
КонецЕсли;
КонецПроцедуры
Однако у подобной реализации есть большие минусы:
- Мы жестко привязаны к имени группы "Группа - 1". Если пользователь изменит наименование группы, то алгоритм перестанет работать. В листинге есть раздел "TODO" по этому поводу.
- Если завтра пользователь захочет включить наблюдение не только для этой группы или наложить дополнительные условия на реквизиты товаров, то нужно будет снова подключать к работе программиста.
Вот тут то и вступает в бой СКД, предлагая более универсальное решение!
С нуля
В регистр сведений "ИзмененныеТовары" добавим макет с типом "СхемаКомпоновкиДанных". В нем нужно лишь написать запрос к справочнику "Товары" и настроить структуру схемы:
Схема готова! С ее помощью при каждой записи товара мы будем проверять удовлетворяет ли записываемый элемент условиям отбора, заданных пользователем. Настройки отбора задает сам пользователь в режиме 1С:Предприятия, которые сохраняются в константе "НастройкиОтборов" с типом "ХранилищеЗначения".
Для начала рассмотрим как изменится программный код обработчика подписки на событие "ПриЗаписи" справочника "Товары":
Процедура ПриЗаписиТоваровСобытиеПриЗаписи(Источник, Отказ) Экспорт
// Таблица результатов проверки
РезультатПроверки = Новый ТаблицаЗначений;
Настройки = Константы.НастройкиОтборов.Получить().Получить();
Если Настройки = Неопределено Тогда
Возврат;
КонецЕсли;
// Устанавливаем отбор по текущему элементу
ЭлементыОтбора = Настройки.Отбор.Элементы;
ЭлементОтбора = ЭлементыОтбора.Добавить(Тип("ЭЛементОтбораКомпоновкиДанных"));
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
ЭлементОтбора.Использование = Истина;
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Товар");
ЭлементОтбора.ПравоЗначение = Источник.Ссылка;
// Программно делаем вывод результата в таблицу значений
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
СхемаКомпоновкиДанных = РегистрыСведений.ИзмененныеТовары.ПолучитьМакет("ПроверкаСхема");
МакетКомпоновки = КомпоновщикМакета.Выполнить(
СхемаКомпоновкиДанных,
Настройки,,,
Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений")
);
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки);
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ПроцессорВывода.УстановитьОбъект(РезультатПроверки);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
Если РезультатПроверки.Количеств() > 0 Тогда
ПериодЗаписи = ТекущаяДатаСеанса();
Набор = РегистрыСведений.ИзмененныеТовары.СоздатьНаборЗаписей();
Набор.Отбор.Период.Установить(ПериодЗаписи);
Набор.Отбор.Товар.Установить(Источник.Ссылка);
Запись = Набор.Добавить();
Запись.Период = ПериодЗаписи;
Запись.Товар = Источник.Ссылка;
Запись.Ответственный = Пользователи.ТекущийПользователь();
Набор.Записать();
КонецЕсли;
КонецПроцедуры
Теперь проверку мы осуществляем не запросом, а получением данных через СКД. Условия выборки остались практически такими же, как и в первом предложенном варианте решения. Отбор устанавливается по ссылке на текущий элемент справочника "Товары", а также дополняется отборами, которые установил пользователь. Результат СКД выводится в таблицу значений. Если количество записей в ней больше 0, тогда текущий записываемый элемент справочника "Товары" удовлетворяет всем установленным отборам.
Обратите внимание, что при выводе результата СКД в таблицу значений необходимо установить тип генератора макета в компоновщике, а также использовать процессор вывода результат в коллекцию значений.
Пользовательские отборы хранятся в константе "НастройкиОтборов" в виде хранилища значения. Рассмотрим далее как эти настройки были настроены и установлены.
Особенности работы с компоновщиком настроек
Настройки хранятся в константе, поэтому и редактировать их будем в форме констант. Создадим основную форму констант и в качестве реквизита формы добавим компоновщик настроек компоновки данных.
Чтобы компоновщик работал правильно необходимо:
- Инициализировать его для соответствующей схемы компоновки данных.
- Загрузить предыдущие настройки, если они были сохранены ранее.
В результате, имеем почти готовое решение. Посмотрите на следующий листинг:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
СхемаКомпоновки = РегистрыСведений.ИзмененныеТовары.ПолучитьМакет("ПроверкаСхема");
АдресСхемы = ПоместитьВоВременноеХранилище(СхемаКомпоновки, ЭтаФорма.УникальныйИдентификатор);
Компоновщик.Инициализировать(Новый ИсточникДоступныхНастроекКомпоновкиДанных(АдресСхемы));
Настройки = Константы.НастройкиОтборов.Получить().Получить();
Если Настройки <> Неопределено Тогда
Компоновщик.ЗагрузитьНастройки(Настройки);
Иначе
Компоновщик.ЗагрузитьНастройки(СхемаКомпоновки.НастройкиПоУмолчанию);
КонецЕсли;
КонецПроцедуры
Инициализация компоновщика осуществляется при создании формы констант на сервере. В этом же обработчике мы получаем схему компоновки, помещаем ее во временное хранилище и на основе полученного адреса создаем источник доступных настроек компоновки.
Настройки компоновщика берем либо из константы, либо получаем стандартные из схемы компоновки, если ранее они не были сохранены.
При записи констант в событии формы "ПриЗаписиНаСервере" выполняем сохранение настроек компоновщика в константу:
&НаСервере
Процедура ПриЗаписиНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
НастройкиХранилище = Новый ХранилищеЗначения(Компоновщик.ПолучитьНастройки());
Константы.НастройкиОтборов.Установить(НастройкиХранилище);
КонецПроцедуры
После выполнения всех описанных действий пользователь самостоятельно может изменять настройки отборов номенклатуры, для которых нужно вести историю изменений.
Все проще чем кажется.
Вместо заключения
Сегодня в статье мы рассмотрели применение системы компоновки данных для решения задач, не связанных с выводом отчета пользователю. В представленном примере результат был выведен в таблицу значений и далее обработан.
Подобный прием используется, например, в настройках обмена данными с сайтом на 1С:Битрикс в типовой конфигурации "Управление торговлей 11".
Кроме того, СКД используется и в таких механизмах платформы как динамически список. Вы же уже замечали схожесть настроек в отчетах и настройках списков? :)
В 1С:Документооборот по такому же принципу работают произвольные условия в бизнес-процессах, причем весьма эффективно (опять же зависит от того, кто эти условия настраивает). Настройки сегментов для партнеров также использует примерной такой же принцип.
Также очень часто можно увидеть обработки, в шапке которых имеются настройки СКД для установки отборов, а в основной области добавлена таблица значений. Последняя заполняется данными, с учетом этих отборов. Наверное, самая популярная обработка с таким механизмом работы - это "Универсальные подбор и обработка объектов" от Сергея Ожерельева.
Если после прочтения статьи появятся вопросы об оптимальности выполнения кода, то волноваться есть о чем. Если обратиться к нашему примеру, то при записи номенклатуры время, затраченной платформой на получение данных с помощью СКД, составляет всего 0.01 секунды.
В этом случае вроде бы ничего страшного. Но есть пару НО:
- Что, если таких проверочных условий и получения данных с помощью СКД будет не одно, а 100! Время уже значительно вырастет.
- А если пользователь поставит сложный отбор с несколькими "ИЛИ", "Содержит", "В группе", "Не равно" и т.д. Запрос может очень сильно измениться, а его время катастрофически увеличиться.
Таким образом, использование системы компоновки данных позволяет решать задачи не только по созданию различного рода отчетов, но и любые практические задачи, связанные с получением данных. Но нужно быть осторожным, ведь универсальность настроек создает риски для стабильности работы информационной системы.
Подходите к задачам рационально!
Другие ссылки
Особо отметить статьи по СКД от Дмитрия Иванова. Всем рекомендую: