При разработке внешних обработок в среде 1С:Предприятие 8.3 программисты часто сталкиваются с ситуацией, когда необходимо вынести часть настроек или заполнение данных на отдельную (вспомогательную) форму. Однако после ввода данных в дополнительной форме и её закрытия выясняется, что в основной форме реквизиты объекта остались пустыми. Рассмотрим подробнее, почему так происходит, и разберем эффективные способы решения этой задачи.
Проанализируем ситуацию, которая приводит к ошибке. Когда мы работаем с объектом, хранящимся в базе данных (например, документом), открытие любой его формы происходит в контексте одной и той же ссылки. С внешними обработками (файлы .epf) ситуация иная. Если мы открываем дополнительную форму через метод ОткрытьФорму(), указывая путь к форме внешней обработки, система зачастую создает новый экземпляр объекта обработки в оперативной памяти.
Представим этот процесс: в основной форме у вас есть Объект (экземпляр №1). При открытии дополнительной формы система создает еще один Объект (экземпляр №2). Когда вы записываете данные в реквизиты во второй форме, вы меняете состояние экземпляра №2. Как только эта форма закрывается, экземпляр №2 уничтожается, а экземпляр №1 в основной форме так и остается с пустыми полями. Таким образом, работа с Объект.ИмяРеквизита напрямую из разных форм одной обработки не обеспечивает автоматическую синхронизацию данных.
Наиболее правильным и архитектурно верным способом в управляемом приложении является использование стандартного механизма выбора. Рассмотрим этот метод по шагам.
Во вспомогательной форме, после того как пользователь заполнил данные и нажал кнопку "ОК" или "Завершить", мы не просто закрываем форму, а передаем структуру данных назад.
Пример кода в дополнительной форме:
&НаКлиенте
Процедура КомандаОК(Команда)
// Формируем структуру с данными, которые нужно передать
СтруктураДанных = Новый Структура;
СтруктураДанных.Вставить("Склад", Объект.Склад);
СтруктураДанных.Вставить("Контрагент", Объект.Контрагент);
// Закрываем форму и передаем структуру в метод ОповеститьОВыборе
ОповеститьОВыборе(СтруктураДанных);
КонецПроцедуры
Теперь разберем, что нужно сделать в основной форме. Чтобы она смогла "поймать" эти данные, нам нужно вызвать открытие дополнительной формы с указанием владельца и описать обработчик события ОбработкаВыбора.
Пример кода в основной форме:
&НаКлиенте
Процедура ОткрытьДопФорму(Команда)
// Открываем форму, указывая текущую форму как владельца
ПараметрыФормы = Новый Структура;
ОткрытьФорму("ВнешняяОбработка.МояОбработка.Форма.ДопФорма", ПараметрыФормы, ЭтаФорма);
КонецПроцедуры
&НаКлиенте
Процедура ОбработкаВыбора(ВыбранноеЗначение, ИсточникВыбора)
// Проверяем, что к нам пришла именно наша структура
Если ТипЗнч(ВыбранноеЗначение) = Тип("Структура") Тогда
// Заполняем реквизиты объекта основной формы
Объект.Склад = ВыбранноеЗначение.Склад;
Объект.Контрагент = ВыбранноеЗначение.Контрагент;
КонецЕсли;
КонецПроцедуры
Иногда требуется изменять данные в основной форме "на лету", не закрывая дополнительную. В этом случае мы можем обратиться к контексту основной формы через свойство ВладелецФормы. Посмотрим на этот сценарий.
Разберем ситуацию: в дополнительной форме при изменении поля Склад мы хотим сразу обновить его в основной форме. В коде дополнительной формы это будет выглядеть так:
&НаКлиенте
Процедура СкладПриИзменении(Элемент)
// Проверяем наличие владельца и доступность его реквизитов
Попытка
ЭтаФорма.ВладелецФормы.Объект.Склад = Объект.Склад;
Исключение
// Обработка ошибки, если форма открыта без владельца
КонецПопытки;
КонецПроцедуры
Важный нюанс: такой подход создает жесткую связь между формами, что не всегда приветствуется в типовых конфигурациях (например, в УТ 11), но для быстрой разработки внешних инструментов это вполне рабочее решение.
Если из дополнительной формы нужно передать не просто пару значений, а целую таблицу значений или сложный массив данных, лучше использовать ПоместитьВоВременноеХранилище. Проанализируем, как это реализовать.
Адрес.Адрес в основную форму (через тот же ОповеститьОВыборе).Пример кода (серверная часть дополнительной формы):
&НаСервере
Функция ПоместитьДанныеВХранилище()
ТаблицаДанных = Объект.Товары.Выгрузить();
Возврат ПоместитьВоВременноеХранилище(ТаблицаДанных, ЭтаФорма.УникальныйИдентификатор);
КонецФункции
Выясним еще один важный момент. Даже если вы находитесь в рамках одной формы, данные, которые вы видите на экране, живут в РеквизитФормы (обычно это Объект). Если вам нужно передать данные в серверный модуль самой обработки (в процедуру объекта), необходимо выполнить преобразование.
Посмотрим на пример кода на сервере:
&НаСервере
Процедура ВыполнитьДействиеНаСервере()
// Конвертируем данные формы в реальный объект обработки
ОбъектОбработки = РеквизитФормыВЗначение("Объект");
// Теперь мы можем вызвать экспортный метод из модуля объекта
ОбъектОбработки.МояПроцедураРасчета();
// После расчета возвращаем данные обратно в форму, чтобы пользователь увидел результат
ЗначениеВРеквизитФормы(ОбъектОбработки, "Объект");
КонецПроцедуры
Этот механизм критически важен, так как без вызова ЗначениеВРеквизитФормы все изменения, сделанные в коде модуля объекта, не отобразятся в интерфейсе пользователя, и поля будут казаться пустыми.
Подводя итог, можно сказать, что основная ошибка кроется в архитектурном непонимании того, как 1С изолирует контексты разных форм внешней обработки. Чтобы ваша разработка работала корректно, всегда придерживайтесь следующих правил:
ЭтаФорма в качестве параметра Владелец.ОповеститьОВыборе для возврата результата.Объект в форме — это лишь проекция данных, и для работы серверных методов объекта требуется явная конвертация.Применяя эти методы, вы обеспечите надежную передачу данных между любыми формами ваших внешних обработок и избежите проблем с "пустыми реквизитами".