Обработка расширением на клиенте



Описываю нетривиальный прием работы с расширением, который позволит относительно быстро реализовывать некоторые обработки данных.

Суть: обработка данных на клиенте с использованием методов, которые реализованы разработчиком конфигурации на форме объекта.

Если эти методы есть вне модуля формы объекта (общий модуль, модуль менеджера, модуль объекта)- лучше сделать обработку более простым способом.

Данный прием помогает в таких ситуациях, когда нужные алгоритмы по обработке единичного объекта уже написаны в его форме разработчиком конфигурации (обработка заполнения при изменении реквизита, заполнение по отдельной кнопке на форме и т.д.) и они достаточно сложные, чтобы тратить время на их разработку и тестирование вне контекста формы.
Несмотря на кажущуюся сложность приема, он сильно экономит время в таких ситуациях.
При наработке этого подхода второй и третий раз сложные вещи можно сделать достаточно быстро.
Разумеется, если основной код уже написан, но не доступен иначе как в форме какого-то объекта.

 

Кейсы: 

  1. загрузили через XML из старой системы в ERP контрагентов, хотим массово заполнить по ИНН,
  2. хотим массово по профилям заполнить группы доступа,
  3. загрузили через XML из старой системы в ERP пользователей, хотим массово создать пользователей ИБ, что бы при этом им были предоставлены доступы согласно настройкам профилей и групп доступа
  4. и так далее

Общий принцип работы

Две формы: списка и объекта, из формы списка вызывается форма объекта, по возвращении в форму списка (после закрытия формы объекта) вызывается форма следующего объекта и так далее. Пр этом на форму объекта передается параметр для обработки. На форме объекта этот параметр при создании на сервере передается в реквизит формы, после открытия через обработчик ожидания выполняется собственно необходимая обработка, она же обращается к процедурам и функциям формы, после заполнения она записывается и закрывается.
Пользователь при выполнении видит, как открываются и закрываются формы. 

На примере кейса "ЗаполнениеПоИНН":
методы, без которых ничего не получится, написаны в форме элемента справочника "Контрагенты", писать их отдельно в своей обработке совсем не хочется, но хочется обратиться к ним. Если бы это была экспортная процедура либо функция из общего модуля, модуля менеджера или хотя бы из модуля какого-то объекта, то проблемы нет: взяли и вызвали этот метод в своей групповой обработке. Но если нужный метод написан в форме объекта, то добраться до него без открытия формы не получится.
Надо соответственно открывать каждую форму и там обращаться к этим методам, а потом записывать и закрывать.
Ниже изложено как это можно реализовать своими руками. 

Разработка по шагам 

Шаг 1: Создание расширения

Создадим отдельное расширение, назовем "Временные доработки", префикс "ВД_".
Лучше отдельное, потому что расширения клиента и менеджер данных (если он у Вас есть) не должны содержать в себе этот сугубо одноразовый функционал, при необходимости после обработки данных или после окончания проекта внедрения это можно за собой удалить. Кроме того созданный инструмент может пригодиться потом где-нибудь еще.
Лично я такие временные обработки "коллекционирую" в одном расширении "временные доработки", но не факт, что это правильный подход.

Шаг 2: Заимствование

Нам надо заимствовать две формы в расширение: форма списка, из которой будет выполняться групповая обработка, форма объекта, в котором есть требуемые методы основной конфигурации.
Чаще всего это формы списка и объекта в одном и том же справочнике (документе и т.д., в тч регистре сведений), но могут быть формы списка и объекта относиться к разным объектам.

Шаг 3: Доработка формы списка

Добавляем команду "ГрупповоеЗаполнение" (или как нравится из контекста задачи, в моем примере "ЗаполнитьПоИНН_Списком"). Команду выводим на командную панель.

У команды клиентский обработчик после (без вызова сервера)

&НаКлиенте
Процедура ВД_ЗаполнитьПоИНН_СпискомПосле(Команда)
МассивПройти = Новый Массив;
Для Каждого ВыделеннаяСтрока Из Элементы.Список.ВыделенныеСтроки Цикл
МассивПройти.Добавить(ВыделеннаяСтрока);
КонецЦикла;
ВД_ЗаполнитьПоИНН_Открыть(Неопределено, МассивПройти);
КонецПроцедуры

В принципе может быть обработка всего справочника, в этом случае обработчик может отличаться и потребовать серверный вызов, в моих задачах подобный подход оправдал себя.

Основной рабочий метод в форме списка "ВД_ОткрытьЗаполнить" (или как в этом примере "ВД_ЗаполнитьПоИНН_Открыть") выполняется так же на клиенте, экспортный.

&НаКлиенте
Процедура ВД_ЗаполнитьПоИНН_Открыть(Результат, СтарыйМассивПройти) Экспорт
Если ТипЗнч(Результат) = Тип("Структура") Тогда
Сообщение = Новый СообщениеПользователю;
ЗаполнитьЗначенияСвойств(Сообщение, Результат);
Сообщение.Сообщить();
КонецЕсли;
МассивПройти = Новый Массив;
ПервыйЕсть = ЛОЖЬ;
Первый = Неопределено;
Для Каждого ВыделеннаяСтрока Из СтарыйМассивПройти Цикл
Если ПервыйЕсть Тогда
МассивПройти.Добавить(ВыделеннаяСтрока);
Иначе
Первый = ВыделеннаяСтрока;
ПервыйЕсть = Истина;
КонецЕсли;
КонецЦикла;
Если НЕ Первый = Неопределено Тогда
ПараметрыОткрытия = Новый Структура("Ключ, ВД_ЗаполнитьПоИНН", Первый, Истина);
ОписаниеОповещения = Новый ОписаниеОповещения(
"ВД_ЗаполнитьПоИНН_Открыть",
ЭтотОбъект,
МассивПройти);
ОткрытьФорму("Справочник.Контрагенты.ФормаОбъекта",
ПараметрыОткрытия,
ЭтотОбъект,
1,
,
,
ОписаниеОповещения,
РежимОткрытияОкнаФормы.БлокироватьОкноВладельца);
Иначе
ПоказатьПредупреждение(,"Заполнение по ИНН завершено" );
КонецЕсли;
КонецПроцедуры

Как работает:
метод работает как бы рекурсивно, через описание оповещения, в самом начале выводит в сообщения только что обработанный объект, выбирает первый элемент массива — его будет обрабатывать, откладывает на следующий вызов оставшуюся часть массива, выполняется, пока массив не закончится.
Можно обрабатывать с конца и оптимизировать.
Главное: при вызове формы объекта передать в нее параметр "ВД_ЗаполнитьЗаписатьЗакрыть" (или "ВД_ЗаполнитьПоИНН" как в моем примере), чтобы сообщить туда о необходимости выполнения обработки, а так же можно передать что-нибудь еще. Можно сделать создание нового, тогда вместо ключа надо передавать параметры заполнения.

Шаг 4: Доработка формы объекта

На форме добавляем параметры и реквизиты, которые мы передаем из списка (не считая предусмотренных разработчиком конфигурации).

При создании на сервере передаем параметры в реквизиты.

При открытии если параметр"ВД_ЗаполнитьЗаписатьЗакрыть" (или "ВД_ЗаполнитьПоИНН" как в моем примере), определяющий необходимость обработки = истина, то через обработчик ожидания с задержкой 0.2 секунды вызвать основной метод, который заполнит нужные поля и обратится к нужным методам формы, после чего запишет объект с передачей ссылки и комментария (для вывода в сообщения)

при создании на сервере переносим параметр в реквизит
 

&НаСервере
Процедура ВД_ПриСозданииНаСервереПосле(Отказ, СтандартнаяОбработка)
Если Параметры.Свойство("ВД_ЗаполнитьПоИНН") Тогда
ВД_ЗаполнитьПоИНН = Параметры.ВД_ЗаполнитьПоИНН;
КонецЕсли;
КонецПроцедуры

при открытии запускаем обработчик ожидания (чтобы наш код выполнился в открытой и полностью инициализированной форме)

&НаКлиенте
Процедура ВД_ПриОткрытииПосле(Отказ)
Если ВД_ЗаполнитьПоИНН Тогда
ПодключитьОбработчикОжидания("ВД_ЗаполнитьПоИНН_Сразу", 0.2, Истина);
КонецЕсли;
КонецПроцедуры

заполнение — основная часть данного расширения, в нем вызываются методы, которые определены только в самой форме (в данном примере нас интересуют методы "ЗаполнениеРеквизитовПоДаннымИННВозможно" и "ЗаполнитьРеквизитыКонтрагентаПоПолученнымДанным", в случае успешного выполнения записываем и закрываем)

//образец взят из процедуры "ЗаполнитьПоИНН(Команда)"
&НаКлиенте
Процедура ВД_ЗаполнитьПоИНН_Сразу()
ИННЗаполненКорректно = Истина;
ТекстСообщения = "";
Если ПустаяСтрока(Объект.ИНН) Тогда
ИННЗаполненКорректно = Ложь;
ВД_ЗаписатьСКомментариемИЗакрыть(Ложь, "Пустой ИНН");
Возврат;
ИначеЕсли НЕ РегламентированныеДанныеКлиентСервер.ИННСоответствуетТребованиям(Объект.ИНН,
ОбщегоНазначенияУТКлиентСервер.ЭтоЮрЛицо(Объект.ЮрФизЛицо),
ТекстСообщения) Тогда
ИННЗаполненКорректно = Ложь;
ВД_ЗаписатьСКомментариемИЗакрыть(Ложь, ТекстСообщения);
Возврат;
КонецЕсли;
Если ЗаполнениеРеквизитовПоДаннымИННВозможно(ИННЗаполненКорректно) Тогда
ОтключитьОбработчикОжидания("ФоновоеЗаданиеПроверитьНаКлиенте");
РеквизитыКонтрагента = ПартнерыИКонтрагентыВызовСервера.ДанныеКонтрагентаПоИНН(
ОбщегоНазначенияУТКлиентСервер.ЭтоЮрЛицо(Объект.ЮрФизЛицо),
Объект.ИНН,
ФоновоеЗаданиеИдентификатор);
Если ЗначениеЗаполнено(РеквизитыКонтрагента.ОписаниеОшибки) Тогда
ВД_ЗаписатьСКомментариемИЗакрыть(Ложь, РеквизитыКонтрагента.ОписаниеОшибки);
Иначе
Если РеквизитыКонтрагента.Свойство("КПП") Тогда
Если Объект.КПП = РеквизитыКонтрагента.КПП Тогда
ЗаполнитьРеквизитыКонтрагентаПоПолученнымДанным(РеквизитыКонтрагента);
ВД_ЗаписатьСКомментариемИЗакрыть(Истина, "Заполнено по ИНН");
Иначе
ВД_ЗаписатьСКомментариемИЗакрыть(Ложь, "Другое значение КПП из реестра");
КонецЕсли;
Иначе
Если Объект.КПП = "" Тогда
ЗаполнитьРеквизитыКонтрагентаПоПолученнымДанным(РеквизитыКонтрагента);
ВД_ЗаписатьСКомментариемИЗакрыть(Истина, "Заполнено по ИНН");
Иначе
ВД_ЗаписатьСКомментариемИЗакрыть(Ложь, "Другое значение КПП из реестра");
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура ВД_ЗаписатьСКомментариемИЗакрыть(Знач Записывать, Комментарий)
Если ПустаяСтрока(Объект.ДополнительнаяИнформация) Тогда
Объект.ДополнительнаяИнформация = Комментарий;
Записывать = Истина;
ИначеЕсли СтрНайти(Объект.ДополнительнаяИнформация, Комментарий) = 0 Тогда
Объект.ДополнительнаяИнформация = Объект.ДополнительнаяИнформация + Символы.ПС + Комментарий;
Записывать = Истина;
КонецЕсли;
Если Записывать Тогда
Записать();
КонецЕсли;
Закрыть(Новый Структура("КлючДанных, Текст", Объект.Ссылка, Строка(Объект.Ссылка) + ": " + Комментарий));
КонецПроцедуры

Результат

открываем список контрагентов, выделяем нужных, запускаем, перед нами постоянно открывается форма контрагента и сразу закрывается, после окончания выводится предупреждение "Заполнение по ИНН завершено", и все объекты с комментариями в форме списка,
после выполнения расширение можно удалить: конфигурация заказчика останется на замке (ну по крайней мере без наших доработок), мы сэкономили время и не разбирались со сложным кодом и при этом получили инструмент, которым можно воспользоваться еще раз.

9 Comments

  1. VmvLer

    осилил.

    субъективно: язык изложения очень костный и путанный

    Reply
  2. EvgenURNN

    (1) спасибо за замечание, добавил раздел с описанием принципа действия в целом, сегондя еще пару раз перечитаю, попробую переписать более понятным языком

    Reply
  3. RocKeR_13
    недокументированный прием работы с расширением

    Заинтриговали, но дальше оказалось, что интриги-то и нет никакой) Скорей хитрости

    Reply
  4. EvgenURNN

    (3) то есть точнее назвать «Хитрый прием работы с расширением»?

    Reply
  5. RocKeR_13

    (4) как вариант) Можно просто опустить слово «недокументированный».

    Reply
  6. mangy

    1. Почему просто с помощью расширения не сделать процедуры формы экспортными и с помощью внешней обработки не обратится к ним после метода Открыть форму?

    2. Для чего в данном случае используется обработчик ожидания?

    Reply
  7. EvgenURNN

    (6)

    мысль интересная,

    я так не пробовал,

    мне тут подкинули еще идею упростить через «ПолучитьФорму» и в ней просто выполнить точно также экспортный метод через расширение,

    Обработчик ожидания чтобы успели отработать все обработчики открытия и чтобы можно было закрыть

    Reply
  8. Yashazz

    Тут уже правильно сказали — можно прямее и проще. А так, имхо, костыль.

    Reply
  9. Rustig

    (0) по идее, у вас групповая обработка объектов — и по идее, групповая обработка не должна зависеть от контекста формы… в принципе….

    …ваш хитрый прием придуман, чтобы не особо разбираться в коде модуля формы?

    часть процедур можно сделать экспортными, вынести в модуль объекта или во внешнюю обработку — но вы почему-то посчитали, что это будет сложнее…

    я не увидел «почему»… можете сложности прояснить?

    а так, метод имеет право на существование….другой взгляд на решение проблем задачи…

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *