Разработка в среде управляемого приложения 1С предъявляет жесткие требования к разделению контекстов исполнения кода. Часто возникает задача: на сервере создается новый документ, заполняется определенными данными, и нам необходимо открыть его форму на клиенте для редактирования пользователем до того, как этот документ будет физически записан в базу данных. Поскольку у нового документа еще нет ссылки (ДокументСсылка), стандартные методы поиска и открытия по ключевой ссылке здесь не сработают.
В этой статье мы подробно разберем, почему возникает данная проблема, проанализируем жизненный цикл управляемой формы и рассмотрим три основных способа передачи данных незаписанного объекта с сервера на клиент для открытия его формы. Мы выясним, какой из методов является наиболее надежным, а какие таят в себе скрытые «подводные камни».
В тонком и веб-клиенте прикладные объекты (такие как ДокументОбъект.ИмяДокумента) физически не существуют. Клиент оперирует только ссылками или специальными коллекциями данных, такими как ДанныеФормыСтруктура (свойство Объект в модуле формы). Если документ еще не записан, в базе данных нет соответствующей записи, а значит, получить валидную ссылку на него невозможно. Попытки использовать методы поиска, например, поиск по номеру на клиенте, завершатся ошибкой, так как менеджеры документов недоступны на стороне клиента.
Давайте пошагово разберем методы решения этой задачи, начиная от самого рекомендуемого стандарта разработки фирмы «1С» и заканчивая альтернативными вариантами для сложных структур данных.
Это самый чистый и архитектурно верный способ. Его суть заключается в том, что мы не создаем документ на сервере вручную заранее. Вместо этого мы упаковываем все необходимые данные для нового документа в структуру на клиенте и передаем их в стандартный метод ОткрытьФорму через параметр ЗначенияЗаполнения.
Давайте рассмотрим этот процесс на практическом примере.
Шаг 1. Формирование параметров и открытие формы на клиенте:
На стороне клиента (например, в обработке, из которой инициируется создание документа) мы подготавливаем структуру с реквизитами будущего документа:
&НаКлиенте
Процедура СоздатьИОткрытьНовыйДокумент()
// Заполняем структуру простыми типами данных
ПараметрыЗаполнения = Новый Структура;
ПараметрыЗаполнения.Вставить("Организация", Объект.Организация);
ПараметрыЗаполнения.Вставить("Контрагент", Объект.Партнер);
ПараметрыЗаполнения.Вставить("Склад", Объект.СкладПолучатель);
ПараметрыЗаполнения.Вставить("Комментарий", "Создано автоматически из внешней обработки");
// Помещаем структуру параметров в системный параметр "ЗначенияЗаполнения"
ПараметрыФормы = Новый Структура("ЗначенияЗаполнения", ПараметрыЗаполнения);
// Открываем форму нового документа
ОткрытьФорму("Документ.РеализацияТоваровУслуг.ФормаОбъекта", ПараметрыФормы);
КонецПроцедуры
Шаг 2. Обработка данных в модуле объекта документа:
При вызове метода ОткрытьФорму платформа 1С автоматически создает новый объект на сервере и инициирует событие ОбработкаЗаполнения в модуле этого документа. Нам остается лишь обработать переданную структуру:
// Код размещается в Модуле объекта документа
Процедура ОбработкаЗаполнения(ДанныеЗаполнения, СтандартнаяОбработка)
Если ТипЗнч(ДанныеЗаполнения) = Тип("Структура") Тогда
// Заполняем реквизиты документа из переданной структуры
ЗаполнитьЗначенияСвойств(ЭтотОбъект, ДанныеЗаполнения);
КонецЕсли;
КонецПроцедуры
Преимущества этого способа:
Если вам необходимо передать большой объем данных (например, перенести многострочные табличные части со сложной структурой), обычная структура параметров может перегрузить клиент-серверный вызов или усложнить код. В этом случае на помощь приходит Временное хранилище.
Обратите внимание на важное ограничение: напрямую поместить объект типа ДокументОбъект во временное хранилище нельзя, так как этот тип не поддерживает сериализацию и не может быть передан между контекстами. Мы должны сериализовать данные в структуру таблиц и значений.
Давайте разберем этот алгоритм по шагам.
Шаг 1. Подготовка данных на сервере и получение адреса хранилища:
В модуле формы-инициатора (или в общем модуле) мы создаем структуру данных, имитирующую наш документ, и сохраняем ее во временном хранилище:
&НаСервере
Функция ПодготовитьДанныеДокументаНаСервере()
// Создаем структуру для хранения шапки и табличных частей
ДанныеДокумента = Новый Структура;
ДанныеДокумента.Вставить("Организация", Справочники.Организации.НайтиПоРеквизиту("ИНН", "7700000000"));
ДанныеДокумента.Вставить("ВалютаДокумента", Справочники.Валюты.СистемнаяВалюта());
// Формируем таблицу для товаров
ТаблицаТоваров = Новый ТаблицаЗначений;
ТаблицаТоваров.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ТаблицаТоваров.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
ТаблицаТоваров.Колонки.Добавить("Цена", Новый ОписаниеТипов("Число"));
// Заполняем тестовую строку
НоваяСтрока = ТаблицаТоваров.Добавить();
НоваяСтрока.Номенклатура = Справочники.Номенклатура.НайтиПоКоду("000001");
НоваяСтрока.Количество = 10;
НоваяСтрока.Цена = 1500;
ДанныеДокумента.Вставить("Товары", ТаблицаТоваров);
// Помещаем во временное хранилище с привязкой к уникальному идентификатору формы-владельца
Возврат ПоместитьВоВременноеХранилище(ДанныеДокумента, УникальныйИдентификатор);
КонецФункции
Шаг 2. Открытие формы с передачей адреса на клиенте:
Полученный адрес временного хранилища передается как произвольный параметр формы:
&НаКлиенте
Процедура ОткрытьФормуЧерезХранилище()
АдресДанных = ПодготовитьДанныеДокументаНаСервере();
ПараметрыФормы = Новый Структура;
ПараметрыФормы.Вставить("АдресЗаполнения", АдресДанных);
ОткрытьФорму("Документ.РеализацияТоваровУслуг.ФормаОбъекта", ПараметрыФормы);
КонецПроцедуры
Шаг 3. Извлечение данных в модуле создаваемой формы:
В модуле управляемой формы открываемого документа мы перехватываем переданный параметр в обработчике ПриСозданииНаСервере:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Проверяем наличие нашего уникального параметра
Если Параметры.Свойство("АдресЗаполнения") Тогда
ДанныеИзХранилища = ПолучитьИзВременногоХранилища(Параметры.AddressЗаполнения);
Если ДанныеИзХранилища <> Неопределено Тогда
// Заполняем реквизиты шапки
ЗаполнитьЗначенияСвойств(Объект, ДанныеИзХранилища);
// Загружаем табличную часть "Товары"
Если ДанныеИзХранилища.Свойство("Товары") Тогда
Объект.Товары.Загрузить(ДанныеИзХранилища.Товары);
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Иногда разработчики используют обходной путь: создают форму программно на клиенте через метод ПолучитьФорму, затем выполняют серверный вызов для заполнения прикладного объекта, конвертируют его в данные формы с помощью функции ЗначениеВДанныеФормы и возвращают обратно на клиент, где выполняют метод КопироватьДанныеФормы(ПолученныеДанные, Форма.Объект).
Давайте проанализируем, почему этот подход считается «плохим тоном» разработки и почему его крайне не рекомендуется применять в типовых конфигурациях (таких как ЗУП 3.1, ERP 2, КА 2, УТ 11).
При вызове метода ПолучитьФорму() платформа сразу же выполняет на сервере процедуру ПриСозданииНаСервере для этой формы. На этот момент времени объект внутри формы еще пуст. В сложных конфигурациях в этой процедуре выполняется колоссальный объем работы:
Когда вы после этого принудительно копируете заполненный объект в форму через КопироватьДанныеФормы, данные в полях формы обновятся, но интерфейс останется прежним! Все динамические элементы, кнопки и колонки, которые должны были скрыться или отобразиться на основании заполненных реквизитов, останутся в состоянии «пустого документа», поскольку процедура ПриСозданииНаСервере повторно не вызывается. Это приводит к рассинхронизации отображения интерфейса и реальных данных объекта.
Если вы открываете несколько незаписанных документов программно (например, в цикле обработки данных), важно помнить о правиле уникальности окон в платформе «1С:Предприятие».
Если вы не укажете уникальный идентификатор при открытии новой формы, платформа посчитает, что вы хотите открыть одну и ту же форму нового документа, и каждое последующее открытие будет просто затирать или активизировать уже открытое ранее окно. Чтобы открыть несколько отдельных окон для разных незаписанных документов, обязательно передавайте четвертый параметр в метод ОткрытьФорму:
&НаКлиенте
Процедура ОткрытьНесколькоДокументов(МассивПараметров)
Для Каждого ТекущиеПараметры Из МассивПараметров Цикл
ПараметрыФормы = Новый Структура("ЗначенияЗаполнения", ТекущиеПараметры);
// Генерация Нового УникальногоИдентификатора заставляет платформу открыть отдельное окно
ОткрытьФорму("Документ.НашДокумент.ФормаОбъекта",
ПараметрыФормы,
,
Новый УникальныйИдентификатор);
КонецЦикла;
КонецПроцедуры
Подводя итог нашему анализу, мы можем выделить ключевые правила работы с незаписанными объектами в управляемых формах:
ЗначенияЗаполнения и перехвату в процедуре ОбработкаЗаполнения). Это гарантирует стабильную работу интерфейса формы и логики типовых конфигураций.ПриСозданииНаСервере.КопироватьДанныеФормы для инициализации новых документов, чтобы не нарушать естественный жизненный цикл инициализации интерфейса управляемой формы.