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