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