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