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