Как скопировать реквизит типа ХранилищеЗначения при копировании документа в управляемых формах 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке на платформе 1С:Предприятие 8.3 программисты часто сталкиваются с ситуацией, когда при интерактивном копировании документа данные в реквизитах с типом ХранилищеЗначения бесследно исчезают. Кажется, что код в модуле объекта написан верно, отладчик показывает заполненное хранилище, но как только открывается форма нового документа — данные превращаются в Неопределено. Разберем подробно, почему это происходит и как правильно реализовать копирование таких данных.

Почему данные теряются: анализируем механику платформы

Прежде чем перейти к написанию кода, выясним причину такого поведения системы. Основная проблема кроется в архитектуре управляемого приложения. Реквизиты объекта с типом ХранилищеЗначения не поддерживаются структурой ДанныеФормы. Это проектное решение разработчиков платформы 1С, принятое для оптимизации трафика между сервером и клиентом. Поскольку в хранилище могут находиться «тяжелые» данные (картинки, файлы, сложные структуры настроек), автоматическая передача их на форму могла бы существенно замедлить работу интерфейса.

Рассмотрим стандартный цикл копирования документа:

  1. Выполняется процедура ПриКопировании в модуле объекта. Здесь ДокументОбъект еще существует в памяти сервера во всей своей полноте, и реквизит ХранилищеЗначения заполнен.
  2. Платформа создает форму для нового документа. Для этого она конвертирует ДокументОбъект в ДанныеФормыСтруктура (тот самый Объект, который мы видим в реквизитах формы).
  3. На этом этапе платформа «отсеивает» все типы, которые не могут жить в ДанныеФормы. Поле реквизита в структуре Объект создается, но оно всегда пустое.
  4. При вызове метода РеквизитФормыВЗначение("Объект") происходит обратная конвертация. Платформа создает новый экземпляр объекта и заполняет его данными из формы. Поскольку в форме данных хранилища не было, в объекте это поле также затирается значением Неопределено.

Способ 1: Использование буферного реквизита формы (рекомендуемый)

Для того чтобы данные «выжили» при копировании и последующем открытии формы, нам необходимо создать промежуточное место хранения в контексте самой формы. Рассмотрим по шагам, как это реализовать.

Шаг 1. Подготовка формы. Добавим в реквизиты формы новый реквизит, назовем его ДанныеНастроекБуфер. Тип выберем Произвольный. Этот реквизит будет служить мостом между объектом-оригиналом и новым объектом.

Шаг 2. Извлечение данных при создании. В модуле формы в процедуре ПриСозданииНаСервере проанализируем параметры. Если документ создается копированием, нам доступен параметр ЗначениеКопирования, который содержит ссылку на исходный документ.


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    // Проверяем, что это новый документ и он создается копированием
    Если Объект.Ссылка.Пустая() И Параметры.ЗначениеКопирования <> Неопределено Тогда
        
        // Получаем объект-оригинал для чтения ХранилищаЗначения
        ОбъектОригинал = Параметры.ЗначениеКопирования.ПолучитьОбъект();
        
        // Извлекаем данные из хранилища и помещаем в буферный реквизит формы
        // РеквизитХЗ - это имя вашего реквизита типа ХранилищеЗначения в объекте
        ДанныеНастроекБуфер = ОбъектОригинал.РеквизитХЗ.Получить();
        
    КонецЕсли;
    
КонецПроцедуры

Шаг 3. Сохранение данных в новый объект. Теперь нам нужно гарантировать, что при записи нового документа данные из буфера попадут обратно в ХранилищеЗначения. Для этого используем обработчик ПередЗаписьюНаСервере в модуле формы.


&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
    
    // Если в буфере есть данные, упаковываем их обратно в ХранилищеЗначения
    Если ДанныеНастроекБуфер <> Неопределено Тогда
        ТекущийОбъект.РеквизитХЗ = Новый ХранилищеЗначения(ДанныеНастроекБуфер, Новый СжатиеДанных(9));
    КонецЕсли;
    
КонецПроцедуры

Важное примечание: Обратите внимание, что мы используем параметр ТекущийОбъект. Это крайне важно, так как изменения, внесенные в ТекущийОбъект, попадут напрямую в базу данных, минуя проблему очистки через ДанныеФормы.

Способ 2: Работа через Временное хранилище (для больших объемов)

Если данные в вашем реквизите ХранилищеЗначения занимают много мегабайт, передача их в реквизите формы произвольного типа может замедлить работу клиента. В этом случае проанализируем ситуацию и воспользуемся механизмом Временного хранилища.

Посмотрим на пример реализации через адрес временного хранилища:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    Если Объект.Ссылка.Пустая() И ЗначениеЗаполнено(Параметры.ЗначениеКопирования) Тогда
        
        // Читаем данные напрямую из базы данных по ссылке (без получения объекта целиком)
        ДанныеИзХЗ = Параметры.ЗначениеКопирования.РеквизитХЗ.Получить();
        
        // Помещаем данные во временное хранилище и сохраняем адрес в реквизите формы (тип Строка)
        АдресХранилища = ПоместитьВоВременноеХранилище(ДанныеИзХЗ, УникальныйИдентификатор);
        
    КонецЕсли;
    
КонецПроцедуры

&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
    
    // Проверяем наличие адреса и получаем данные
    Если ЭтоАдресВременногоХранилища(АдресХранилища) Тогда
        ДанныеДляЗаписи = ПолучитьИзВременногоХранилища(АдресХранилища);
        ТекущийОбъект.РеквизитХЗ = Новый ХранилищеЗначения(ДанныеДляЗаписи);
    КонецЕсли;
    
КонецПроцедуры

Этот метод считается более профессиональным и безопасным с точки зрения производительности системы 1С.

Разберем типичные ошибки

Проанализируем ситуацию, описанную автором темы. Почему вызов РеквизитФормыВЗначение("Объект") возвращал пустое значение?

Когда вы находитесь в процедуре формы, Объект — это не ДокументОбъект, а ДанныеФормыСтруктура. Вызывая РеквизитФормыВЗначение, вы просите платформу собрать объект из того, что есть «на экране». А так как типа ХранилищеЗначения на экране (в структуре данных формы) нет, платформа просто создает пустой конструктор для этого поля в результирующем объекте. Никогда не используйте этот метод для работы с хранилищами значений, если предварительно не восстановили их вручную.

Также важно помнить про процедуру ПриЧтенииНаСервере. Она понадобится вам, если вы захотите извлекать данные из хранилища не только при копировании, но и при обычном открытии уже существующего документа. Рассмотрим этот фрагмент кода:


&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
    // Извлекаем данные при открытии существующего документа
    ДанныеНастроекБуфер = ТекущийОбъект.РеквизитХЗ.Получить();
КонецПроцедуры

Итоги и рекомендации

Подводя итог, можно выделить основные правила работы с ХранилищеЗначения в управляемых формах:

Выполнение этих шагов гарантирует сохранность ваших настроек, вложений или любых других данных, которые вы решили доверить типу ХранилищеЗначения.

← На главную