При переходе на управляемые формы разработчики часто сталкиваются с непониманием того, как платформа «1С:Предприятие 8» разделяет данные формы и реальные прикладные объекты. Типичный сценарий: попытка использовать стандартную обработку «Выгрузка и загрузка данных XML» в автоматическом режиме через регламентное (фоновое) задание. На этом пути программист неизбежно натыкается на ошибки, связанные с вызовами метода РеквизитФормыВЗначение, «пропаданием» динамических колонок дерева метаданных и аварийным завершением работы фонового процесса.
В этой статье мы подробно разберем внутренние механизмы платформы, выясним, почему динамические данные ведут себя подобным образом, и реализуем полностью рабочую схему программной выгрузки XML на сервере без использования форм.
Для начала проанализируем, почему возникает путаница с методом РеквизитФормыВЗначение. В управляемом приложении форма и ее реквизиты существуют на стороне клиента (и в специальном контексте сервера формы), а прикладные объекты (такие как СправочникОбъект, ДокументОбъект или ВнешняяОбработкаОбъект) живут исключительно на сервере в момент выполнения транзакций или вызова серверных процедур.
Реквизиты формы (например, реквизит Объект типа ДанныеФормыСтруктура) — это лишь «легковесные» проекции реальных объектов. Они содержат только те данные, которые необходимы для отображения в интерфейсе и редактирования пользователем.
Когда нам требуется выполнить сложный алгоритм, написанный в модуле объекта (например, экспортный метод выгрузки), нам необходимо временно воссоздать «тяжелый» прикладной объект на сервере. Для этого и предназначен метод РеквизитФормыВЗначение("Объект"). Работает он по следующему алгоритму:
Объект.После завершения работы с объектом, если мы изменили его данные и хотим отразить изменения на клиенте, мы вызываем обратный метод — ЗначениеВРеквизитФормы(Объект, "Объект"). Он перезаписывает данные формы актуальной информацией из объекта.
Рассмотрим ситуацию, с которой столкнулся автор темы. В обработке выгрузки XML при старте вызывается код:
ОбъектНаСервере = РеквизитФормыВЗначение("Объект");
ОбъектНаСервере.Инициализация();
// Внутри метода Инициализация() выполняется код:
// ДеревоМетаданных.Колонки.Добавить("ЭлементОписания");
ЗначениеВРеквизитФормы(ОбъектНаСервере.ДеревоМетаданных, "Объект.ДеревоМетаданных");
Разработчик ожидает, что при последующем вызове РеквизитФормыВЗначение("Объект") объект вернется со всеми созданными колонками. Однако колонка ЭлементОписания бесследно исчезает. Почему это происходит?
Когда выполняется метод ЗначениеВРеквизитФормы, платформа сопоставляет структуру табличной части или дерева значений объекта со статической структурой реквизитов формы, заданной в конфигураторе. Так как на самой форме в реквизите Объект.ДеревоМетаданных нет колонки с именем ЭлементОписания, платформа просто игнорирует ее при копировании. Данные этой колонки отбрасываются, поскольку форме «некуда» их записать.
При повторном вызове РеквизитФормыВЗначение("Объект") объект инициализируется заново на основе урезанных данных формы. В итоге колонка ЭлементОписания отсутствует, что приводит к ошибкам при попытке обратиться к ней в методе ВыполнитьВыгрузку().
Важное правило: Если вам необходимо динамически добавить колонку в таблицу или дерево значений на управляемой форме, недостаточно просто добавить её в объект. Сначала нужно программно изменить структуру реквизитов самой управляемой формы с помощью метода ИзменитьРеквизиты(), и только после этого выполнять связывание данных.
Регламентные и фоновые задания выполняются на сервере «1С:Предприятия» в изолированных сеансах без участия пользователя. В этом контексте:
РеквизитФормыВЗначение и ЗначениеВРеквизитФормы приведет к критической ошибке выполнения (Run-time error), так как объект типа УправляемаяФорма на сервере в фоновом задании создать невозможно.Отсюда следует очевидный вывод: для запуска выгрузки в регламентном задании нам необходимо полностью отказаться от использования экранных форм и работать напрямую с объектной моделью обработки.
Когда разработчик пытается запустить выгрузку в регламентном задании через модуль объекта следующим кодом:
Инициализация();
ВыполнитьВыгрузку();
он сталкивается с тем, что колонка, которую должен был создать метод Инициализация(), все равно оказывается не создана. Почему так происходит, ведь формы здесь нет?
Дело в том, что типовые обработки (включая «Выгрузка и загрузка данных XML») изначально проектировались как интерактивные инструменты. Внутри процедур инициализации и анализа метаданных очень часто зашиты обращения к интерфейсным элементам. Например:
ПолучитьФорму("ОкноИсполнения");Состояние("Обработано объектов...");Когда этот код начинает выполняться в фоновом задании на сервере, первая же инструкция вроде ПолучитьФорму() или Состояние() вызывает исключение. Программа аварийно прерывает выполнение процедуры Инициализация() на середине. Из-за этого код, создающий колонки дерева метаданных (который обычно расположен ближе к концу процедуры), просто не успевает отработать. Ошибка «колонка не создана» — это лишь симптом того, что инициализация упала по причине обращения к несуществующему UI-контексту.
Разберем по шагам, как правильно переписать логику и запустить автоматическую выгрузку данных.
Откройте модуль объекта вашей обработки «Выгрузка и загрузка данных XML» в конфигураторе. Вам необходимо локализовать и обезвредить все вызовы, которые требуют взаимодействия с пользователем или формой. Для этого используйте инструкции препроцессора или проверку системных условий.
Например, если в коде встречается инициализация индикатора прогресса:
// Было:
ФормаПрогресса = ПолучитьФорму("ОкноИсполнения");
ФормаПрогресса.Открыть();
// Стало:
#Если Клиент Тогда
ФормаПрогресса = ПолучитьФорму("ОкноИсполнения");
ФормаПрогресса.Открыть();
#КонецЕсли
Аналогичным образом оберните все вызовы функций Состояние(), Предупреждение() и любые другие интерактивные методы. Для вывода технической информации в фоновом задании используйте запись в системный журнал регистрации через метод ЗаписьЖурналаРегистрации().
В модуле вашего регламентного задания напишите код, который будет создавать объект обработки программно. Так как мы не открываем форму, мы работаем исключительно в оперативной памяти сервера, и все реквизиты объекта (включая дерево метаданных со всеми динамическими колонками) будут создаваться корректно и без потерь.
Рассмотрим подробный пример реализации выгрузки конкретных документов (например, документов «Реализация товаров и услуг»):
Процедура ВыполнитьВыгрузкуДанныхПоРасписанию() Экспорт
УстановитьПривилегированныйРежим(Истина);
// 1. Создаем объект обработки.
// Если обработка встроена в конфигурацию:
ОбработкаВыгрузки = Обработки.ВыгрузкаЗагрузкаДанныхXML.Создать();
// Если обработка внешняя и хранится на диске или в справочнике:
// ОбработкаВыгрузки = ВнешниеОбработки.Создать(ПутьКФайлуОбработки);
// 2. Настраиваем параметры выгрузки
ОбработкаВыгрузки.ИмяФайлаВыгрузки = "C:\Exchange\ExportData.xml";
ОбработкаВыгрузки.ДатаНачала = НачалоДня(ТекущаяДата() - 86400); // за вчерашний день
ОбработкаВыгрузки.ДатаОкончания = КонецДня(ТекущаяДата());
// 3. Вызываем инициализацию метаданных.
// Поскольку мы убрали интерактивные вызовы, метод отработает на сервере до конца
// и успешно создаст дерево метаданных со всеми необходимыми колонками.
ОбработкаВыгрузки.Инициализация();
// 4. По умолчанию при инициализации признак выгрузки для всех объектов равен 0 (Не выгружать).
// Нам нужно обойти дерево метаданных и включить выгрузку для нужных типов данных.
// Найдем строку, соответствующую документу "РеализацияТоваровУслуг"
ИмяОбъектаМетаданных = "РеализацияТоваровУслуг";
СтрокаДокумента = ОбработкаВыгрузки.ДеревоМетаданных.Строки.Найти(
Метаданные.Документы[ИмяОбъектаМетаданных],
"ОбъектМД",
Истина
);
Если СтрокаДокумента <> Неопределено Тогда
// Устанавливаем признак "Выгружать"
СтрокаДокумента.Выгружать = 1;
// Вызываем экспортный метод обработки для автоматического обновления зависимых объектов (если требуется)
ОбработкаВыгрузки.ОбработкаИзмененияСостоянияВыгружать(СтрокаДокумента);
Иначе
ЗаписьЖурналаРегистрации(
"ВыгрузкаДанныхXML.Регламент",
УровеньЖурналаРегистрации.Предупреждение,
,
,
"Не удалось найти объект метаданных в дереве: " + ИмяОбъектаМетаданных
);
Возврат;
EndIf;
// 5. Запускаем непосредственно процесс выгрузки в файл
Попытка
ОбработкаВыгрузки.ВыполнитьВыгрузку();
ЗаписьЖурналаРегистрации(
"ВыгрузкаДанныхXML.Регламент",
УровеньЖурналаРегистрации.Информация,
,
,
"Выгрузка данных успешно завершена в файл: " + ОбработкаВыгрузки.ИмяФайлаВыгрузки
);
Исключение
ИнформацияОбОшибке = ИнформацияОбОшибке();
ЗаписьЖурналаРегистрации(
"ВыгрузкаДанныхXML.Регламент",
УровеньЖурналаРегистрации.Ошибка,
,
,
"Ошибка при выполнении выгрузки: " + КраткоеПредставлениеОшибки(ИнформацияОбОшибке)
);
КонецПопытки;
КонецПроцедуры
Для реализации стабильного обмена данными в фоновом режиме мы применили важные архитектурные принципы платформы «1С:Предприятие 8»:
РеквизитФормыВЗначение жестко привязан к структуре реквизитов управляемой формы и не подходит для использования в безклиентской серверной среде.Используя данный подход, вы сможете автоматизировать любые интерактивные обработки обмена, обеспечив их быструю и безотказную работу по расписанию.