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