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