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