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