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