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