Все выросло из чисто прикладной задачи - ускорить и упростить копирование элементов сложных справочников.
При копировании Номенклатуры нужно было воспроизвести единицы измерения, свойства, штрихкоды и т.д. Так как данные, относящиеся к предмету, раскиданы по подчиненным справочникам и регистрам сведений, пользователи весьма удивлялись, почему по кнопочке "создать элемент копированием" у них на форме копии заполненными оказывалась едва ли треть от привычных им сведений.
Чтобы не утомлять их объяснениями, почему в 1С все так сделано. я сел и написал функцию, которая скопирует практически все, что имеет отношение к объекту. Кроме, конечно, регистров накопления и расчета, а также регистров сведений, связанных с регистраторами.
На этом описательную часть считаю законченой. Собственно сама функция:
//Возвращает ссылку на копию элемента справочника
//При этом копируются элементы подчиненных справочников
//и записи независимых регистров сведений
//у которых владелец и ведущее измерение представлены ссылкой на возвращаемую копию.
//АРГУМЕНТЫ:
// Ссылка (СправочникСсылка) - Ссылка на копируемый элемент
// ИсключаяРеквизиты (Строка) - Список имен реквизитов, значения которых копировать не нужно.
// стрНовыеРеквизиты (Структура) - Структура реквизитов и значений для их присвоения копии
Функция СоздатьПолнуюКопиюЭлементаСправочника(Ссылка, ИсключаяРеквизиты = "", стрНовыеРеквизиты = "") Экспорт
НачатьТранзакцию();
//Если нет владельцев тогда не присваиваем этот реквизит при копировании
Если ссылка.Метаданные().Владельцы.Количество() = 0 и Найти(ИсключаяРеквизиты , "Владелец") = 0 Тогда
ИсключаяРеквизиты = ИсключаяРеквизиты + ",Владелец";
КонецЕсли;
//Если не иерархический, но забыли исключить родителя
Если НЕ ссылка.Метаданные().Иерархический и Найти(ИсключаяРеквизиты , "Родитель") = 0 Тогда
ИсключаяРеквизиты = ИсключаяРеквизиты + ",Родитель";
КонецЕсли;
//Если забыли исключить реквизиты доступные только для групп добавим в исключения и их
Для Каждого Реквизит Из ссылка.Метаданные().Реквизиты Цикл
Если Реквизит.Использование = Метаданные.СвойстваОбъектов.ИспользованиеРеквизита.ДляГруппы И Найти(ИсключаяРеквизиты , Реквизит.Имя) = 0 Тогда
ИсключаяРеквизиты = ИсключаяРеквизиты + "," + Реквизит.Имя;
КонецЕсли;
КонецЦикла;
//Формируем список подчиненных справочников и массив их типов
спПодчиненныхСправочников = Новый Массив;
мТиповПодчиненныхСправочников = Новый Массив;
Для Каждого Спр Из Метаданные.Справочники Цикл
Если Спр.Владельцы.Содержит(ссылка.Метаданные()) Тогда
спПодчиненныхСправочников.Добавить(Спр.Имя);
мТиповПодчиненныхСправочников.Добавить(Тип("СправочникСсылка." + Спр.Имя));
КонецЕсли;
КонецЦикла;
//Регистры сведений. Только там где наш элемент копирования может быть ведущим измерением. Запоминаем названия регистров и имена измерений.
спРегистровСведений = Новый Соответствие;
Для Каждого Рег Из МЕтаданные.РегистрыСведений Цикл
Для Каждого Измерение Из Рег.Измерения Цикл
Если Измерение.Тип.СодержитТип(Тип("СправочникСсылка." + Ссылка.Метаданные().Имя)) И Измерение.Ведущее Тогда
спРегистровСведений.Вставить(Рег.Имя, Рег.Измерения.Индекс(Измерение)); Прервать;
КонецЕсли;
КонецЦикла;
КонецЦикла;
//Копируем необычно, чтобы легко исключить требуемые реквизиты
КопияСсылки = Справочники[ссылка.Метаданные().Имя].СоздатьЭлемент();
НовыйКод = КопияСсылки.Код;
ЗаполнитьЗначенияСвойств(КопияСсылки, Ссылка,,ИсключаяРеквизиты);
КопияСсылки.Код = НовыйКод;
//Если нужно присвоим нужные значения нужным реквизитам копии.
Если ТипЗнч(стрНовыеРеквизиты) = Тип("Структура") Тогда
ЗаполнитьЗначенияСвойств(КопияСсылки,стрНовыеРеквизиты);
КонецЕсли;
//Очищаем значения подчиненных справочников ибо они не могут принадлежать новому владельцу (копии старого)
Для Каждого Реквизит Из КопияСсылки.Метаданные().Реквизиты Цикл
Если Не мТиповПодчиненныхСправочников.Найти(ТипЗнч(КопияСсылки[Реквизит.Имя])) = Неопределено Тогда
КопияСсылки[Реквизит.Имя] = Неопределено;
КонецЕсли;
КонецЦикла;
//Теперь запишется:)
КопияСсылки.Записать();
//ТЗ. Сложим сюда старые значения подчиненных справочников и соответствующие им новые, для нашей копии
тСинхронизацииРеквизитов = Новый ТаблицаЗначений;
тСинхронизацииРеквизитов.Колонки.Добавить("СтароеЗначение");
тСинхронизацииРеквизитов.Колонки.Добавить("НовоеЗначение");
//А теперь создадим новые подчиненные элементы копированием и сопоставим их имеющимся у объекта копирования
Для Каждого ПодчиненныйСправочник Из спПодчиненныхСправочников Цикл
ВыборкаИзПодчиненногоСправочника = Справочники[ПодчиненныйСправочник].Выбрать(,Ссылка);
Пока ВыборкаИзПодчиненногоСправочника.Следующий() Цикл
ЗаписьСинхронизации = тСинхронизацииРеквизитов.Добавить();
НовыйПодчиненныйЭлемент = ВыборкаИзПодчиненногоСправочника.Ссылка.Скопировать();
НовыйПодчиненныйЭлемент.Владелец = КопияСсылки.Ссылка;
НовыйПодчиненныйЭлемент.Записать();
ЗаписьСинхронизации.СтароеЗначение = ВыборкаИзПодчиненногоСправочника.Ссылка;
ЗаписьСинхронизации.НовоеЗначение = НовыйПодчиненныйЭлемент.Ссылка;
КонецЦикла;
КонецЦикла;
//Присвоим копии элемента копии значений подчиненных справочников
Для Каждого Реквизит Из Ссылка.Метаданные().Реквизиты Цикл
Если Не мТиповПодчиненныхСправочников.Найти(ТипЗнч(КопияСсылки[Реквизит.Имя])) = Неопределено Тогда
НайденныйРеквизитСоответствия = тСинхронизацииРеквизитов.Найти(Ссылка[Реквизит.Имя],"СтароеЗначение");
Если НЕ НайденныйРеквизитСоответствия = Неопределено
И Найти(ИсключаяРеквизиты, Реквизит.Имя) = 0 Тогда
КопияСсылки[Реквизит.Имя] = НайденныйРеквизитСоответствия.НовоеЗначение;
КонецЕсли;
КонецЕсли;
КонецЦикла;
//Снова запишемся перед записью регистров
КопияСсылки.Записать();
//Регистры сведений, содержащие измерением копируемый элемент
Для Каждого Регистр Из спРегистровСведений Цикл
РегистрМетаданные = Метаданные.РегистрыСведений.Найти(Регистр.Ключ);
ИмяИзмерения = РегистрМетаданные.Измерения.Получить(Регистр.Значение).Имя;
//Только независимые, ибо копировать еще и регистраторы это уже слишком!
Если РегистрМетаданные.РежимЗаписи = Метаданные.СвойстваОбъектов.РежимЗаписиРегистра.Независимый Тогда
Если РегистрМетаданные.ПериодичностьРегистраСведений = Метаданные.СвойстваОбъектов.ПериодичностьРегистраСведений.Непериодический Тогда
РегистрВыборка = РегистрыСведений[Регистр.Ключ].Выбрать(Новый Структура(ИмяИзмерения, Ссылка));//Непериодические
Иначе
РегистрВыборка = РегистрыСведений[Регистр.Ключ].Выбрать(,,Новый Структура(ИмяИзмерения, Ссылка));//Периодические
КонецЕсли;
НаборЗаписей = РегистрыСведений[Регистр.Ключ].СоздатьНаборЗаписей();
НаборЗаписей.Отбор[ИмяИзмерения].Установить(КопияСсылки.Ссылка);
Пока РегистрВыборка.Следующий() Цикл
//Если в выборке есть записи с копируемым элементом скопируем ее для его копии
НоваяЗапись = НаборЗаписей.Добавить();
ЗаполнитьЗначенияСвойств(НоваяЗапись, РегистрВыборка,,ИмяИзмерения);
НоваяЗапись[ИмяИзмерения] = КопияСсылки.Ссылка; //А сюда подставим копию.
КонецЦикла;
Если НаборЗаписей.Количество()>0 Тогда
НаборЗаписей.Записать();
КонецЕсли;
КонецЕсли;
КонецЦикла;
ЗафиксироватьТранзакцию();
Возврат КопияСсылки.Ссылка;
КонецФункции