gifts2017

Внесение изменений в обработки событий полей формы, без проблем с дальнейшей поддержкой

Опубликовал Максим Лёвочкин (frutty) в раздел Программирование - Практика программирования

Сталкивались ли Вы с необходимостью дополнить, либо полностью изменить реакцию формы на события в стандартных конфигурациях? Испытывали ли Вы проблемы в дальнейшем сопровождении такой конфигурации? Предлагаю ознакомиться и обсудить моё решение данной проблемы.

Проблема.

Программисты 1С с недавних пор начали заботиться о тех, кто дорабатывает их конфигурации и обновляет их в дальнейшем, а именно добавили модуль "МодификацияКонфигурацииПереопределяемый", в которой добавили процедуру "ПриСозданииНаСервере", чтобы можно было дополнить форму своими данными, либо что-то поменять. Они также добавили процедуры "НоменклатураПриИзмененииПереопределяемый" и "ХарактеристикаПриИзмененииПереопределяемый", чтобы можно было как-то реагировать на смену номенклатуры и характеристики, без необходимости корректировки модуля формы, но это всё-таки ограниченный список, а иногда необходимо реагировать и на другие события. Что же делать тогда? Как добавить свой код на определённые события и не иметь проблем при дальнейшем обновлении?

Моё решение.

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

Процедура ПриСозданииНаСервере(Форма, Отказ, СтандартнаяОбработка) Экспорт
	Если Форма.ИмяФормы = "Документ.РеализацияТоваровУслуг.Форма.ФормаДокумента" Тогда
		ПриСозданииНаСервереРеализацияТоваровУслугФормаФормаДокумента(Форма, Отказ, СтандартнаяОбработка);  
	КонецЕсли;	
КонецПроцедуры

Процедура ПриСозданииНаСервереРеализацияТоваровУслугФормаФормаДокумента(Форма, Отказ, СтандартнаяОбработка)
	
	ДОП_ОбработкаСобытийПолейФормы.Подключить_ПриИзменении(Форма, "Партнер");
	ДОП_ОбработкаСобытийПолейФормы.Подключить_ПриИзменении(Форма, "Организация");
	
КонецПроцедуры

Теперь можно добавлять модуль, в котором будут подменяться родные обработыки событий нашими, назвал я его "ДОП_ОбработкаСобытийПолейФормы"

// Добавляет обработку события "ПриИзменении" для выбранного элемента, либо для всех подходящих элеметнов формы
//
// Параметры:
//  Форма - УправляемаяФорма - Форма, в которой необходимо добавить событие
//                 на изменение элемента.
//  ИмяПоляФормы - Строка - Имя элемента для которого необходимо добавить событие
//                 на изменение. 
//                 Если значение пустое, то событие добавляется для всех подходящих элементов.
//
&НаСервере
Процедура Подключить_ПриИзменении(Форма, ИмяПоляФормы = "") Экспорт

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

	ДобавитьРеквизитДляРодныхОбработок(Форма);
	
	Если ИмяПоляФормы <> "" И Форма.Элементы.Найти(ИмяПоляФормы) <> Неопределено И ПодходящийЭлементПриИзменении(Форма.Элементы[ИмяПоляФормы]) Тогда
		РоднаяОбработкаСобытия = Форма.Элементы[ИмяПоляФормы].ПолучитьДействие("ПриИзменении");
		Если РоднаяОбработкаСобытия <> "" Тогда
			Форма["ДОП_РодныеОбработкиСобытийПолейФормы"].Добавить(РоднаяОбработкаСобытия, ИмяПоляФормы+"ПриИзменении");
		КонецЕсли;
		Форма.Элементы[ИмяПоляФормы].УстановитьДействие("ПриИзменении","ДОП_ПолеФормыПриИзменении");
	ИначеЕсли ИмяПоляФормы = "" Тогда
		Для каждого Элемент Из Форма.Элементы Цикл
			Если ПодходящийЭлементПриИзменении(Элемент) Тогда
				РоднаяОбработкаСобытия = Элемент.ПолучитьДействие("ПриИзменении");
				Если РоднаяОбработкаСобытия <> "" Тогда
					Форма["ДОП_РодныеОбработкиСобытийПолейФормы"].Добавить(РоднаяОбработкаСобытия, Элемент.Имя+"ПриИзменении");
				КонецЕсли;
				Элемент.УстановитьДействие("ПриИзменении","ДОП_ПолеФормыПриИзменении");
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;

КонецПроцедуры // Подключить_ПриИзменении(Форма, ИмяПоляФормы = "")

// Проверяет возможность добавить обработку события "ПриИзменении" для элемента
//
// Параметры:
//  Элемент - Любой - Проверяемый элемент формы.
// Возвращаемое значение:
//	Булево - Истина, если элемент формы подходящий, Ложь - в противном случае.
//
&НаСервере
Функция ПодходящийЭлементПриИзменении(Элемент)

	Возврат ТипЗнч(Элемент) = Тип("ПолеФормы") ИЛИ ТипЗнч(Элемент) = Тип("ТаблицаФормы");

КонецФункции // ПодходящийЭлементПриИзменении(Элемент)

// Проверяет и в случае отсутствия добавляет новый реквизит формы,
// который хранит наименование "родных" процедур для элементов формы
//
// Параметры:
//  Форма - УправляемаяФорма - Форма, в которой необходимо добавить новый реквизит формы.
//
&НаСервере
Процедура ДобавитьРеквизитДляРодныхОбработок(Форма)

	МассивРеквизитов = Форма.ПолучитьРеквизиты();
	Счётчик = МассивРеквизитов.Количество();
	Пока Счётчик > 0 Цикл
		РеквизитФормы = МассивРеквизитов[Счётчик-1];
		Если РеквизитФормы.Имя = "ДОП_РодныеОбработкиСобытийПолейФормы" Тогда
			Возврат;
		КонецЕсли;
		Счётчик = Счётчик-1;
	КонецЦикла;
	
	ДобавляемыеРеквизиты = Новый Массив;
	ДобавляемыеРеквизиты.Добавить(Новый РеквизитФормы("ДОП_РодныеОбработкиСобытийПолейФормы", Новый ОписаниеТипов("СписокЗначений")));
	Форма.ИзменитьРеквизиты(ДобавляемыеРеквизиты);
	
КонецПроцедуры // ДобавитьРеквизитДляРодныхОбработок(Форма)                                     

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

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

Чтобы всё заработало, необходимо добавить в модуль изменяемой формы (в моём случае, это форма документа реализации товаров и услуг) следующий код:

&НаКлиенте
Процедура ДОП_ПолеФормыПриИзменении(Элемент)

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

	КонецЕсли;

КонецПроцедуры

Всё. Теперь если нам необходимо добавить свой код для события "ПриИзменении" поля формы, в нашем модуле "ДОП_МодификацияФорм" в процедуре "ПриСозданииНаСервереРеализацияТоваровУслугФормаФормаДокумента" добавить:

ДОП_ОбработкаСобытийПолейФормы.Подключить_ПриИзменении(Форма); // для всей формы
ДОП_ОбработкаСобытийПолейФормы.Подключить_ПриИзменении(Форма, "Партнер"); // только для элемента "Партнер"

Аналогично можно поступить и с другими событиями полей формы, таких как: НачалоВыбора, НачалоВыбораИзСписка, Очистка, Регулирование, Открытие, Создание, ОбработкаВыбора, ИзменениеТекстаРедактирования, ПередНачаломДобавления и прочее...

Желания

Было бы неплохо, если бы программисты из 1С разрешили бы нам устанавливать обработчик события из общих модулей, тогда бы не пришлось вообще снимать с поддержки "чужие" формы и модули. Но пока этого не случилось, буду искать пути наименьшего сопротивления.

См. также

Подписаться Добавить вознаграждение
Комментарии
1. cmd_vasec (cmd_vasec) 19.05.15 15:17
Добрый день.
Я бы использовал новый функционал "Расширения".
Rustig; AnyBody; Yashazz; frutty; kit; fancy; +6 Ответить 1
2. Максим Лёвочкин (frutty) 19.05.15 15:31
(1) cmd_vasec, спасибо за ссылку, видимо я не в тренде, не знал о такой функциональности. Но свой механизм я бы не сбрасывал со щитов, т.к. "Расширения" реализованы только в 8.3.6, а многие фирмы с 8.2 ещё соскочить не могут.
3. Андрей (h00k) 19.05.15 18:12
(2) frutty, забавно, все что вы описали, давно и успешно применяется без каких либо изменений модулей, пусть они хоть трижды "переопределяемые". Хотя, конечно же, спасибо разработчикам, за эту, пусть и не востребованную, возможность. Про новинку - "расширения" в 8.3.6 вам сразу сказали, а вот про подписку на событие "ОбработкаПолученияФормы", появившуюся в 8.2, видимо забыли сообщить.

П.С.: Своим общим модулям лучше давать более осмысленные имена, содержащие тип объекта в названии, такие как "допМодификацияФормСправочники" или "допМодификацияФормЖурналы".

П.П.С.: Через подписку "ОбработкаПолученияФормы" можно "подменять" формы почти всех объектов и даже заменять сами объекты на свои, например заменив типовой отчет на свой или подменив ту же форму списка справочника номенклатура на свою форму в обработке.
Yashazz; Zahader; Dach; kit; fancy; Fox-trot; +6 1 Ответить 3
4. Максим Лёвочкин (frutty) 20.05.15 09:03
(3) h00k, проблема была не в визуальном изменении формы, а в добавлении своего кода в событие полей формы, а этого не сделать, без добавления процедур в модуль этой формы, что в свою очередь добавляет проблем при обновлении...
Либо я опять чего-то не знаю =)
5. Валерий К (klinval) 20.05.15 09:35
(4) frutty,
Ответ на твой вопрос см тут. На самом деле 100% меня устраивающего способа решения подобной задачи я не нашёл.
6. Максим Лёвочкин (frutty) 20.05.15 13:12
(5) klinval, ответа не увидел. Проблема у меня заключалась в том, что мне необходимо было при выборе "Партнёра" в документах "Заказ клиента", "РТиУ", "Акт выпол. работ" и др., проверять полноту заполнения анкеты партнёра и в случае не полных сведений, предлагать пользователю, дополнить их. При это минимизировать геморрой при обновлении.
7. Иван Иванчиков (Dr.ZIG) 21.05.15 04:42
Сам давно пользуюсь программным добавлением элементов формы, реквизитов формы и переопределением типовых обработчиков, для удобства обновления. Только непонятна фраза:
Программисты 1С с недавних пор начали заботиться о тех, кто дорабатывает их конфигурации и обновляет их в дальнейшем, а именно добавили модуль "МодификацияКонфигурацииПереопределяемый", в которой добавили процедуру "ПриСозданииНаСервере"

Куда программисты 1С добавили этот модуль? У меня такого нет, специально заглянул в несколько типовых конфиг, в частности в последнюю бухгалтерию 3.0 и там этого модуля нет.
Сам я давно использую публикацию http://infostart.ru/public/182443/ от 2013 года, адаптировав под управляемые формы. Также использую статью по добавлению предопределённых элементов не изменяя типовых справочников http://1clancer.ru/article/predopredelennye_elementy..._ne_izmenyaya_tipovuyu_konfiguratsiyu_687
8. Валерий К (klinval) 21.05.15 11:41
(6) frutty,
Как я понимаю в вашей конфе 1С-ники в добавили модуль "МодификацияКонфигурацииПереопределяемый" и в каждой форме "ПриСозданииНаСервере" вызывают процедуру из этого модуля. Если это так - да ваш способ поможет избежать гемора при обновлении.
Но тем у кого нет "МодификацияКонфигурацииПереопределяемый" ваша статья становится бессмысленной, т.к. для реализации вашего способа необходимо всё-равно изменить в форме "ПриСозданииНаСервере" (что повлечёт небольшие трудности при обновлении) от изменения которого вы и пытались уйти.

В (7) правильный вопрос задали: куда программисты 1С добавили этот модуль? Что у вас за конфа?

Вы описали метод как универсальный для всех конфигураций 1с (в характеристиках значится: "Конфигурация:Все для 1C:8"), что является неправдой. Вам необходимо в статье и в характеристиках статьи подправить данную информацию. Тогда недопониманий в комментариях станет поменьше
9. Артем Целовальников (slazzy) 21.05.15 13:57
(8),(7)
Добавили они как минимум в ERP 2.0 и в УТ11. Вероятно в остальные добавят тоже
10. Петр Базелюк (pbazeliuk) 25.05.15 16:24
(3) h00k, "ОбработкаПолученияФормы" - не всегда отрабатывает, перенаправить вызов обработчика события на общий модуль в таком случае нельзя.

(2) frutty, у вас есть проблема, которая может вызвать рекурсию, если используется множественная подмена обработчика события разными не связанными подсистемами (как пример, серийные номера, расчет даты отгрузки, бонусирование товара и т.д.). У меня была статья про переопределения http://infostart.ru/public/169131/, но она уже безнадежно устарела...
11. Андрей (h00k) 25.05.15 17:36
(10) pbazeliuk,
"ОбработкаПолученияФормы" - не всегда отрабатывает

Да, есть такое, приходилось изворачиваться и лепить костыли. Надеюсь это в прошлом, сейчас переносим все подобные доработки в расширения.
12. Ирина progr-2008 Санкт-Петербург (progr-2008) 28.05.15 23:37
Вроде бы в УПП этого модуля нет.
13. Андрей (h00k) 29.05.15 04:07
(12) progr-2008, Он есть только в ERP 2.0 и его производных, таких как УТ 11 и КА 2...
14. г. Казань Рустем Гумеров (Rustig) 14.06.15 23:47
(3) все, что вы написали верно, но не имеет значения в рамках конкретной задачи, описанной в посте (6)
многие бы решали задачу проще: переопределили бы подписку на событие ПередЗаписью(), в которой проверяли бы заполненность анкеты партнера, задавая вопрос о желании заполнить незаполненные данные
15. Ирина progr-2008 Санкт-Петербург (progr-2008) 04.07.15 22:00
16. Максим Лёвочкин (frutty) 05.07.15 01:37
(14) Rustig, да, но в конкретно моей ситуации, реквизиты заполняются в момент общения, и удобнее спросить дополнительные сведения у партнера во время "знакомства", но никак не в момент сохранения полностью, либо частично заполненного документа.
17. Ирина progr-2008 Санкт-Петербург (progr-2008) 05.07.15 22:16
(16) frutty, тогда другое событие - например, ПриОткрытии.
18. Максим Лёвочкин (frutty) 06.07.15 14:48
(17) progr-2008, при открытии чего Вы собираетесь дополнять сведения? При открытии формы документа? Или формы списка справочника? Видимо Вы не вникли в проблему, либо я вовсе не понимаю того, что Вы пишите.