При разработке сервисных и административных обработок на управляемых формах часто возникает задача: предоставить пользователю выбор среди объектов конфигурации (например, всех видов документов или справочников) с помощью флажков, а затем выполнить групповое действие над выбранными типами данных. Разберем по шагам, как правильно организовать получение метаданных, заполнить элемент формы и корректно обработать документы на сервере.
В управляемом приложении коллекция Метаданные доступна только на стороне сервера. Попытка обратиться к ней из клиентских процедур (с директивой &НаКлиенте) вызовет ошибку компиляции или выполнения. Поэтому цикл обхода метаданных обязательно выносится в серверную процедуру.
Важно помнить об ограничении платформы: объекты типа ОбъектМетаданных не сериализуются и не могут передаваться с сервера на клиент. Если попытаться записать в качестве значения элемента списка значений сам объект метаданных, платформа выдаст ошибку передачи контекста при возврате управления на форму. В качестве значения необходимо сохранять строковое имя объекта (Имя), а в пользовательское представление передавать его синоним (Синоним).
Если на управляемой форме уже создан реквизит типа СписокЗначений (например, с именем Результат), то внутри серверной контекстной процедуры &НаСервере к нему можно обращаться напрямую по имени. Использовать дополнительные методы ЗначениеВРеквизитФормы или РеквизитФормыВЗначение в данном случае не требуется, так как контекст формы синхронизируется платформой автоматически.
Рассмотрим реализацию команды заполнения списка. На форме создается команда Заполнить, связанная с клиентским обработчиком, который вызывает серверный контекст.
&НаКлиенте
Процедура Заполнить(Команда)
ЗаполнитьНаСервере();
КонецПроцедуры
&НаСервере
Процедура ЗаполнитьНаСервере()
// Очищаем реквизит формы перед новым заполнением
Результат.Очистить();
// Обходим метаданные документов
Для Каждого МДДокумент Из Метаданные.Документы Цикл
// Проверяем доступность чтения для текущего пользователя
Если ПравоДоступа("Чтение", МДДокумент) Тогда
Результат.Добавить(МДДокумент.Имя, МДДокумент.Синоним);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
После выполнения этой процедуры список на форме будет заполнен строковыми именами документов, а пользователь увидит понятные синонимы с возможностью включения или снятия отметок (флажков).
После того как пользователь отметил нужные виды документов, требуется обойти коллекцию и выполнить целевое действие, например пометить документы на удаление. Разберем два основных подхода к реализации этой задачи.
Вместо ручного формирования динамических запросов можно обратиться к менеджеру конкретного документа через коллекцию Документы, используя строковое имя как индекс. Этот способ делает код компактным и избавляет от необходимости сборки строк запроса.
&НаКлиенте
Процедура ВыполнитьОбработку(Команда)
ВыполнитьНаСервере();
КонецПроцедуры
&НаСервере
Процедура ВыполнитьНаСервере()
Для Каждого ЭлементСписка Из Результат Цикл
// Проверяем, установлен ли флажок у строки списка
Если ЭлементСписка.Пометка Тогда
ИмяДокумента = ЭлементСписка.Значение;
МенеджерДокумента = Документы[ИмяДокумента];
// Получаем выборку всех документов данного типа
Выборка = МенеджерДокумента.Выбрать();
Пока Выборка.Следующий() Цикл
Если НЕ Выборка.ПометкаУдаления Тогда
ДокОбъект = Выборка.Ссылка.ПолучитьОбъект();
// Установка пометки удаления со снятием с проведения
ДокОбъект.УстановитьПометкуУдаления(Истина);
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Если перед получением объектов необходимо применить сложные фильтры (например, отобрать документы только за определенный период или исключить уже помеченные на удаление), целесообразно использовать запрос с динамической подстановкой имени таблицы.
&НаСервере
Процедура ВыполнитьОбработкуЧерезЗапрос()
ШаблонЗапроса =
"ВЫБРАТЬ
| Таб.Ссылка КАК Ссылка
|ИЗ
| Документ.%ИмяТаблицы% КАК Таб
|ГДЕ
| НЕ Таб.ПометкаУдаления";
Для Каждого ЭлементСписка Из Результат Цикл
Если ЭлементСписка.Пометка Тогда
ИмяДокумента = ЭлементСписка.Значение;
Запрос = Новый Запрос;
Запрос.Текст = СтрЗаменить(ШаблонЗапроса, "%ИмяТаблицы%", ИмяДокумента);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
ДокОбъект = Выборка.Ссылка.ПолучитьОбъект();
ДокОбъект.УстановитьПометкуУдаления(Истина);
КонецЦикла;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
УстановитьПометкуУдаления и прямой записью свойства: метод ДокОбъект.УстановитьПометкуУдаления(Истина) для проведенного документа автоматически снимает его с проведения и очищает все движения по регистрам. Если же просто присвоить ДокОбъект.ПометкаУдаления = Истина и вызвать метод Записать(РежимЗаписиДокумента.Запись), документ будет помечен на удаление без отмены движений, что приведет к рассогласованию регистров и остатков в базе.ПравоДоступа("ИнтерактивноеУдалениеПометки", Метаданные.Документы[ИмяДокумента]) или ПравоДоступа("Изменение", ...), чтобы избежать прерывания работы по системной ошибке нарушения прав.ДокОбъект.ОбменДанными.Загрузка = Истина (когда отключение проверок допустимо регламентом).