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