gifts2017

Полная копия элемента справочника не своими руками

Опубликовал Andrey Smirnov (dusha0020) в раздел Программирование - Практика программирования

Копируем элемент справочника, элементы подчиненных справочников и записи регистров сведений, относящиеся к элементу. Одним движением.

Все выросло из чисто прикладной задачи - ускорить и упростить копирование элементов сложных справочников.

При копировании Номенклатуры нужно было воспроизвести единицы измерения, свойства, штрихкоды и т.д. Так как данные, относящиеся к предмету, раскиданы по подчиненным справочникам и регистрам сведений, пользователи весьма удивлялись, почему по кнопочке "создать элемент копированием" у них на форме копии заполненными оказывалась едва ли треть от привычных им сведений.

Чтобы не утомлять их объяснениями, почему в 1С все так сделано. я сел и написал функцию, которая скопирует практически все, что имеет отношение к объекту. Кроме, конечно, регистров накопления и расчета, а также регистров сведений, связанных с регистраторами.

На этом описательную часть считаю законченой. Собственно сама функция:

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

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

См. также

Подписаться Добавить вознаграждение

Комментарии

1. Владимир Зленко (ZLENKO) 27.04.16 16:00
Автору риспект и уважуха! Полезная вещь!
yurii_host; +1 Ответить
2. Олег Шалимов (CaSH_2004) 28.04.16 23:21
(0) Странно, но я такой потребности за более чем 10 лет работы приходящим программистом не слышал. Более того в нормальных конторах просили затирать многие поля при копировании, а в некоторых даже в принципе запрещать что либо копировать! Т.к. заведение нового объекта копированием основная причина дублей и неправильных данных - ведь один раз неправильно заведенные данные потом копируются в неограниченном количестве. Такое удобство как копирование разрешается только для грамотного пользователя (у нас администратора базы) т.к. только он знает где и что потом нужно исправить, поэтому и нет удивления почему не все данные копируются.
Полагаю либо пользователи неграмотные либо автор либо очень специфичная вещь - очень часто после таких "оптимизаторов" приходишь в фирму и спрашиваешь -"а почему у вас вот это так заполняют, а это иначе?", и слышишь в ответ - "ну я не знаю", "так было", "так нам настроили".
С другой стороны это наш хлеб что кто-то допускает ошибки - после того как база вся переписана, забита хламом так что никто ничего не понимает, приходишь и люди на все согласны чтобы навели им порядок. Поэтому трудитесь дальше в том же духе если есть желание.
Но в общем для админов это наверное хорошая вещь если в нее добавить возможность чтобы при копировании вываливался список чего собственно будет создано чтобы можно было отметить галочками что требуется копировать - типа дерева значения. Да и за универсальность + бы поставил, но не проверив код воздержусь, а проверять как я говорю без интерактива создавая кучу хлама не имеет смысла.
monkbest; Ta_Da; +2 1 Ответить 5
3. Сергей Д (dddxddd) 29.04.16 08:52
(2) CaSH_2004, Раз вы работаете "приходящим" (без обид, ничего личного) программистом, значит Ваши клиенты сравнительно не большие предприятия, где копирование действительно больше вред чем польза.
В больших предприятиях, где различные части справочников заполняются разными специалистами (отвечающими за правильность своего набора реквизитов) иной раз и нельзя быстро создать новую запись иначе чем копированием. Поэтому говорить, что решение такое решение несет вред, это все равно, что ударив микроскопом по пальцам в попытке забить им гвоздь, заявить, что микроскопы несут вред и посему бесполезны. :-)
4. Maxim Kolkin (the1) 29.04.16 09:13
(2) CaSH_2004, присоединяюсь на счет интерактива. Вот это была бы весчь!
5. Andrey Smirnov (dusha0020) 29.04.16 09:15
(2) CaSH_2004, (3) dddxddd, Интересная дискуссия получилась. И интересные мнения. Понятно, что раз я автор, то придерживаюсь точки зрения пользы от разработки, но...
Жизнь вообще не так однозначна как нам хотелось бы и в этом я сильно согласен с dddxddd. Видел я случаи захламленных баз и даже писал и использовал чужие удалялки дублей, но видел и другие ситуации, когда чтобы создать ту же номенклатуру с обложкой другого цвета приходилось тратить минут 10 времени. А таких позиций нужно создать 200 штук за 2 дня, чтобы поставить быстро на приход.
Инструмент не бывает хорош или плох в принципе - он бывает правильно или неправильно применен. Поэтому с кинутым тапком не согласен:)
mickey.1cx; Ta_Da; +2 Ответить
6. DUH Technolover (DJDUH) 29.04.16 10:25
А более хитрым способом
ОбъектМетаданных.Скопировать()
- не?!)
7. {ÐƦǑƝȊ} mx (dour-dead) 29.04.16 11:01
(2) CaSH_2004,

Но в общем для админов это наверное хорошая вещь если в нее добавить возможность чтобы при копировании вываливался список чего собственно будет создано чтобы можно было отметить галочками что требуется копировать - типа дерева значения.


давным давно просили для 7/7 такое

http://infostart.ru/public/66284/
8. Игорь Фелькер (Brawler) 30.04.16 17:31
(6) DJDUH, я думаю вы не поняли, что именно копируется в описанном способе автора.
Там помимо самого элемента справочника копируется и разная связанная информация.
Ну простой пример, сведения регистра сведений счета учета номенклатуры, если говорить о БУХии 2.0, 3.0 и баз пораньше.
По крайней мере так я понял.
Код не изучал опубликованный тут.
9. Владимир Чаклин (vec435) 04.05.16 09:26
хорошо бы еще в XML - в различные базы закинуть постобработкой
10. Артур Хасанов (gigabyte_artur) 04.05.16 11:35
(2) CaSH_2004, господа, Вам дали инструмент. Ещё и бесплатно. Пользоваться им или нет - дело Ваше. Зачем же сразу столько высокомерия?

Я бы в такой ситуации написал для номенклатуры/контрагента/договора/%%ReferenceName%% свою функцию копирования, которая ведёт себя, как я задумал. Возможно, взяв за основу разработку автора.
11. Дмитрий Иванов (ikekoval) 04.05.16 22:33
Было приятно почитать. Спасибо автору за решение!
12. Александр Пузаков (puzakov) 05.05.16 12:53
Режет глаза многократное использование
Ссылка.Метаданные()

надо было однократно поместить метаданные элемента в переменную, и работать с переменной, а не получать метаданные многократно.
корум; wowik; amon_ra; dusha0020; +4 Ответить 1
13. Andrey Smirnov (dusha0020) 05.05.16 13:23
(12) puzakov, Конструктивная критика. Благодарю!
14. Олег Шалимов (CaSH_2004) 05.05.16 19:39
(3) dddxddd, если заполняют разные специалисты то как один специалист может создать ее копированием? И для чего спрашивается если опять таки оно должно заполнятся разными специалистами? Уважаемый - вы противоречите сами себе.
(10) gigabyte_artur, в смысле дали? А мы типа просили? Еще раз повторюсь - более чем за 10 лет обслуживания несколько сотен разных клиентов разных сфер и размеров от 1 до 30 пользователей 1С я не разу не встречал такой потребности как и сам ее не испытывал, зато знаю чем заканчивается обычное копирование, а вы ратуете за групповое копирование обычным пользователем!.
Я не говорю что инструмент плох. Но давать его пользователю - крайне опасно, и нормальны программист/администратор не стал бы этого делать если только он хоть как то следит за базой. Никто не застрахован от ошибок - а вот результаты их придется устранять админу, а не пользователю. Так что и сам бы использовал такой инструмент только с указанными ранее интерактивными отметками что нужно копировать, а что нет. А получить "кота в мешке" на выходе - боже упаси.

15. Владимир Зленко (ZLENKO) 30.05.16 12:45
(14) CaSH_2004, "более чем за 10 лет обслуживания несколько сотен разных клиентов разных сфер и размеров от 1 до 30 пользователей 1С я не разу не встречал такой потребности как и сам ее не испытывал"

Это не означает что такой потребности нет :-) Но соглашусь, что нужно такое достаточно редко.

"Но давать его пользователю - крайне опасно, и нормальны программист/администратор не стал бы этого делать если только он хоть как то следит за базой."

Все мужчины потенциально опасны, т.к. у них есть то чем они отличаются от женщин и этим они могут изнасиловать кого нибудь. Логика примерно такая же :-)
16. Антон Антонов (monkbest) 31.05.16 07:39
Критика в комментах думаю уместна, т.к. много кто зайдет сюда, увидит, обрадуется и потащит процедуру к себе не подумав. А ведь действительно:
1. В УТ 10 при копировании штрихкод не копируется. А надо? Зачем две номенклатуры с одинаковым штрих кодом?
2. В Бух 2.0 счета учета не копируются. А надо? Если бухгалтер уверен, что там все однотипно, так может счета повесить на папку?
Собственно авторы типовой конфы, они ведь Вам не только структуру данных дают, но и методику работы.
корум; +1 Ответить
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа