Удаление помеченных объектов / Модуль менеджера

файл
///////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2024, ООО 1С-Софт
// Все права защищены. Эта программа и сопроводительные материалы предоставляются 
// в соответствии с условиями лицензии Attribution 4.0 International (CC BY 4.0)
// Текст лицензии доступен по ссылке:
// https://creativecommons.org/licenses/by/4.0/legalcode
///////////////////////////////////////////////////////////////////////////////////////////////////////

#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда

#Область СлужебныеПроцедурыИФункции

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

#Область КонкурентноеУдаление

// Параметры:
//  УдаляемыеОбъекты - Массив из ЛюбаяСсылка
//   ПараметрыУдаления - см. РезультатУдаления
//
Процедура УдалитьПомеченныеОбъектыКонкурентно(УдаляемыеОбъекты, ПараметрыУдаления)
	
	СведенияОМетаданных = СведенияОМетаданных(ПараметрыУдаления, УдаляемыеОбъекты);
	УдаляемыеОбъекты = УдаляемыеОбъекты(УдаляемыеОбъекты, СведенияОМетаданных);
	РезультатОбработки = ОбработатьУдаляемыеОбъекты(ПараметрыУдаления, УдаляемыеОбъекты, СведенияОМетаданных);
	
	ОтразитьМестаИспользования(ПараметрыУдаления, РезультатОбработки.МестаИспользования);
	ОтразитьНеУдаленные(ПараметрыУдаления, РезультатОбработки.НеудаленныеОбъекты);
	ПараметрыУдаления.Удаленные = Новый Массив(Новый ФиксированныйМассив(РезультатОбработки.УдаленныеОбъекты));
КонецПроцедуры

#Область ПреобразованиеКФорматуРезультата

// Параметры:
//   ПараметрыУдаления - см. РезультатУдаления
//   НеУдаленные - см. ПакетУдаляемыхОбъектов
//
Процедура ОтразитьНеУдаленные(ПараметрыУдаления, НеУдаленные)

	ВерсииДанных = СтандартныеПодсистемыСервер.ЗначенияРеквизитовОбъектовЕслиСуществуют(
		НеУдаленные.ВыгрузитьКолонку("УдаляемыйСсылка"), "ВерсияДанных");
	Для Каждого Элемент Из НеУдаленные Цикл

		// Объект может быть удален как подчиненный другому объекту, помеченному на удалению.
		Если ОбъектУдален(Элемент.УдаляемыйСсылка, ВерсииДанных) Тогда
			Продолжить;
		КонецЕсли;
		
		ПараметрыУдаления.Неудаленные.Добавить(Элемент.УдаляемыйСсылка);
		Если Элемент.РезультатУдаления <> КодыРезультатаУдаления().НеПомеченНаУдаление Тогда
			РегистрыСведений.НеудаленныеОбъекты.Добавить(Элемент.УдаляемыйСсылка);
		КонецЕсли;
		
		Если Элемент.РезультатУдаления = КодыРезультатаУдаления().ОшибкаПриУдалении Тогда
			Причина = ПараметрыУдаления.ПрепятствующиеУдалению.Добавить();
			Причина.УдаляемыйСсылка = Элемент.УдаляемыйСсылка;
			Причина.ОписаниеОшибки = ОбработкаОшибок.КраткоеПредставлениеОшибки(Элемент.ИнформацияОбОшибке) + Элемент.Сообщения;
			Причина.ПодробноеОписаниеОшибки = ОбработкаОшибок.ПодробноеПредставлениеОшибки(Элемент.ИнформацияОбОшибке);
		ИначеЕсли Элемент.РезультатУдаления = КодыРезультатаУдаления().НеПомеченНаУдаление Тогда 	
			Причина = ПараметрыУдаления.ПрепятствующиеУдалению.Добавить();
			Причина.УдаляемыйСсылка = Элемент.УдаляемыйСсылка;
			Причина.ОписаниеОшибки = НСтр("ru='Удаляемый объект не помечен на удаление.'");
		КонецЕсли;

	КонецЦикла;
	
КонецПроцедуры

Процедура ОтразитьМестаИспользования(ПараметрыУдаления, МестаИспользования)

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

#КонецОбласти

#Область УдалениеОбъектов

// Параметры:
//   ПараметрыУдаления - см. РезультатУдаления
//   УдаляемыеОбъекты - см. УдаляемыеОбъекты
//   СведенияОМетаданных - см. СведенияОМетаданных
// 	
// Возвращаемое значение:
//   см. РезультатОбработки
//
Функция ОбработатьУдаляемыеОбъекты(ПараметрыУдаления, УдаляемыеОбъекты, СведенияОМетаданных)
	
	РезультатыУдаления = РезультатОбработки();
	Пакет = ПакетУдаляемыхОбъектов();
	ОбъектыСЗапретомУдаления = ПакетУдаляемыхОбъектов();
	
	Для Каждого ЭлементОчереди Из УдаляемыеОбъекты Цикл
		Если НЕ Пакет.Найти(ЭлементОчереди.УдаляемыйСсылка, "УдаляемыйСсылка") <> Неопределено Тогда
			// @skip-check query-in-loop - Порционная обработка больших объемов данных.
			ЭлементОчередиВПакете = ЭлементОчередиВПакет(ЭлементОчереди, СведенияОМетаданных, Пакет); 
			
			Если ЗапрещенаОбработкаЭлемента(ЭлементОчередиВПакете) Тогда
				ОбъектыСЗапретомУдаления = ОбъединениеТаблиц(ОбъектыСЗапретомУдаления, ЭлементОчередиВПакете);
			Иначе
				Пакет = ОбъединениеТаблиц(Пакет, ЭлементОчередиВПакете, Истина);
			КонецЕсли;
		КонецЕсли;
		
		Если Пакет.Количество() >= ПараметрыУдаления.РазмерПакета
				ИЛИ УдаляемыеОбъекты.Индекс(ЭлементОчереди) = УдаляемыеОбъекты.Количество()-1 Тогда
				
			РезультатОбработкиПакета = ОбработатьПакет(ПараметрыУдаления, Пакет);
			ОбъединитьРезультатУдаления(РезультатыУдаления, РезультатОбработкиПакета);
			Пакет.Очистить();
		КонецЕсли;
	КонецЦикла;
	
	РезультатУдаленияИзНеобработанногоПакета = РезультатУдаленияИзНеобработанногоПакета(ПараметрыУдаления, ОбъектыСЗапретомУдаления);
	ОбъединитьРезультатУдаления(РезультатыУдаления, РезультатУдаленияИзНеобработанногоПакета);
	
	// Попробуем удалить объекты, образующие циклические ссылки в одной транзакции
	КоличествоНеудаленныхЭлементов = 0;
	Пока РезультатыУдаления.НеудаленныеОбъекты.Количество() <> КоличествоНеудаленныхЭлементов Цикл
		КоличествоНеудаленныхЭлементов = РезультатыУдаления.НеудаленныеОбъекты.Количество();
		
		Пакет = ЦиклическиеСсылки(РезультатыУдаления.НеудаленныеОбъекты, РезультатыУдаления.МестаИспользования);
		РезультатОбработкиЦиклическихСсылок = ОбработатьПакетВОднойТранзакции(ПараметрыУдаления, Пакет);
		Если РезультатОбработкиЦиклическихСсылок.УдаленныеОбъекты.Количество() > 0 Тогда
			РезультатыУдаления = РезультатПослеУдаленияЦиклическихСсылок(РезультатыУдаления, РезультатОбработкиЦиклическихСсылок);
		КонецЕсли;
	КонецЦикла;
	
	Возврат РезультатыУдаления;
КонецФункции

Функция РезультатУдаленияИзНеобработанногоПакета(ПараметрыУдаления, ОбъектыСЗапретомУдаления)
	РезультатыУдаления = РезультатОбработки();
	
	РезультатыУдаления.НеудаленныеОбъекты = ОбъединениеТаблиц(
		РезультатыУдаления.НеудаленныеОбъекты,
		ОбъектыСЗапретомУдаления);
		
	РезультатыУдаления.МестаИспользования = ОбъединениеТаблиц(
		РезультатыУдаления.МестаИспользования,
		ОставшиесяМестаИспользованияУдаляемогоОбъекта(ПараметрыУдаления,
			МестаИспользованияЭлементовПакета(ПараметрыУдаления, ОбъектыСЗапретомУдаления)));
			
	Возврат РезультатыУдаления;			
КонецФункции

Функция ЗапрещенаОбработкаЭлемента(ЭлементОчередиВПакете)
	Возврат (ЭлементОчередиВПакете.Количество() > 0 
		И ЭлементОчередиВПакете[ЭлементОчередиВПакете.Количество() - 1].РезультатУдаления 
			<> КодыРезультатаУдаления().Успешно);
КонецФункции

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

Функция ОбработатьПакет(ПараметрыУдаления, Пакет)
	РезультатОбработкиПакета = РезультатОбработки();
	
	Если ЗначениеЗаполнено(ПараметрыУдаления.ИдентификаторЗадания) И ОбщегоНазначения.РежимОтладки() Тогда
		Идентификатор = ПараметрыУдаления.ИдентификаторЗадания;
	Иначе	
		УстановитьПривилегированныйРежим(Истина);
		ФоновоеЗадание = ПолучитьТекущийСеансИнформационнойБазы().ПолучитьФоновоеЗадание();
		Идентификатор = ?(ФоновоеЗадание <> Неопределено, ФоновоеЗадание.УникальныйИдентификатор, Новый УникальныйИдентификатор);
		УстановитьПривилегированныйРежим(Ложь);
	КонецЕсли;
	
	ПередОбработкойПакета(Пакет, Идентификатор);
	
	МестаИспользования = МестаИспользованияЭлементовПакета(ПараметрыУдаления, Пакет);

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

		ПослеУдаленияГруппыОбъектов(Контекст, НЕ НельзяУдалятьОсновнойОбъект);
		ОсновнойИПодчиненныеОбъекты.Очистить();
		СсылкиНаУдаляемыеОбъекты.Очистить();

	КонецЦикла;
	
	ПослеОбработкиПакета(ПараметрыУдаления, Идентификатор);
	ОчиститьСсылкиНаУдаляемыеОбъекты(ПараметрыУдаления, РезультатОбработкиПакета);

	Возврат РезультатОбработкиПакета;
КонецФункции

// см. УдалениеПомеченныхОбъектовПереопределяемый.ПередУдалениемГруппыОбъектов
Процедура ПередУдалениемГруппыОбъектов(Контекст, УдаляемыеОбъекты)
	ИнтеграцияПодсистемБСП.ПередУдалениемГруппыОбъектов(Контекст, УдаляемыеОбъекты);
	УдалениеПомеченныхОбъектовПереопределяемый.ПередУдалениемГруппыОбъектов(Контекст, УдаляемыеОбъекты);
КонецПроцедуры

// см. УдалениеПомеченныхОбъектовПереопределяемый.ПослеУдаленияГруппыОбъектов
Процедура ПослеУдаленияГруппыОбъектов(Контекст, Успешно)
	ИнтеграцияПодсистемБСП.ПослеУдаленияГруппыОбъектов(Контекст, Успешно);
	УдалениеПомеченныхОбъектовПереопределяемый.ПослеУдаленияГруппыОбъектов(Контекст, Успешно);
КонецПроцедуры

Функция РезультатУдаленияЭлемента(Знач Элемент)
	Результат = Новый Структура();
	
	Результат.Вставить("УдаляемыйСсылка", Элемент.УдаляемыйСсылка);
	Результат.Вставить("Код", КодыРезультатаУдаления().Успешно);
	Результат.Вставить("МестаИспользования", МестаИспользования());
	Результат.Вставить("Сообщения", Новый ФиксированныйМассив(Новый Массив));
	Результат.Вставить("ЭтоОсновнойОбъект", Элемент.ЭтоОсновнойОбъект);
	Результат.Вставить("ИнформацияОбОшибке");
	
	Возврат Результат;
КонецФункции

#Область ОчисткаСсылокНаУдаляемыеОбъекты

// Параметры:
//   ПараметрыУдаления - см. РезультатУдаления
//   РезультатОбработкиПакета - см. РезультатОбработки
//
Процедура ОчиститьСсылкиНаУдаляемыеОбъекты(ПараметрыУдаления, РезультатОбработкиПакета)
	Если Не ПараметрыУдаления.ОчищатьСсылкиВМестахИспользования
		Или РезультатОбработкиПакета.МестаОчисткиСсылок.Количество() = 0 Тогда
			
		Возврат;
	КонецЕсли;
	
	МестаИспользования = РезультатОбработкиПакета.МестаОчисткиСсылок;
	МестаИспользования.Сортировать("МестоИспользования", Новый СравнениеЗначений());

	УдаляемыеОбъектыВМестеИспользования = Новый Массив;
	ПозицияТекущегоМестаИспользования = 0;
	Для Каждого СведениеОМестеИспользования Из МестаИспользования Цикл
		Если МестаИспользования[ПозицияТекущегоМестаИспользования].МестоИспользования
			  <> СведениеОМестеИспользования.МестоИспользования Тогда

			ОчиститьСсылкиВМестеИспользования(МестаИспользования[ПозицияТекущегоМестаИспользования].МестоИспользования,
				УдаляемыеОбъектыВМестеИспользования);
			ПозицияТекущегоМестаИспользования = МестаИспользования.Индекс(СведениеОМестеИспользования);
			УдаляемыеОбъектыВМестеИспользования.Очистить();
		КонецЕсли;
		
		УдаляемыеОбъектыВМестеИспользования.Добавить(СведениеОМестеИспользования.УдаляемыйСсылка);

		Если МестаИспользования.Индекс(СведениеОМестеИспользования) = МестаИспользования.Количество() - 1 Тогда
			ОчиститьСсылкиВМестеИспользования(СведениеОМестеИспользования.МестоИспользования,
				УдаляемыеОбъектыВМестеИспользования);
		КонецЕсли;
	КонецЦикла;
КонецПроцедуры

Процедура ОчиститьСсылкиВМестеИспользования(МестоИспользования, УдаляемыеОбъекты)

	НачатьТранзакцию();
	Попытка
		МетаданныеОбъекта = МестоИспользования.Метаданные();

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

		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ЗаписатьПредупреждение(НСтр("ru = 'Не удалось очистить ссылки на удаляемый объект в ""%1"" по причине:'"), 
			МестоИспользования, ИнформацияОбОшибке());
	КонецПопытки;
	
КонецПроцедуры

Процедура ОчиститьСсылкиВТабличнойЧасти(МестоИспользованияОбъект, КоллекцияРеквизитов, УдаляемыеОбъекты)
	Для Каждого СтрокаТЧ Из МестоИспользованияОбъект Цикл
		ОчиститьСсылкиВКоллекцииРеквизитов(СтрокаТЧ, КоллекцияРеквизитов, УдаляемыеОбъекты);
	КонецЦикла;
КонецПроцедуры

Процедура ОчиститьСсылкиВКоллекцииРеквизитов(МестоИспользованияОбъект, КоллекцияРеквизитов, УдаляемыеОбъекты)
	Для Каждого Реквизит Из КоллекцияРеквизитов Цикл
		Если УдаляемыеОбъекты.Найти(МестоИспользованияОбъект[Реквизит.Имя]) <> Неопределено Тогда
			МестоИспользованияОбъект[Реквизит.Имя] = Реквизит.Тип.ПривестиЗначение(Неопределено);
		КонецЕсли;
	КонецЦикла;	
КонецПроцедуры

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

#Область ФормированиеДочернихПодчиненныхЭлементов

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

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

// Параметры:
//   РезультатПакета - ВыборкаИзРезультатаЗапроса
//
// Возвращаемое значение:
//   см. ТаблицаДочернихПодчиненныхЭлементов
//
Функция ПодчиненныеЭлементы(РезультатПакета)
	ДочерниеПодчиненныеЭлементы = ТаблицаДочернихПодчиненныхЭлементов();

	Пока РезультатПакета.Следующий() Цикл
		ЗаполнитьЗначенияСвойств(ДочерниеПодчиненныеЭлементы.Добавить(), РезультатПакета);
	КонецЦикла;
	
	Возврат ДочерниеПодчиненныеЭлементы;
КонецФункции

// Возвращаемое значение:
// 	ТаблицаЗначений :
//   * Элемент - ЛюбаяСсылка
//   * ДочерниеЭлементы - см. ТаблицаДочернихПодчиненныхЭлементов 
//   * ПометкаУдаления - Булево
//   * ТипЭлемента - Строка
//
Функция ТаблицаДочернихПодчиненныхЭлементов()
	ДочерниеПодчиненныеЭлементы = Новый ТаблицаЗначений();
	ДочерниеПодчиненныеЭлементы.Колонки.Добавить("Элемент");
	ДочерниеПодчиненныеЭлементы.Колонки.Добавить("ДочерниеЭлементы", Новый ОписаниеТипов("ТаблицаЗначений"));
	ДочерниеПодчиненныеЭлементы.Колонки.Добавить("ПометкаУдаления", Новый ОписаниеТипов("Булево"));
	ДочерниеПодчиненныеЭлементы.Колонки.Добавить("ТипЭлемента", Новый ОписаниеТипов("Строка"));
	Возврат ДочерниеПодчиненныеЭлементы
КонецФункции

Функция ТекстЗапросаДочернихЭлементов(ИнформацияОМетаданных)
	ШаблонЗапроса = "ВЫБРАТЬ
	|	Таб.Ссылка КАК Элемент,
	|	Таб.ПометкаУдаления КАК ПометкаУдаления,
	|	""Дочерний"" КАК ТипЭлемента
	|ИЗ
	|	&ИмяТаблицы КАК ТАБ
	|ГДЕ
	|	Таб.Ссылка В ИЕРАРХИИ (&Родитель)
	|УПОРЯДОЧИТЬ ПО
	|	Элемент ИЕРАРХИЯ";
	
	Таблица = ИнформацияОМетаданных.Метаданные.ПолноеИмя();
	ТекстЗапроса = СтрЗаменить(ШаблонЗапроса, "&ИмяТаблицы", Таблица);
	
	Возврат ТекстЗапроса;
КонецФункции

// Параметры:
//   ИнформацияОМетаданных - см. СведенияОМетаданных
//
// Возвращаемое значение:
//   Строка
//
Функция ТекстЗапросаПодчиненныхЭлементов(ИнформацияОМетаданных)
	ТекстыЗапросов = Новый Массив;// Массив из Строка
	
	ШаблонУсловияСвязи = "Таб.%1 = &Ссылка";
	ШаблонЗапроса = "ВЫБРАТЬ
	|	Таб.Ссылка КАК Элемент,
	|	Таб.ПометкаУдаления КАК ПометкаУдаления,
	|	""Подчиненный"" КАК ТипЭлемента
	|ИЗ
	|	&ИмяТаблицы КАК ТАБ
	|ГДЕ
	|	Таб.Ссылка <> &Ссылка
	|	И &УсловиеСвязи";
	
	УсловиеСвязи = "ЛОЖЬ";
	Для Каждого ПодчиненныйОбъект Из ИнформацияОМетаданных.ОписаниеПодчиненныхОбъектов Цикл
		УсловиеСвязи = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонУсловияСвязи, ПодчиненныйОбъект.ИмяРеквизита);
		ТекстЗапроса = СтрЗаменить(ШаблонЗапроса, "&ИмяТаблицы", ПодчиненныйОбъект.Метаданные.ПолноеИмя());
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеСвязи", УсловиеСвязи);
		ТекстыЗапросов.Добавить(ТекстЗапроса);
	КонецЦикла;
	
	Возврат СтрСоединить(ТекстыЗапросов, Символы.ПС + "ОБЪЕДИНИТЬ ВСЕ" + Символы.ПС);	
КонецФункции
#КонецОбласти

// Параметры:
//   РезультатыУдаления - см. РезультатОбработки
//   РезультатОбработки - см. РезультатОбработки
//
Процедура ОбъединитьРезультатУдаления(РезультатыУдаления, РезультатОбработки)
	
	РезультатыУдаления.НеудаленныеОбъекты = ОбъединениеТаблиц(РезультатыУдаления.НеудаленныеОбъекты, РезультатОбработки.НеудаленныеОбъекты);
	РезультатыУдаления.МестаИспользования = ОбъединениеТаблиц(РезультатыУдаления.МестаИспользования, РезультатОбработки.МестаИспользования);
	ОбщегоНазначенияКлиентСервер.ДополнитьМассив(РезультатыУдаления.УдаленныеОбъекты, РезультатОбработки.УдаленныеОбъекты);
	
КонецПроцедуры

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

Функция РазрешеноУдалениеЭлемента(ДополнительныеСведения)
	Результат = Истина;
	
	Для Каждого Элемент Из ДополнительныеСведения Цикл
		Если НЕ Элемент.ПометкаУдаления И НЕ Элемент.ТипЭлемента = "Подчиненный" Тогда
			Результат = Ложь;
			Прервать;
		КонецЕсли;
	КонецЦикла;
	Возврат Результат;
КонецФункции

Функция ОбъектУдален(УдаляемыйСсылка, ВерсииДанных = Неопределено)
	Возврат ЗначениеЗаполнено(УдаляемыйСсылка) 
		И ОбщегоНазначенияКлиентСервер.ЕстьРеквизитИлиСвойствоОбъекта(УдаляемыйСсылка, "ВерсияДанных")
		И ?(ВерсииДанных <> Неопределено, Не ЗначениеЗаполнено(ВерсииДанных[УдаляемыйСсылка])
			Или Не ЗначениеЗаполнено(ВерсииДанных[УдаляемыйСсылка].ВерсияДанных),
			Не ЗначениеЗаполнено(ОбщегоНазначения.ЗначениеРеквизитаОбъекта(УдаляемыйСсылка, "ВерсияДанных")));
КонецФункции

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

КонецПроцедуры

Функция СообщенияПользователяВСтроку(Сообщения)
	Результат = "";
	
	Для Каждого Элемент Из Сообщения Цикл
		Результат = Символы.ПС + Результат + Элемент.Текст;
	КонецЦикла;
	
	Возврат Результат;
КонецФункции

// Обработать пакет в одной транзакции.
// 
// Параметры:
//  ПараметрыУдаления - см. РезультатУдаления
//  Пакет - см. ПакетУдаляемыхОбъектов
// 
// Возвращаемое значение:
//   см. РезультатОбработки
//
Функция ОбработатьПакетВОднойТранзакции(ПараметрыУдаления, Пакет)
	
	Результат = РезультатОбработки();
	
	Контекст = Новый Структура;
	УдаляемыеОбъекты = Пакет.ВыгрузитьКолонку("УдаляемыйСсылка");
	ПередУдалениемГруппыОбъектов(Контекст, УдаляемыеОбъекты);

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

// Параметры:
//   ПараметрыУдаления - см. РезультатУдаления
//   Элемент - СтрокаТаблицыЗначений из см. ПакетУдаляемыхОбъектов
//   МестаИспользования - см. МестаИспользования
//   НеудаленныеЭлементы - Массив из ЛюбаяСсылка
//
// Возвращаемое значение:
//   Структура:
//   * Код - Строка
//   * МестаИспользования - см. МестаИспользованияЭлементовПакета
//   * Сообщения - Массив из СообщениеПользователю
//   * ИнформацияОбОшибке - Неопределено
// 	                      - ИнформацияОбОшибке
//
Функция УдалитьЭлементПакета(ПараметрыУдаления, Элемент, МестаИспользования, НеудаленныеЭлементы)

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

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

	ОставшиесяМестаИспользования = ОставшиесяМестаИспользованияУдаляемогоОбъекта(ПараметрыУдаления, МестаИспользования);
	Если ОставшиесяМестаИспользования.Количество() = 0
			Или МестаОчисткиСсылок.Количество() = ОставшиесяМестаИспользования.Количество() Тогда
		
		Результат.МестаИспользования = МестаОчисткиСсылок;  	
	Иначе
		Результат.МестаИспользования = ОставшиесяМестаИспользования;
		Результат.Код = КодыРезультатаУдаления().ЕстьМестаИспользования;
	КонецЕсли;

	Возврат Результат;
КонецФункции

Процедура УдалитьОбъект(Объект)
	Объект.ДополнительныеСвойства.Вставить("НеВыполнятьКонтрольУдаляемых");
	Объект.Удалить();
КонецПроцедуры

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

Функция СсылкаВВедущемИзмерении(СведенияОМестеИспользования)
	
	ИменаИзмерений = УдалениеПомеченныхОбъектовПовтИсп.ВедущиеИзмеренияРегистра(СведенияОМестеИспользования.Метаданные.ПолноеИмя());
	Измерения = Новый Структура(ИменаИзмерений);
	ЗаполнитьЗначенияСвойств(Измерения, СведенияОМестеИспользования.МестоИспользования);
	Для каждого Измерение Из Измерения Цикл
		Если Измерение.Значение = СведенияОМестеИспользования.УдаляемыйСсылка Тогда
			Возврат Истина;
		КонецЕсли;
	КонецЦикла;
	Возврат Ложь;
	
КонецФункции

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

// Параметры:
//   Пакет - см. ПакетУдаляемыхОбъектов
//   ДочерниеПодчиненныеЭлементы - см. ДочерниеПодчиненныеЭлементы
//
Процедура ДополнитьПакетДочернимиИПодчиненнымиЭлементами(Пакет, ОсновнойЭлемент, ДочерниеПодчиненныеЭлементы, Исключения)
	Для Каждого ОписаниеЭлемента Из ДочерниеПодчиненныеЭлементы Цикл
		Если Пакет.Найти(ОписаниеЭлемента.Элемент, "УдаляемыйСсылка") <> Неопределено
			ИЛИ Исключения.Найти(ОписаниеЭлемента.Элемент, "УдаляемыйСсылка") <> Неопределено Тогда
			
			Продолжить;
		КонецЕсли;
		
		ЭлементПакета = Пакет.Добавить();
		ЭлементПакета.ОсновнойЭлементСсылка = ОсновнойЭлемент;
		ЭлементПакета.УдаляемыйСсылка = ОписаниеЭлемента.Элемент;
		ЭлементПакета.ДочерниеПодчиненныеОбъекты = ОписаниеЭлемента.ДочерниеЭлементы;
	КонецЦикла;
КонецПроцедуры

// Параметры:
//   ПараметрыУдаления - см. РезультатУдаления
//
Процедура ОповеститьОПрогрессе(ПараметрыВыполнения)
	ТекущееВремя = ТекущаяУниверсальнаяДатаВМиллисекундах();
	Если ПараметрыВыполнения.ЭтоРегламентноеЗадание 
			ИЛИ ТекущееВремя - ПараметрыВыполнения.ВремяОповещения <= ПараметрыВыполнения.ПериодОповещения Тогда
		Возврат;
	КонецЕсли;
	ПараметрыВыполнения.Всего = ПараметрыВыполнения.КоличествоУдаляемых;
	ОтметитьПрогрессОбходаКоллекции(ПараметрыВыполнения, "ПользовательскиеОбъекты");
	ПараметрыВыполнения.ВремяОповещения = ТекущееВремя;
КонецПроцедуры

#КонецОбласти

#Область ФильтрыКоллекций

// Параметры:
//   Пакет - см. ПакетУдаляемыхОбъектов
//   МестаИспользования - см. МестаИспользования
// 	
// Возвращаемое значение:
//   см. ПакетУдаляемыхОбъектов
//
Функция ЦиклическиеСсылки(Пакет, МестаИспользования)
	Результат = СсылкиТолькоНаЭлементыВнутриПакета(Пакет, МестаИспользования);
	
	КоличествоВПакете = Пакет.Количество();
	Пока (Результат[0].Количество() <> КоличествоВПакете) Цикл
		КоличествоВПакете = Результат[0].Количество();
		Результат = СсылкиТолькоНаЭлементыВнутриПакета(Результат[0], Результат[1]);
	КонецЦикла;
	
	Возврат Результат[0];
КонецФункции

// Параметры:
//   ПараметрыУдаления - Структура:
//   * РазмерПакета - Число
//   * ИсключенияПоискаСсылок - Массив
//   МестаИспользования - см. МестаИспользованияЭлементовПакета
//
// Возвращаемое значение:
//   см. МестаИспользования
//
Функция ОставшиесяМестаИспользованияУдаляемогоОбъекта(ПараметрыУдаления, МестаИспользования, СсылкаИсключение = Неопределено)
	Результат = МестаИспользования.СкопироватьКолонки();

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

	Возврат Результат;
КонецФункции

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

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

#КонецОбласти

#Область ОбработчикиСобытийКонкурентногоОбновления

Процедура ПослеОбработкиПакета(ПараметрыУдаления, Идентификатор)
	УдалениеПомеченныхОбъектовСлужебный.СнятьБлокировкуИспользованияУдаляемыхОбъектов(Идентификатор);
	ПараметрыСеанса.ВыполняетсяУдалениеОбъектов = Ложь;
КонецПроцедуры

Процедура ПередОбработкойПакета(Пакет, Идентификатор)
	ПараметрыСеанса.ВыполняетсяУдалениеОбъектов = Истина;
	УдалениеПомеченныхОбъектовСлужебный.УстановитьБлокировкуИспользованияУдаляемыхОбъектов(Пакет, Идентификатор);
КонецПроцедуры

#КонецОбласти

#Область Конструкторы

// Возвращаемое значение:
//  Структура:
//   * ПользовательскиеОбъекты - Массив из ЛюбаяСсылка 
//   * Монопольно - Булево 
//   * ОчищатьСсылкиВМестахИспользования - Булево
//   * Режим - Строка
//   * ЭтоРегламентноеЗадание - Булево
//   * РазмерПакета - Число
//
Функция ПараметрыУдаления() Экспорт
	
	Результат = Новый Структура;
	Результат.Вставить("ПользовательскиеОбъекты", Новый Массив);
	Результат.Вставить("Монопольно", Ложь);
	Результат.Вставить("ОчищатьСсылкиВМестахИспользования", Ложь);
	Результат.Вставить("Режим", "");
	Результат.Вставить("ЭтоРегламентноеЗадание", Ложь);
	Результат.Вставить("РазмерПакета", 100);
	Возврат Результат;
	
КонецФункции

// ПараметрыУдаления:
//   УдаляемыеОбъекты - Массив из ЛюбаяСсылка
//   ПараметрыУдаления - см. ПараметрыУдаления
//
// Возвращаемое значение:
//   Структура:
//   * КоличествоНеудаленных - Число
//   * ПовторноУдаляемые - Массив из ЛюбаяСсылка
//   * ПрепятствующиеУдалению - ТаблицаЗначений:
//      ** УдаляемыйСсылка - ЛюбаяСсылка
//      ** МестоИспользования  - ЛюбаяСсылка
//      ** ОбнаруженныйСтатус - Строка
//      ** ПодробноеОписаниеОшибки - Строка
//      ** ОписаниеОшибки - Строка
//   * НеУдаленные - Массив из ЛюбаяСсылка
//   * Удаленные - Массив из ЛюбаяСсылка
//   * ПользовательскиеОбъекты - Массив из ЛюбаяСсылка
//   * Монопольно - Булево
//   * ОчищатьСсылкиВМестахИспользования - Булево
//   * КешИнформацииОТипах - Соответствие
//   * РазмерПакета - Число
//   * ИсключенияПоискаСсылок - Массив из ОбъектМетаданных
//   * ИдентификаторЗадания - УникальныйИдентификатор
//   * ВремяОповещения - Число - время последнего оповещения в миллисекундах
//   * ПериодОповещения - Число - количество миллисекунд между оповещениями
//
Функция РезультатУдаления(УдаляемыеОбъекты, ПараметрыУдаления)
	ИсключенияПоискаСсылок = ИсключенияПоискаСсылокПриУдалении();

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

Функция ИсключенияПоискаСсылокПриУдалении()
	ИсключенияПоискаСсылок = ОбщегоНазначения.ИсключенияПоискаСсылок();
	Для Каждого Элемент Из УдалениеПомеченныхОбъектовСлужебный.ИсключенияПоискаСсылокРазрешающихУдаление() Цикл
		ИсключенияПоискаСсылок.Вставить(Элемент, "*");
	КонецЦикла;
	
	ИсключенияПоискаСсылок.Удалить(Метаданные.Справочники.ВнешниеПользователи);
	
	Возврат ИсключенияПоискаСсылок;
КонецФункции

// Возвращаемое значение:
//   Структура:
//   * НеудаленныеОбъекты - см. ПакетУдаляемыхОбъектов
//   * УдаленныеОбъекты - Массив из ЛюбаяСсылка
//   * МестаИспользования - см. МестаИспользования
//   * МестаОчисткиСсылок - см. МестаИспользования
//
Функция РезультатОбработки()

	Результат = Новый Структура();
	Результат.Вставить("УдаленныеОбъекты", Новый Массив);
	Результат.Вставить("НеудаленныеОбъекты", ПакетУдаляемыхОбъектов()); 
	Результат.Вставить("МестаИспользования", МестаИспользования());
	Результат.Вставить("МестаОчисткиСсылок", МестаИспользования());
	Возврат Результат;
	
КонецФункции

// Возвращаемое значение:
//   ТаблицаЗначений:
//   * УдаляемыйСсылка - ЛюбаяСсылка
//   * МестоИспользования - ЛюбаяСсылка
//   * Метаданные - ОбъектМетаданных 
//   * ЭтоСлужебныеДанные - Булево 
//
Функция МестаИспользования() Экспорт
	Таблица = Новый ТаблицаЗначений();
	
	Таблица.Колонки.Добавить("УдаляемыйСсылка");
	Таблица.Колонки.Добавить("МестоИспользования");
	Таблица.Колонки.Добавить("Метаданные");
	Таблица.Колонки.Добавить("ЭтоСлужебныеДанные", Новый ОписаниеТипов("Булево"));
	
	Таблица.Индексы.Добавить("УдаляемыйСсылка");
	Таблица.Индексы.Добавить("МестоИспользования");
	
	Возврат Таблица;
КонецФункции

// Возвращаемое значение:
//   ТаблицаЗначений:
//   * УдаляемыйСсылка - ЛюбаяСсылка
//   * ЕстьДочерниеОбъекты - Булево
//   * ЕстьПодчиненныеОбъекты - Булево
//   * ИнформацияОбОшибке - ИнформацияОбОшибке
//   * Сообщения - Строка
//   * РезультатУдаления - Число
//   * ДочерниеПодчиненныеОбъекты - см. ТаблицаДочернихПодчиненныхЭлементов
//   * ЭтоОсновнойОбъект - Булево
//
Функция ПакетУдаляемыхОбъектов()
	
	Пакет = Новый ТаблицаЗначений;
	Пакет.Колонки.Добавить("УдаляемыйСсылка");
	Пакет.Колонки.Добавить("ЕстьДочерниеОбъекты", , Новый ОписаниеТипов("Булево"));
	Пакет.Колонки.Добавить("ЕстьПодчиненныеОбъекты", Новый ОписаниеТипов("Булево"));
	Пакет.Колонки.Добавить("ИнформацияОбОшибке", Новый ОписаниеТипов("ИнформацияОбОшибке"));
	Пакет.Колонки.Добавить("Сообщения", Новый ОписаниеТипов("Строка"));
	Пакет.Колонки.Добавить("РезультатУдаления", Новый ОписаниеТипов("Число"));
	Пакет.Колонки.Добавить("ДочерниеПодчиненныеОбъекты", Новый ОписаниеТипов("ТаблицаЗначений"));
	Пакет.Колонки.Добавить("ЭтоОсновнойОбъект", Новый ОписаниеТипов("Булево"));
	Пакет.Колонки.Добавить("ОсновнойЭлементСсылка");
	
	Пакет.Индексы.Добавить("УдаляемыйСсылка");
	Возврат Пакет;
	
КонецФункции

Функция КодыРезультатаУдаления() 
	ТипыОшибок = Новый Структура;
	ТипыОшибок.Вставить("Успешно", -1);
	ТипыОшибок.Вставить("ЕстьМестаИспользования", 0);
	ТипыОшибок.Вставить("ОшибкаПриУдалении", 1);
	ТипыОшибок.Вставить("ЕстьДочерниеПодчиненныеЭлементы", 2);
	ТипыОшибок.Вставить("ЕстьДочерниеПодчиненныеЭлементыНепомеченныеНаУдаление", 3);
	ТипыОшибок.Вставить("НеПомеченНаУдаление", 4);
	Возврат ТипыОшибок;
КонецФункции

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

#Область ФормированиеСведенийОМетаданных

// Параметры:
//   ПараметрыУдаления - Структура 
//   УдаляемыеОбъекты - Массив из ЛюбаяСсылка
//
// Возвращаемое значение:
//   ТаблицаЗначений:
//   * Метаданные - ОбъектМетаданных 
//   * Иерархический - Булево
//   * Приоритет - Число
//   * Тип - Тип
//   * ОписаниеПодчиненныхОбъектов - ТаблицаЗначений
//
Функция СведенияОМетаданных(ПараметрыУдаления, УдаляемыеОбъекты)
	
	Результат = Новый ТаблицаЗначений();
	Результат.Колонки.Добавить("Метаданные", Новый ОписаниеТипов("ОбъектМетаданных"));
	Результат.Колонки.Добавить("Иерархический", Новый ОписаниеТипов("Булево"));
	Результат.Колонки.Добавить("Приоритет", Новый ОписаниеТипов("Число"));
	Результат.Колонки.Добавить("ЕстьПодчиненныеОбъекты", Новый ОписаниеТипов("Булево"));
	Результат.Колонки.Добавить("Тип", Новый ОписаниеТипов("Тип"));
	Результат.Колонки.Добавить("ОписаниеПодчиненныхОбъектов", Новый ОписаниеТипов("ТаблицаЗначений"));
	
	Результат.Индексы.Добавить("Тип");
	Результат.Индексы.Добавить("Метаданные");
	
	ЗаполнитьСведенияОМетаданных(Результат, УдаляемыеОбъекты);
	Возврат Результат;
	
КонецФункции

// Возвращаемое значение:
//   ТаблицаЗначений:
//   * Тип - Тип
//   * ПодчиненныеМетаданные - ОбъектМетаданных
//   * Реквизиты - Массив
//
Функция ТаблицаПоискаПодчиненныхОбъектов()
	Таблица = Новый ТаблицаЗначений;
	Таблица.Колонки.Добавить("Тип", Новый ОписаниеТипов("Тип"));
	Таблица.Колонки.Добавить("Метаданные", Новый ОписаниеТипов("ОбъектМетаданных"));
	Таблица.Колонки.Добавить("ИмяРеквизита", Новый ОписаниеТипов("Строка"));
	
	Возврат Таблица;
КонецФункции

Процедура ЗаполнитьСведенияОМетаданных(СведенияОМетаданных, УдаляемыеОбъекты)
	ПодчиненныеОбъекты = ОбщегоНазначения.ПодчиненныеОбъекты();
	
	Для Каждого Элемент Из УдаляемыеОбъекты Цикл
		Если СведенияОМетаданных.Найти(Элемент.Метаданные(), "Метаданные") = Неопределено Тогда
			ИнформацияОМетаданных = СведенияОМетаданных.Добавить();
			ИнформацияОМетаданных.Метаданные = Элемент.Метаданные();
			ИнформацияОМетаданных.Приоритет = 0;
			ИнформацияОМетаданных.Тип = ТипЗнч(Элемент);
			ИнформацияОМетаданных.ОписаниеПодчиненныхОбъектов = ТаблицаПоискаПодчиненныхОбъектов();
			ИнформацияОМетаданных.Иерархический = ЭтоИерархическийОбъектМетаданных(ИнформацияОМетаданных.Метаданные);
		КонецЕсли;
	КонецЦикла;
	
	Для Каждого ОбъектМетаданных Из Метаданные.Справочники Цикл
		ОписаниеПодчиненногоОбъекта = ПодчиненныеОбъекты.Найти(ОбъектМетаданных, "ПодчиненныйОбъект");
		
		Если ОписаниеПодчиненногоОбъекта = Неопределено
				И ОбъектМетаданных.Владельцы.Количество() > 0 Тогда
				
			ОписаниеПодчиненногоОбъекта	= ПодчиненныеОбъекты.Добавить();
			ОписаниеПодчиненногоОбъекта.ПодчиненныйОбъект = ОбъектМетаданных;
			ОписаниеПодчиненногоОбъекта.ПоляСвязей = "Владелец";
		ИначеЕсли ОбъектМетаданных.Владельцы.Количество() > 0 Тогда
			
			ОписаниеПодчиненногоОбъекта.ПоляСвязей = ?(СтрНайти(ОписаниеПодчиненногоОбъекта.ПоляСвязей, "Владелец") > 0,
				ОписаниеПодчиненногоОбъекта.ПоляСвязей,
				ОписаниеПодчиненногоОбъекта.ПоляСвязей + ",Владелец");
		КонецЕсли;
			
		ЗарегистрироватьРеквизиты(СведенияОМетаданных, ОбъектМетаданных.СтандартныеРеквизиты, ОписаниеПодчиненногоОбъекта);
		ЗарегистрироватьРеквизиты(СведенияОМетаданных, ОбъектМетаданных.Реквизиты, ОписаниеПодчиненногоОбъекта);
		ЗарегистрироватьРеквизитыТабличныхЧастей(СведенияОМетаданных, ОбъектМетаданных.ТабличныеЧасти);
	КонецЦикла;
	
	Для Каждого ОбъектМетаданных Из Метаданные.Документы Цикл
		ОписаниеПодчиненногоОбъекта = ПодчиненныеОбъекты.Найти(ОбъектМетаданных, "ПодчиненныйОбъект");
		
		ЗарегистрироватьРеквизиты(СведенияОМетаданных, ОбъектМетаданных.СтандартныеРеквизиты, ОписаниеПодчиненногоОбъекта);
		ЗарегистрироватьРеквизиты(СведенияОМетаданных, ОбъектМетаданных.Реквизиты, ОписаниеПодчиненногоОбъекта);
		ЗарегистрироватьРеквизитыТабличныхЧастей(СведенияОМетаданных, ОбъектМетаданных.ТабличныеЧасти);
	КонецЦикла;
	
	Для Каждого ОбъектМетаданных Из Метаданные.ПланыВидовХарактеристик Цикл
		ОписаниеПодчиненногоОбъекта = ПодчиненныеОбъекты.Найти(ОбъектМетаданных, "ПодчиненныйОбъект");
		
		ЗарегистрироватьРеквизиты(СведенияОМетаданных, ОбъектМетаданных.СтандартныеРеквизиты, ОписаниеПодчиненногоОбъекта);
		ЗарегистрироватьРеквизиты(СведенияОМетаданных, ОбъектМетаданных.Реквизиты, ОписаниеПодчиненногоОбъекта);
		ЗарегистрироватьРеквизитыТабличныхЧастей(СведенияОМетаданных, ОбъектМетаданных.ТабличныеЧасти);
	КонецЦикла;
	
	Для Каждого ОбъектМетаданных Из Метаданные.ПланыВидовРасчета Цикл
		ОписаниеПодчиненногоОбъекта = ПодчиненныеОбъекты.Найти(ОбъектМетаданных, "ПодчиненныйОбъект");
		
		ЗарегистрироватьРеквизиты(СведенияОМетаданных, ОбъектМетаданных.СтандартныеРеквизиты, ОписаниеПодчиненногоОбъекта);
		ЗарегистрироватьРеквизиты(СведенияОМетаданных, ОбъектМетаданных.Реквизиты, ОписаниеПодчиненногоОбъекта);
		ЗарегистрироватьРеквизитыТабличныхЧастей(СведенияОМетаданных, ОбъектМетаданных.СтандартныеТабличныеЧасти);
		ЗарегистрироватьРеквизитыТабличныхЧастей(СведенияОМетаданных, ОбъектМетаданных.ТабличныеЧасти);
	КонецЦикла;
	
	Для Каждого ОбъектМетаданных Из Метаданные.ПланыСчетов Цикл
		ОписаниеПодчиненногоОбъекта = ПодчиненныеОбъекты.Найти(ОбъектМетаданных, "ПодчиненныйОбъект");
		
		ЗарегистрироватьРеквизиты(СведенияОМетаданных, ОбъектМетаданных.СтандартныеРеквизиты, ОписаниеПодчиненногоОбъекта);
		ЗарегистрироватьРеквизиты(СведенияОМетаданных, ОбъектМетаданных.Реквизиты, ОписаниеПодчиненногоОбъекта);
		ЗарегистрироватьРеквизитыТабличныхЧастей(СведенияОМетаданных, ОбъектМетаданных.СтандартныеТабличныеЧасти);
		ЗарегистрироватьРеквизитыТабличныхЧастей(СведенияОМетаданных, ОбъектМетаданных.ТабличныеЧасти);
	КонецЦикла;
	
	Для Каждого ОбъектМетаданных Из Метаданные.БизнесПроцессы Цикл
		ОписаниеПодчиненногоОбъекта = ПодчиненныеОбъекты.Найти(ОбъектМетаданных, "ПодчиненныйОбъект");
		
		ЗарегистрироватьРеквизиты(СведенияОМетаданных, ОбъектМетаданных.СтандартныеРеквизиты, ОписаниеПодчиненногоОбъекта);
		ЗарегистрироватьРеквизиты(СведенияОМетаданных, ОбъектМетаданных.Реквизиты, ОписаниеПодчиненногоОбъекта);
		ЗарегистрироватьРеквизитыТабличныхЧастей(СведенияОМетаданных, ОбъектМетаданных.ТабличныеЧасти);
	КонецЦикла;
	
	Для Каждого ОбъектМетаданных Из Метаданные.Задачи Цикл
		ОписаниеПодчиненногоОбъекта = ПодчиненныеОбъекты.Найти(ОбъектМетаданных, "ПодчиненныйОбъект");
		
		ЗарегистрироватьРеквизиты(СведенияОМетаданных, ОбъектМетаданных.СтандартныеРеквизиты, ОписаниеПодчиненногоОбъекта);
		ЗарегистрироватьРеквизиты(СведенияОМетаданных, ОбъектМетаданных.РеквизитыАдресации, ОписаниеПодчиненногоОбъекта);
		ЗарегистрироватьРеквизиты(СведенияОМетаданных, ОбъектМетаданных.Реквизиты, ОписаниеПодчиненногоОбъекта);
		ЗарегистрироватьРеквизитыТабличныхЧастей(СведенияОМетаданных, ОбъектМетаданных.ТабличныеЧасти);
	КонецЦикла;
КонецПроцедуры

Процедура ЗарегистрироватьРеквизитыТабличныхЧастей(СведенияОМетаданных, ТабличныеЧасти)
	Для Каждого ТабличнаяЧасть Из ТабличныеЧасти Цикл
		Если ТипЗнч(ТабличнаяЧасть) = Тип("ОбъектМетаданных") Тогда
			ЗарегистрироватьРеквизиты(СведенияОМетаданных, ТабличнаяЧасть.Реквизиты);	
		Иначе
			ЗарегистрироватьРеквизиты(СведенияОМетаданных, ТабличнаяЧасть.СтандартныеРеквизиты);
		КонецЕсли;
	КонецЦикла;
КонецПроцедуры

Процедура ЗарегистрироватьРеквизиты(СведенияОМетаданных, КоллекцияРеквизитов, ОписаниеПодчиненногоОбъекта = Неопределено)
	
	Для Каждого Реквизит Из КоллекцияРеквизитов Цикл
		ЗарегистрироватьРеквизит(СведенияОМетаданных, Реквизит, ОписаниеПодчиненногоОбъекта);
	КонецЦикла;
	
КонецПроцедуры

Процедура ЗарегистрироватьРеквизит(СведенияОМетаданных, Реквизит,
		ОписаниеПодчиненногоОбъекта)
	ИменаПолейСвязи = ?(ЗначениеЗаполнено(ОписаниеПодчиненногоОбъекта), СтрРазделить(ОписаниеПодчиненногоОбъекта.ПоляСвязей, ","), Новый Массив);
	ЭтоПолеСвязиПодчиненногоОбъекта = ИменаПолейСвязи.Найти(Реквизит.Имя) <> Неопределено;

	Для Каждого Тип Из Реквизит.Тип.Типы() Цикл
		Если УдалениеПомеченныхОбъектовСлужебный.ЭтоПростойТип(Тип) Тогда
			Продолжить;
		КонецЕсли;

		НайденныеСведения = СведенияОМетаданных.Найти(Тип, "Тип");
		Если НайденныеСведения <> Неопределено Тогда
			НайденныеСведения.Приоритет = НайденныеСведения.Приоритет + 1;

			Если ЭтоПолеСвязиПодчиненногоОбъекта Тогда
				Связь = НайденныеСведения.ОписаниеПодчиненныхОбъектов.Добавить();
				Связь.Тип = Тип;
				Связь.Метаданные = ОписаниеПодчиненногоОбъекта.ПодчиненныйОбъект;
				Связь.ИмяРеквизита = Реквизит.Имя;
				НайденныеСведения.ЕстьПодчиненныеОбъекты = Истина;
			КонецЕсли;
		КонецЕсли;
	КонецЦикла;
КонецПроцедуры

Функция ЭтоИерархическийОбъектМетаданных(ОбъектМетаданных)

	Если ОбщегоНазначения.ЭтоПланСчетов(ОбъектМетаданных) Тогда
		Возврат Истина;
	Иначе
		ЗначениеРеквизита = Новый Структура("Иерархический");
		ЗаполнитьЗначенияСвойств(ЗначениеРеквизита, ОбъектМетаданных);
		Возврат ЗначениеРеквизита.Иерархический;
	КонецЕсли;
	
КонецФункции

#КонецОбласти

#КонецОбласти

#Область МонопольноеУдаление

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

			Сообщения = ДлительныеОперации.СообщенияПользователю(Истина);
			Для каждого Сообщение Из Сообщения Цикл 
				ОписаниеОшибки = ОписаниеОшибки + Символы.ПС + Символы.ПС + Сообщение.Текст;
			КонецЦикла;
			ПослеУдаленияГруппыОбъектов(Контекст, Ложь);
			
			ВызватьИсключение ОписаниеОшибки;
		КонецПопытки;
		
		УстановитьПривилегированныйРежим(Ложь);
		
		Если ПрепятствующиеУдалению.Колонки.Количество() < 3 Тогда
			ВызватьИсключение НСтр("ru = 'Не удалось выполнить удаление объектов.'");
		КонецЕсли;
		
		СлужебныеСвязиДанных = ОбщегоНазначения.СлужебныеСвязиДанных(ПрепятствующиеУдалению, ПараметрыВыполнения.ИсключенияПоискаСсылок);

		// Назначение имен колонок для таблицы конфликтов, возникших при удалении.
		ПрепятствующиеУдалению.Колонки[0].Имя = "УдаляемыйСсылка";
		ПрепятствующиеУдалению.Колонки[1].Имя = "МестоИспользования";
		ПрепятствующиеУдалению.Колонки[2].Имя = "Метаданные";
		
		ВсеСвязиВИсключениях = Истина;
		
		ПослеУдаленияГруппыОбъектов(Контекст, ПрепятствующиеУдалению.Количество() = 0);
		
		ИнформацияОТипах = УдалениеПомеченныхОбъектовСлужебный.ИнформацияОТипах(
			ПрепятствующиеУдалению.ВыгрузитьКолонку("УдаляемыйСсылка"), ИнформацияОТипах); 

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

Процедура ЗаписатьПредупреждение(Знач Описание, Знач ПредставлениеЭлемента, Знач ИнформацияОбОшибке)
	Если ТипЗнч(ИнформацияОбОшибке) = Тип("ИнформацияОбОшибке") Тогда
		ТекстДляЖурнала = ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке);
	Иначе
		ТекстДляЖурнала = ИнформацияОбОшибке;
	КонецЕсли;
	ТекстДляЖурнала = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(Описание, ПредставлениеЭлемента) 
		+ Символы.ПС + ТекстДляЖурнала;
	ЗаписьЖурналаРегистрации(НСтр("ru = 'Удаление помеченных'", ОбщегоНазначения.КодОсновногоЯзыка()),
		УровеньЖурналаРегистрации.Предупреждение,,, ТекстДляЖурнала);
КонецПроцедуры

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

	Причина = ПараметрыВыполнения.ПрепятствующиеУдалению.Добавить();
	ЗаполнитьЗначенияСвойств(Причина, СтрокаТаблицы);

	МестоИспользования = СтрокаТаблицы.МестоИспользования;	 
	Если ТипЗнч(МестоИспользования) = Тип("Строка") Тогда
		Причина.ОписаниеОшибки = МестоИспользования;
		Причина.МестоИспользования = Неопределено;
	КонецЕсли; 

	Если МестоИспользования = Неопределено 
		И НЕ Метаданные.Константы.Содержит(СтрокаТаблицы.Метаданные) Тогда
			Причина.ОписаниеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Обнаружены неразрешимые ссылки (%1)'"),
				СтрокаТаблицы.Метаданные.Представление());
	КонецЕсли;
	
КонецПроцедуры

Функция СвязьТолькоСВедущимиИзмерениямиРегистров(ОписаниеСвязиДанных)

	Результат = Ложь;
	Если Не ОбщегоНазначения.ЭтоРегистр(ОписаниеСвязиДанных.Метаданные) Тогда
		Возврат Результат;
	КонецЕсли;	

	Для каждого Измерение Из ОписаниеСвязиДанных.Метаданные.Измерения Цикл
		
		Если ОписаниеСвязиДанных.МестоИспользования[Измерение.Имя] = ОписаниеСвязиДанных.УдаляемыйСсылка
				И Измерение.Ведущее Тогда
			Результат = Истина;
			Прервать;
		КонецЕсли;
		
		Если ОписаниеСвязиДанных.МестоИспользования["Регистратор"] = ОписаниеСвязиДанных.УдаляемыйСсылка Тогда
			Результат = Истина;
		КонецЕсли;
		
	КонецЦикла;	
	Возврат Результат;
	
КонецФункции

#Область ПередачаИнформацииНаКлиент

// Параметры:
//   ПараметрыВыполнения - см. РезультатУдаления 
//   ИмяКоллекции - Строка
//
Процедура ОтметитьПрогрессОбходаКоллекции(ПараметрыВыполнения, ИмяКоллекции)
	ПараметрыВыполнения.Номер = ПараметрыВыполнения.Номер + 1;
	
	Процент = Окр(100 * (ПараметрыВыполнения.КоличествоОбработанных)
		/ ПараметрыВыполнения.КоличествоУдаляемых);
	ДополнительныеПараметры = Неопределено;
	
	// Подготовка передаваемых параметров.
	Если ИмяКоллекции = "ПередПоискомПомеченныхНаУдаление" Тогда
		
		Текст = НСтр("ru = 'Подготовка к поиску объектов, помеченных на удаление.'");
		
	ИначеЕсли ИмяКоллекции = "НайтиПомеченныеНаУдаление" Тогда
		
		Текст = НСтр("ru = 'Поиск объектов, помеченных на удаление.'");
		
	ИначеЕсли ИмяКоллекции = "ВсеПомеченныеНаУдаление" Тогда
		
		Текст = НСтр("ru = 'Анализ помеченных на удаление.'");
		
	ИначеЕсли ИмяКоллекции = "МонопольноеУдаление" Тогда
		
		Текст = НСтр("ru = 'Выполняется удаление объектов.'");
		
	ИначеЕсли ИмяКоллекции = "ПользовательскиеОбъекты" Тогда
		
		ДополнительныеПараметры = Новый Структура;
		ДополнительныеПараметры.Вставить("НомерСеанса", НомерСеансаИнформационнойБазы());
		ДополнительныеПараметры.Вставить("КоличествоОбработанных", ПараметрыВыполнения.КоличествоОбработанных);
		Текст = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Обработано %1 из %2'"),
			ПараметрыВыполнения.КоличествоОбработанных,
			ПараметрыВыполнения.КоличествоУдаляемых);
		
	ИначеЕсли ИмяКоллекции = "ПовторноУдаляемые" Тогда
		
		Текст = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Повторная проверка не удаленных объектов: %1 из %2.'"),
			Формат(ПараметрыВыполнения.Номер, "ЧН=0; ЧГ="),
			Формат(ПараметрыВыполнения.Всего, "ЧН=0; ЧГ="));
		
	ИначеЕсли ИмяКоллекции = "ПрепятствующиеУдалению" Тогда
		
		Текст = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Анализ объектов, препятствующих удалению: %1 из %2.'"),
			Формат(ПараметрыВыполнения.Номер, "ЧН=0; ЧГ="),
			Формат(ПараметрыВыполнения.Всего, "ЧН=0; ЧГ="));
		
	Иначе
		
		Возврат;
		
	КонецЕсли;
	
	ДлительныеОперации.СообщитьПрогресс(Процент, Текст, ДополнительныеПараметры);
КонецПроцедуры

#КонецОбласти

#КонецОбласти

Функция ОбъединениеТаблиц(Таблица1, Таблица2, СохранятьПорядок = Ложь)
	Возврат УдалениеПомеченныхОбъектовСлужебный.ОбъединениеТаблиц(Таблица1, Таблица2, СохранятьПорядок);
КонецФункции

Процедура ОтправитьСтатистику(РежимУдаления, ВремяВыполнения, КоличествоУдаляемыхЭлементов)
	
	Если НЕ ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ЦентрМониторинга") Тогда
		Возврат;
	КонецЕсли;	
	
	МодульЦентрМониторинга = ОбщегоНазначения.ОбщийМодуль("ЦентрМониторинга");
	МодульЦентрМониторинга.ЗаписатьОперациюБизнесСтатистики(
		"БазоваяФункциональность.УдалениеПомеченных."+РежимУдаления, ВремяВыполнения,
		СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru='Количество элементов: %1'"), КоличествоУдаляемыхЭлементов));
		
КонецПроцедуры

Функция ОтменаИсключенийПоискаСсылокПриУдаленииОбъектов()

	ОтменаИсключений = Новый Массив;
	ОтменаИсключений.Добавить(Метаданные.Справочники.ВнешниеПользователи);
	Возврат ОтменаИсключений;

КонецФункции

#КонецОбласти

#КонецЕсли

Связанные материалы