Как программно открыть вторую форму внешней обработки 1С 8.3 и передать в нее данные

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Разработка внешних обработок в управляемом приложении 1С 8.3 имеет ряд специфических особенностей. Одна из наиболее частых задач, с которой сталкиваются программисты — это необходимость открыть вторую (дополнительную) форму обработки и передать в неё данные из первой (основной) формы, а после изменения — вернуть их обратно. На стандартных объектах конфигурации (например, справочниках или документах) эта задача решается относительно просто, так как они имеют постоянную ссылку в базе данных. В случае с внешними обработками ситуация усложняется тем, что они запускаются во временном контексте и не имеют постоянной ссылки.

Если мы попытаемся вызвать открытие второй формы простым указанием пути, платформа создаст абсолютно новый экземпляр объекта обработки, в котором все реквизиты будут пустыми. В этой статье мы подробно разберем, как правильно реализовать этот сценарий, и рассмотрим два основных профессиональных подхода: с использованием системного метода КопироватьДанныеФормы и через Временное хранилище.

В чем кроется главная проблема внешних обработок?

Прежде чем перейти к коду, выясним причину такого поведения платформы 1С. Когда пользователь запускает внешнюю обработку, система регистрирует её в сеансе под уникальным временным именем. Если в коде первой формы написать стандартный вызов:

ОткрытьФорму("ВнешняяОбработка.МояОбработка.Форма.ВтораяФорма");

Платформа не сможет связать этот вызов с текущим запущенным файлом обработки. Вместо этого она попытается инициализировать новый объект. В результате на второй форме реквизит Объект окажется пустым, а данные, которые пользователь уже ввел на первой форме, останутся в контексте первого окна.

Для решения этой проблемы и синхронизации данных между формами на платформе 1С 8.3 применяются два ключевых метода.

Способ 1. Использование метода КопироватьДанныеФормы (полная синхронизация)

Этот способ является наиболее лаконичным и быстрым в реализации. Он отлично подходит, когда структура данных основной и вспомогательной форм полностью идентична (например, обе формы используют один и тот же основной реквизит Объект с одинаковым набором реквизитов и табличных частей).

Рассмотрим по шагам, как реализовать этот алгоритм без использования модальности, в соответствии со стандартами разработки 1С.

Шаг 1. Вызов открытия второй формы из основной формы

В модуле первой (основной) формы мы описываем процедуру открытия. Передаем текущее состояние реквизита Объект через параметры формы, а также настраиваем описание оповещения для обработки возвращаемых данных:


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

Шаг 2. Прием данных во второй форме при создании

В модуле второй формы в обработчике события ПриСозданииНаСервере мы принимаем переданный объект и копируем его данные в локальный реквизит Объект второй формы:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    Если Параметры.Свойство("ОбъектДанные") Тогда
        // Копируем данные из переданного параметра в локальный реквизит формы
        КопироватьДанныеФормы(Параметры.ОбъектДанные, Объект);
    КонецЕсли;
    
КонецПроцедуры

Шаг 3. Возврат измененных данных при закрытии второй формы

После того как пользователь отредактировал данные во второй форме, мы возвращаем их обратно в первую форму при закрытии:


&НаКлиенте
Процедура ЗаписатьИЗакрыть(Команда)
    
    // Передаем измененный Объект в качестве результата закрытия формы
    Закрыть(Объект);
    
КонецПроцедуры

Шаг 4. Обработка возвращенных данных в основной форме

В основной форме срабатывает процедура оповещения, которую мы указали на Шаге 1. Здесь мы принимаем измененный объект и копируем его данные обратно в основную форму:


&НаКлиенте
Процедура ПослеЗакрытияДополнительнойФормы(РезультатЗакрытия, ДополнительныеПараметры) Экспорт
    
    Если РезультатЗакрытия <> Неопределено Тогда
        // Переносим измененные данные назад в форму-владелец
        КопироватьДанныеФормы(РезультатЗакрытия, Объект);
        ПоказатьОповещениеПользователя("Данные успешно обновлены!");
    КонецЕсли;
    
КонецПроцедуры

Важное ограничение метода: Системный метод КопироватьДанныеФормы требует абсолютной идентичности типов и структуры источника и приемника. Если на второй форме отсутствуют какие-либо реквизиты, которые есть на первой (или наоборот), платформа выдаст ошибку несовместимости типов. Что же делать, если структуры форм различаются или нужно передать только часть данных?

Способ 2. Передача выборочных данных через Временное хранилище

Этот способ значительно гибче и безопаснее, когда вам нужно передать во вторую форму лишь определенный набор реквизитов или конкретные строки табличной части. В этом случае мы упаковываем данные в обычную структуру, помещаем во Временное хранилище и передаем между формами только адрес этого хранилища.

Проанализируем этот процесс детально:

Шаг 1. Помещение данных в хранилище и открытие формы

Нам потребуется серверный контекст первой формы для выгрузки данных в структуру и сохранения их во временном хранилище:


&НаСервере
Функция ПодготовитьДанныеДляПередачиНаСервере()
    
    // Выгружаем данные формы в прикладной объект
    ОбъектОбработки = РеквизитФормыВЗначение("Объект");
    
    СтруктураДанных = Новый Структура;
    СтруктураДанных.Вставить("Организация", ОбъектОбработки.Организация);
    СтруктураДанных.Вставить("Комментарий", ОбъектОбработки.Комментарий);
    
    // Выгружаем табличную часть в обычную Таблицу Значений
    СтруктураДанных.Вставить("Товары", ОбъектОбработки.Товары.Выгрузить());
    
    // Помещаем во Временное хранилище с привязкой к УникальномуИдентификатору формы
    // Это гарантирует очистку памяти после закрытия формы
    АдресХранилища = ПоместитьВоВременноеХранилище(СтруктураДанных, УникальныйИдентификатор);
    
    Возврат АдресХранилища;
    
КонецФункции

&НаКлиенте
Процедура ОткрытьФормуЧерезХранилище(Команда)
    
    АдресДанных = ПодготовитьДанныеДляПередачиНаСервере();
    
    ПараметрыФормы = Новый Структура("АдресДанных", АдресДанных);
    Оповещение = Новый ОписаниеОповещения("ПослеЗакрытияВторойФормыХранилища", ЭтотОбъект);
    
    ПутьКФорме = СтрЗаменить(ЭтаФорма.ИмяФормы, "Форма.ИмяОсновнойФормы", "Форма.ИмяВторойФормы");
    
    ОткрытьФорму(ПутьКФорме, ПараметрыФормы, ЭтаФорма, УникальныйИдентификатор, , , Оповещение, РежимОткрытияОкнаФормы.БлокироватьОкноВладельца);
    
КонецПроцедуры

Шаг 2. Получение данных во второй форме

Во второй форме при создании мы извлекаем структуру из временного хранилища и распределяем данные по нужным нам реквизитам, даже если их структура отличается от первой формы:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    Если Параметры.Свойство("АдресДанных") И ЭтоАдресВременногоХранилища(Параметры.АдресДанных) Тогда
        
        Данные = ПолучитьИзВременногоХранилища(Параметры.АдресДанных);
        
        // Заполняем реквизиты второй формы
        Объект.Организация = Данные.Организация;
        Объект.Комментарий = Данные.Комментарий;
        
        // Загружаем табличную часть
        Объект.Товары.Загрузить(Данные.Товары);
        
        // Сохраняем адрес во временном реквизите формы для обратной передачи
        АдресВременногоХранилища = Параметры.АдресДанных;
        
    КонецЕсли;
    
КонецПроцедуры

Шаг 3. Обратная передача данных при закрытии

Перед закрытием второй формы мы перезаписываем данные по тому же адресу временного хранилища и закрываем форму с успешным результатом:


&НаСервере
Процедура СохранитьДанныеВХранилищеНаСервере()
    
    СтруктураОбратно = Новый Структура;
    СтруктураОбратно.Вставить("Организация", Объект.Организация);
    СтруктураОбратно.Вставить("Комментарий", Объект.Комментарий);
    СтруктураОбратно.Вставить("Товары", Объект.Товары.Выгрузить());
    
    ПоместитьВоВременноеХранилище(СтруктураОбратно, АдресВременногоХранилища);
    
КонецПроцедуры

&НаКлиенте
Процедура ЗавершитьРедактирование(Команда)
    
    СохранитьДанныеВХранилищеНаСервере();
    Закрыть(Истина); // Возвращаем Истина как признак того, что данные изменены
    
КонецПроцедуры

Шаг 4. Чтение измененных данных в основной форме

В основной форме в процедуре оповещения мы просто считываем данные из временного хранилища, адрес которого нам уже известен:


&НаКлиенте
Процедура ПослеЗакрытияВторойФормыХранилища(РезультатЗакрытия, ДополнительныеПараметры) Экспорт
    
    Если РезультатЗакрытия = Истина Тогда
        ПринятьИзмененияИзХранилищаНаСервере();
    КонецЕсли;
    
КонецПроцедуры

&НаСервере
Процедура ПринятьИзмененияИзХранилищаНаСервере()
    
    // Получаем обновленную структуру данных
    // АдресДанных должен быть сохранен в реквизите основной формы при вызове
    Данные = ПолучитьИзВременногоХранилища(АдресДанных);
    
    Объект.Организация = Данные.Организация;
    Объект.Комментарий = Данные.Комментарий;
    Объект.Товары.Загрузить(Данные.Товары);
    
КонецПроцедуры

Важнейший нюанс: Как правильно определить имя внешней обработки программно?

Обратим внимание на один критически важный аспект разработки внешних обработок в 1С 8.3. Никогда не прописывайте путь к подчиненным формам жестко (в виде строки вроде "ВнешняяОбработка.МояОбработка.Форма.ФормаНастроек")!

Дело в том, что внешняя обработка может запускаться пользователями в разных режимах:

  1. Через стандартное меню Файл — Открыть (тогда платформа присваивает обработке динамическое имя в сеансе, например, ВнешняяОбработка.ИмяФайла).
  2. Через стандартную библиотеку БСП (механизм Дополнительные отчеты и обработки), где обработке присваивается системный GUID в качестве имени подключения.

Если вы жестко пропишете имя, то при запуске обработки через БСП или файл программа гарантированно выдаст ошибку: «Неизвестный идентификатор формы».

Правильное и универсальное решение: всегда получайте имя подчиненной формы динамически, используя свойство текущей формы ИмяФормы. Поскольку основная и дополнительная формы находятся внутри одного файла, префикс их системного пути всегда совпадает. Мы можем выполнить простую замену имени основной формы на имя вспомогательной формы:


ИмяВторойФормы = СтрЗаменить(ЭтаФорма.ИмяФормы, ".Форма.ФормаОсновная", ".Форма.ФормаДополнительная");

Этот простой прием сделает вашу внешнюю обработку полностью автономной. Она будет стабильно работать на любой конфигурации 1С, независимо от способа её запуска пользователем.

Резюме

Мы подробно разобрали два основных способа передачи данных между формами внешних обработок 1С 8.3 в управляемом приложении. Способ с использованием метода КопироватьДанныеФормы идеален для быстрой и полной синхронизации объектов с одинаковой структурой. Использование Временного хранилища — это более гибкий архитектурный подход, позволяющий передавать выборочные массивы данных без жесткой привязки к структуре метаданных. Применение динамического определения имени формы гарантирует надежную работу обработки в любых режимах запуска.

← На главную