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