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