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

#Область СлужебныйПрограммныйИнтерфейс

#Область ДляВызоваИзДругихПодсистем

// См. ОбновлениеИнформационнойБазыБСП.ПриДобавленииОбработчиковОбновления.
Процедура ПриДобавленииОбработчиковОбновления(Обработчики) Экспорт

	Обработчик = Обработчики.Добавить();
	Обработчик.Версия = "2.4.1.1";
	Обработчик.НачальноеЗаполнение = Истина;
	Обработчик.Процедура = "УдалениеПомеченныхОбъектовСлужебный.ВключитьУдалениеПомеченныхОбъектов";
	Обработчик.РежимВыполнения = "Оперативно";

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

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

// См. РегламентныеЗаданияПереопределяемый.ПриОпределенииНастроекРегламентныхЗаданий
Процедура ПриОпределенииНастроекРегламентныхЗаданий(Настройки) Экспорт

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

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

// См. ОчередьЗаданийПереопределяемый.ПриПолученииСпискаШаблонов.
Процедура ПриПолученииСпискаШаблонов(ШаблоныЗаданий) Экспорт

	ШаблоныЗаданий.Добавить(Метаданные.РегламентныеЗадания.УдалениеПомеченныхОбъектов.Имя);
	ШаблоныЗаданий.Добавить(Метаданные.РегламентныеЗадания.КонтрольУдаленияПомеченныхОбъектов.Имя);

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

// См. ОчередьЗаданийПереопределяемый.ПриОпределенииПсевдонимовОбработчиков.
Процедура ПриОпределенииПсевдонимовОбработчиков(СоответствиеИменПсевдонимам) Экспорт

	СоответствиеИменПсевдонимам.Вставить(Метаданные.РегламентныеЗадания.УдалениеПомеченныхОбъектов.ИмяМетода);
	СоответствиеИменПсевдонимам.Вставить(Метаданные.РегламентныеЗадания.КонтрольУдаленияПомеченныхОбъектов.ИмяМетода);

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

// См. ОбщегоНазначенияПереопределяемый.ПриДобавленииИсключенийПоискаСсылок.
Процедура ПриДобавленииИсключенийПоискаСсылок(ИсключенияПоискаСсылок) Экспорт

	ИсключенияПоискаСсылок.Добавить(Метаданные.РегистрыСведений.УдаляемыеОбъекты);
	ИсключенияПоискаСсылок.Добавить(Метаданные.РегистрыСведений.НеудаленныеОбъекты);

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

// См. ПодключаемыеКомандыПереопределяемый.ПриОпределенииКомандПодключенныхКОбъекту.
Процедура ПриОпределенииКомандПодключенныхКОбъекту(НастройкиФормы, Источники, ПодключенныеОтчетыИОбработки, Команды) Экспорт

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

		Источник = Источники.Строки.Найти(ПодключенныйОбъект, "Метаданные");
		Если Источник <> Неопределено Тогда

			Команда = Команды.Добавить();
			Команда.Вид = "ОтображениеПомеченныхНаУдаление";
			Команда.Важность = "СмТакже";
			Команда.Представление = НСтр("ru = 'Показать помеченные на удаление'");
			Команда.РежимЗаписи = "НеЗаписывать";
			Команда.ВидимостьВФормах = "ФормаСписка";
			Команда.МножественныйВыбор = Ложь;
			Команда.Обработчик = "УдалениеПомеченныхОбъектовКлиент.ВыполнитьПодключаемуюКомандуПоказатьПомеченныеНаУдаление";
			Команда.ТолькоВоВсехДействиях = Истина;
			Команда.ЗначениеПометки = "ПараметрыУдаленияПомеченных.%Источник%.ЗначениеПометки";
			Команда.Порядок = 20;

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

		КонецЕсли;

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

// См. ПодключаемыеКомандыПереопределяемый.ПриОпределенииВидовПодключаемыхКоманд.
Процедура ПриОпределенииВидовПодключаемыхКоманд(ВидыПодключаемыхКоманд) Экспорт

	Если ВидыПодключаемыхКоманд.Найти("ОтображениеПомеченныхНаУдаление", "Имя") = Неопределено Тогда

		Вид = ВидыПодключаемыхКоманд.Добавить();
		Вид.Имя         = "ОтображениеПомеченныхНаУдаление";
		Вид.ИмяПодменю  = "Сервис";
		Вид.Заголовок   = НСтр("ru = 'Сервис'");
		Вид.Порядок     = 80;
		Вид.Картинка    = БиблиотекаКартинок.ПодменюСервис;
		Вид.Отображение = ОтображениеКнопки.КартинкаИТекст;

	КонецЕсли;

	Если ВидыПодключаемыхКоманд.Найти("ПерейтиКПомеченнымНаУдаление", "Имя") = Неопределено Тогда

		Вид = ВидыПодключаемыхКоманд.Добавить();
		Вид.Имя         = "ПерейтиКПомеченнымНаУдаление";
		Вид.ИмяПодменю  = "Сервис";
		Вид.Заголовок   = НСтр("ru = 'Сервис'");
		Вид.Порядок     = 80;
		Вид.Картинка    = БиблиотекаКартинок.ПодменюСервис;
		Вид.Отображение = ОтображениеКнопки.КартинкаИТекст;

	КонецЕсли;

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

// Параметры:
//   ТекущиеДела - см. ТекущиеДелаСервер.ТекущиеДела.
//
Процедура ПриЗаполненииСпискаТекущихДел(ТекущиеДела) Экспорт
	Если Не Пользователи.ЭтоПолноправныйПользователь() Тогда
		Возврат;
	КонецЕсли;

	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	ISNULL(КОЛИЧЕСТВО(НеудаленныеОбъекты.Объект),0) КАК Всего
	|ИЗ
	|	РегистрСведений.НеудаленныеОбъекты КАК НеудаленныеОбъекты
	|ГДЕ
	|	НеудаленныеОбъекты.КоличествоПопыток > 3";

	РезультатЗапроса = Запрос.Выполнить();
	КоличествоНеудаленныхОбъектов = РезультатЗапроса.Выбрать();
	КоличествоНеудаленныхОбъектов.Следующий();

	МодульТекущиеДелаСервер = ОбщегоНазначения.ОбщийМодуль("ТекущиеДелаСервер");

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

	ИдентификаторЗадания = "НеудаленныеОбъекты";
	Для Каждого Раздел Из Разделы Цикл

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

	КонецЦикла;

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

// См. ОбщегоНазначенияПереопределяемый.ПриДобавленииОбработчиковУстановкиПараметровСеанса.
Процедура ПриДобавленииОбработчиковУстановкиПараметровСеанса(Обработчики) Экспорт

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

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

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

// См. ВыгрузкаЗагрузкаДанныхПереопределяемый.ПриЗаполненииТиповИсключаемыхИзВыгрузкиЗагрузки.
Процедура ПриЗаполненииТиповИсключаемыхИзВыгрузкиЗагрузки(Типы) Экспорт

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

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

// См. ИнтерфейсODataПереопределяемый.ПриЗаполненииЗависимыхТаблицДляВыгрузкиЗагрузкиOData
Процедура ПриЗаполненииЗависимыхТаблицДляВыгрузкиЗагрузкиOData(Таблицы) Экспорт

	Таблицы.Добавить(Метаданные.РегистрыСведений.НеудаленныеОбъекты.ПолноеИмя());
	Таблицы.Добавить(Метаданные.РегистрыСведений.УдаляемыеОбъекты.ПолноеИмя());

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

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

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

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

#Область ОбработчикиПодписок

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

Процедура ЗапретитьИспользованиеУдаляемыхОбъектовВНаборахЗаписейПриЗаписи(Источник, Отказ, Замещение) Экспорт
	ЗапретитьИспользованиеУдаляемыхОбъектов(Источник, Отказ);
КонецПроцедуры

Процедура ЗапретитьИспользованиеУдаляемыхОбъектовВНаборахРегистровРасчетаПриЗаписи(Источник, Отказ, Замещение) Экспорт
	ЗапретитьИспользованиеУдаляемыхОбъектов(Источник, Отказ);
КонецПроцедуры

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

Процедура ЗапретитьИспользованиеУдаляемыхОбъектовВДокументахПередЗаписью(Источник, Отказ, РежимЗаписи, РежимПроведения) Экспорт
	ЗапретитьИспользованиеУдаляемыхОбъектов(Источник, Отказ);
КонецПроцедуры

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

Функция ДопустимыеРежимыУдаления() Экспорт

	ДопустимыеРежимы = Новый Массив;
	ДопустимыеРежимы.Добавить("Стандартный");
	ДопустимыеРежимы.Добавить("Монопольный");
	ДопустимыеРежимы.Добавить("Упрощенный");
	Возврат ДопустимыеРежимы;

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

Процедура ВключитьУдалениеПомеченныхОбъектов() Экспорт
	Константы.ИспользоватьУдалениеПомеченныхОбъектов.Установить(Истина);
КонецПроцедуры

Процедура УстановитьРасписаниеУдаленияПомеченных() Экспорт

	Если ОбщегоНазначения.РазделениеВключено() Тогда
		Возврат;
	КонецЕсли;

	Расписание = Новый РасписаниеРегламентногоЗадания;
	Расписание.ПериодПовтораДней = 1;
	Расписание.ПериодНедель = 1;
	Расписание.ВремяНачала = '00010101040000'; // в 04:00 
	Расписание.ВремяКонца = '00010101060000'; // в 06:00 
	Расписание.ВремяЗавершения = '00010101060000'; // 06:00 

	ПараметрыЗадания = Новый Структура;
	ПараметрыЗадания.Вставить("Расписание", Расписание);
	ПараметрыЗадания.Вставить("ИнтервалПовтораПриАварийномЗавершении", 10);
	ПараметрыЗадания.Вставить("КоличествоПовторовПриАварийномЗавершении", 3);

	РегламентныеЗаданияСервер.УстановитьПараметрыРегламентногоЗадания(
		Метаданные.РегламентныеЗадания.УдалениеПомеченныхОбъектов, ПараметрыЗадания);

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

// Точка входа регламентного задания.
//
Процедура УдалениеПомеченныхПоРасписанию() Экспорт

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

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

// Возвращает Истина, если переданный тип является просты
// 
// Параметры:
//   Тип - Тип
//
// Возвращаемое значение:
//   Булево
//
Функция ЭтоПростойТип(Тип) Экспорт
	Возврат (Тип = Неопределено Или Тип = Тип("Строка") Или Тип = Тип("Число") Или Тип = Тип("Булево") Или Тип = Тип(
		"Дата"));
КонецФункции

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

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

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

	ВремяЖизниБлокировки = ВремяЖизниБлокировки();
	ВремяСнятияБлокировки = ТекущаяДатаСеанса() - ВремяЖизниБлокировки;

	ТекстЗапроса =
	"ВЫБРАТЬ РАЗЛИЧНЫЕ ПЕРВЫЕ 1000
	|	УдаляемыеОбъекты.ИдентификаторСеанса КАК ИдентификаторСеанса,
	|	УдаляемыеОбъекты.Период КАК ВремяБлокировки
	|ИЗ
	|	РегистрСведений.УдаляемыеОбъекты КАК УдаляемыеОбъекты
	|ГДЕ
	|	УдаляемыеОбъекты.Период <= &ВремяСнятияБлокировки
	|	И &Условие
	|
	|УПОРЯДОЧИТЬ ПО
	|	ВремяБлокировки";
	УсловиеПоВремени = "УдаляемыеОбъекты.Период > &ВремяБлокировки";
	ТекстЗапросаСУсловием = СтрЗаменить(ТекстЗапроса, "&Условие", УсловиеПоВремени);

	Запрос = Новый Запрос(СтрЗаменить(ТекстЗапроса, "&Условие", "ИСТИНА"));
	Запрос.УстановитьПараметр("ВремяСнятияБлокировки", ВремяСнятияБлокировки);
	РезультатЗапроса = Запрос.Выполнить().Выгрузить();

	ОбработанныеСеансы = Новый Соответствие;
	Пока РезультатЗапроса.Количество() > 0 Цикл

		Для Каждого ВыборкаДетальныеЗаписи Из РезультатЗапроса Цикл
			СеансУдаленияОбработан = ОбработанныеСеансы[ВыборкаДетальныеЗаписи.ИдентификаторСеанса];
			Если СеансУдаленияОбработан = Истина Тогда
				Продолжить;
			КонецЕсли;

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

		ОбработанныеСеансы = Новый Соответствие;
		Запрос.Текст = ТекстЗапросаСУсловием;
		Запрос.УстановитьПараметр("ВремяБлокировки", ВыборкаДетальныеЗаписи.ВремяБлокировки);
		Запрос.УстановитьПараметр("ВремяСнятияБлокировки", ВремяСнятияБлокировки);
		РезультатЗапроса = Запрос.Выполнить().Выгрузить(); // @skip-check query-in-loop - Порционная обработка больших объемов данных.

	КонецЦикла;

	ПопробоватьОтключитьКонтрольУдаленияПомеченныхОбъектов();

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

// Возвращает максимальное количество секунд, в течение которого должен быть установлен 
// запрет использования удаляемого объекта в других объектах. Значение равно трем часам.
// 
// Возвращаемое значение:
//   Число
//
Функция ВремяЖизниБлокировки() Экспорт

	Возврат 3 * 60 * 60;

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

// Формирует строковый ключ для сохранения настроек отображения помеченных на удаления.
//
// Параметры:
//   ИмяФормы - Строка
//   ИмяСписка - Строка
//
// Возвращаемое значение:
//   Строка
//
Функция КлючНастроек(ИмяФормы, ИмяСписка) Экспорт

	Ключ = "ПоказыватьПомеченныеНаУдаление/" + ИмяФормы + "/" + ИмяСписка;
	Возврат Ключ;

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

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

	Если ДополняемаяИнформацияОТипах = Неопределено Тогда
		ДополняемаяИнформацияОТипах = Новый Соответствие;
	КонецЕсли;

	Информация = ДополняемаяИнформацияОТипах[ТипОбъекта];
	Если Информация <> Неопределено Тогда
		Возврат Информация;
	КонецЕсли;

	Информация = Новый Структура("ПолноеИмя, ПредставлениеЭлемента, ПредставлениеСписка,
								 |Вид, Ссылочный, Технический, Разделенный");
	ДополняемаяИнформацияОТипах[ТипОбъекта] = Информация;

	ОбъектМетаданных = Метаданные.НайтиПоТипу(ТипОбъекта);

	Информация.ПолноеИмя = ВРег(ОбъектМетаданных.ПолноеИмя());
	Информация.Вид = Лев(Информация.ПолноеИмя, СтрНайти(Информация.ПолноеИмя, ".") - 1);
	Информация.ПредставлениеЭлемента = ОбщегоНазначения.ПредставлениеОбъекта(ОбъектМетаданных);
	Информация.ПредставлениеСписка = ОбщегоНазначения.ПредставлениеСписка(ОбъектМетаданных);
	Информация.Ссылочный = ОбщегоНазначения.ЭтоОбъектСсылочногоТипа(ОбъектМетаданных);
	Информация.Технический = ЭтоТехническийОбъект(Информация.ПолноеИмя);

	Если ОбщегоНазначения.РазделениеВключено() Тогда

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

	КонецЕсли;
	Возврат Информация;

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

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

	Результат = ?(ТипЗнч(ДополняемаяИнформацияОТипах) = Тип("Соответствие"), ДополняемаяИнформацияОТипах,
		Новый Соответствие);

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

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

Функция ОбъединениеТаблиц(Таблица1, Таблица2, СохранятьПорядок = Ложь) Экспорт
	Если СохранятьПорядок Или Таблица1.Количество() >= Таблица2.Количество() Тогда
		Приемник = Таблица1.Скопировать();
		Источник = Таблица2;
	Иначе
		Приемник = Таблица2.Скопировать();
		Источник = Таблица1;
	КонецЕсли;

	Для Каждого Элемент Из Источник Цикл
		ЗаполнитьЗначенияСвойств(Приемник.Добавить(), Элемент);
	КонецЦикла;

	Возврат Приемник;
КонецФункции

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

	Если ПромежуточнаяТаблица.Количество() > 0 Тогда
		Результат = ПромежуточнаяТаблица[0].Счетчик;
	КонецЕсли;

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

Функция ЕстьАктуальныеЗаблокированныеОбъекты()

	ВремяЖизниБлокировки = ВремяЖизниБлокировки();
	ВремяСнятияБлокировки = ТекущаяДатаСеанса() - ВремяЖизниБлокировки;
	
	Запрос = Новый Запрос();
	Запрос.Текст =
	"ВЫБРАТЬ РАЗЛИЧНЫЕ ПЕРВЫЕ 1
	|	УдаляемыеОбъекты.ИдентификаторСеанса КАК ИдентификаторСеанса,
	|	УдаляемыеОбъекты.Период КАК ВремяБлокировки
	|ИЗ
	|	РегистрСведений.УдаляемыеОбъекты КАК УдаляемыеОбъекты
	|ГДЕ
	|	УдаляемыеОбъекты.Период > &ВремяСнятияБлокировки";
	
	Запрос.УстановитьПараметр("ВремяСнятияБлокировки", ВремяСнятияБлокировки);
	РезультатЗапроса = Запрос.Выполнить();
	
	Возврат Не РезультатЗапроса.Пустой();
	
КонецФункции

#Область ОбработчикиКомандФормыУдаленияПомеченных

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

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

	Результат = РезультатОбработкиУдаляемыхОбъектов();

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

		Если Действие.Действие <> "ЗаменитьСсылку" И (Не ЗначениеЗаполнено(ВерсииДанных[Действие.Источник])
			Или Не ЗначениеЗаполнено(Действие.ОбнаруженныйСсылка)
			Или Не ЗначениеЗаполнено(Значение(Действие.ОбнаруженныйСсылка, "ВерсияДанных"))) Тогда

			Продолжить;
		КонецЕсли;

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

	Если ОчередьПометкиКУдалению.Количество() > 0 Тогда
		Результат = ОбъединениеТаблиц(Результат, ОбработатьОчередьПометокНаУдаление(ОчередьПометкиКУдалению));
	КонецЕсли;

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

	Возврат Результат;

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

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

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

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

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

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

// Записывает информацию, необходимую для блокировки удаляемых объектов, и включает регламентное задание
// проверки использования удаляемых объектов.
// Не используется для неразделенного сеанса в модели сервиса.
//
Процедура УстановитьБлокировкуИспользованияУдаляемыхОбъектов(Пакет, ИдентификаторСеанса) Экспорт

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

	НаборЗаписей = РегистрыСведений.УдаляемыеОбъекты.СоздатьНаборЗаписей();
	НаборЗаписей.Отбор.ИдентификаторСеанса.Установить(ИдентификаторСеанса);

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

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

		Константы.ПроверятьИспользованиеУдаляемыхОбъектов.Установить(Истина);
		НаборЗаписей.Записать();

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

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

	НаборЗаписей = РегистрыСведений.УдаляемыеОбъекты.СоздатьНаборЗаписей();
	НаборЗаписей.Отбор.ИдентификаторСеанса.Установить(ИдентификаторСеанса);
	Если ВремяСнятияБлокировки <> Неопределено Тогда
		НаборЗаписей.Отбор.Период.Установить(ВремяСнятияБлокировки);
	КонецЕсли;
	НаборЗаписей.Записать();

	Если ВремяСнятияБлокировки = Неопределено Тогда
		ПопробоватьОтключитьКонтрольУдаленияПомеченныхОбъектов();
	КонецЕсли;

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

Процедура ПопробоватьОтключитьКонтрольУдаленияПомеченныхОбъектов()

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

		Если Константы.ПроверятьИспользованиеУдаляемыхОбъектов.Получить() Тогда
			Запрос = Новый Запрос("ВЫБРАТЬ ПЕРВЫЕ 1 Таб.ИдентификаторСеанса ИЗ РегистрСведений.УдаляемыеОбъекты КАК Таб");
			Если Запрос.Выполнить().Пустой() Тогда
				Константы.ПроверятьИспользованиеУдаляемыхОбъектов.Установить(Ложь);

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

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

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

	ЗначенияПометкиУдаления = ОбщегоНазначения.ЗначениеРеквизитаОбъектов(Ссылки, "ПометкаУдаления");

	КоличествоНепомеченныхНаУдаление = 0;
	Для Каждого СсылкаЗначениеРеквизита Из ЗначенияПометкиУдаления Цикл
		Если СсылкаЗначениеРеквизита.Значение = Ложь Тогда
			КоличествоНепомеченныхНаУдаление = КоличествоНепомеченныхНаУдаление + 1;
		КонецЕсли;
	КонецЦикла;

	Если КоличествоНепомеченныхНаУдаление = ЗначенияПометкиУдаления.Количество() Тогда
		ИтоговоеЗначениеПометкиУдаления = Истина;
	КонецЕсли;

	НачатьТранзакцию();
	Попытка

		Для Каждого СсылкаЗначениеРеквизита Из ЗначенияПометкиУдаления Цикл

			Если СсылкаЗначениеРеквизита.Значение <> ИтоговоеЗначениеПометкиУдаления Тогда
				СтрокаРезультата = Результат.Добавить();
				СтрокаРезультата.Ссылка = СсылкаЗначениеРеквизита.Ключ;

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

		КонецЦикла;
		ЗафиксироватьТранзакцию();

	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;

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

#Область ПоискПомеченныхНаУдаление

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

	ДополнительныеРеквизитыУдаляемыхОбъектов = СтандартныеПодсистемыСервер.ЗначенияРеквизитовОбъектовЕслиСуществуют(УдаляемыеОбъекты, "Дата");

	УзлыПервогоУровня = Новый Соответствие;

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

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

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

		Если ПометкиУстанавливаютсяВыборочно И Отмеченные.Найти(УдаляемыйСсылка) = Неопределено Тогда
			УзелУдаляемого.Пометка = Ложь;
			УзелТипа.Пометка       = Ложь;
		КонецЕсли;

	КонецЦикла;

	ДеревоЗначений = ДополнитьДеревоДополнительнымиРеквизитами(ДеревоЗначений, Настройки);

	ДеревоЗначений.Колонки.Удалить(ДеревоЗначений.Колонки.Количество);
		ДеревоЗначений.Строки.Сортировать("Дата, Представление", Истина);

	Возврат ДеревоЗначений;
КонецФункции

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

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

	Для Каждого Элемент Из Настройки Цикл
		Элемент.Метаданные = ВРег(Элемент.Метаданные);
	КонецЦикла;

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

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

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

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

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

	Для Каждого Реквизит Из НастройкиДополнительныхРеквизитов Цикл
		ДополнительныеРеквизиты.Добавить(Реквизит.Реквизит);
	КонецЦикла;

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

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

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

#Область ВыполнениеДополнительнойОбработкиПричинНеудаления

Функция ОбработатьОчередьПометокНаУдаление(ОчередьПометкиКУдалению)
	Результат = РезультатОбработкиУдаляемыхОбъектов();

	ПредставленияОшибок = Новый Соответствие;
	ОбъектыСОшибками = Новый Массив;

	Для Каждого Элемент Из ОчередьПометкиКУдалению Цикл

		Описание = Результат.Добавить();
		Описание.УдаляемыйСсылка = Элемент;
		Описание.ТребуетсяУдаление = Истина;

		НачатьТранзакцию();
		Попытка
			Блокировка = Новый БлокировкаДанных;
			ЭлементБлокировки = Блокировка.Добавить(Элемент.Метаданные().ПолноеИмя());
			ЭлементБлокировки.УстановитьЗначение("Ссылка", Элемент);
			Блокировка.Заблокировать();

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

		Исключение
			ОтменитьТранзакцию();
			Ошибка = ИнформацияОбОшибке();
			Описание.ТребуетсяУдаление = Ложь;
			Описание.ТекстОшибки = ОбработкаОшибок.КраткоеПредставлениеОшибки(Ошибка);

			ПредставленияОшибок[Элемент] = ОбработкаОшибок.ПодробноеПредставлениеОшибки(Ошибка);
			ОбъектыСОшибками.Добавить(Элемент);
		КонецПопытки;

	КонецЦикла;

	Если ПредставленияОшибок.Количество() > 0 Тогда

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

	КонецЕсли;

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

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

#Область КонтрольИспользованияУдаляемыхОбъектов

Процедура ЗапретитьИспользованиеУдаляемыхОбъектов(Источник, Отказ)
	
	// ОбменДанными.Загрузка = Истина не устанавливается, т.к. необходимо выполнение
	// при загрузке из внешних источников

	Если МонопольныйРежим() Тогда
		Возврат;
	КонецЕсли;

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

	Если ОбщегоНазначения.ЭтоКонстанта(Источник.Метаданные()) Тогда
		ТипЗначений = ТипЗнч(Источник.Значение);

		Если Не ОбщегоНазначения.ЭтоСсылка(ТипЗначений) Тогда
			Возврат;
		КонецЕсли;
	КонецЕсли;

	УстановитьПривилегированныйРежим(Истина);

	Если Не ПроверятьИспользованиеУдаляемыхОбъектов() Тогда
		Возврат;
	КонецЕсли;
	
	Если Не ЕстьАктуальныеЗаблокированныеОбъекты() Тогда
		Возврат;
	КонецЕсли;

	Если ИсключенияПоискаСсылокРазрешающихУдаление().Найти(Источник.Метаданные()) <> Неопределено Тогда
		Возврат;
	КонецЕсли;

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

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

	ТекстСообщения = "";
	Если СсылкиНаУдаляемыеОбъекты.Количество() = 1 Тогда
		ПредставлениеСсылки = "";
		Для Каждого УдаляемаяСсылка Из СсылкиНаУдаляемыеОбъекты Цикл
			ПредставлениеСсылки = ОбщегоНазначения.ПредметСтрокой(УдаляемаяСсылка.Ключ);
		КонецЦикла;

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

	ИначеЕсли СсылкиНаУдаляемыеОбъекты.Количество() > 1 Тогда

		ПредставлениеСсылок = Новый Массив;
		Для Каждого УдаляемаяСсылка Из СсылкиНаУдаляемыеОбъекты Цикл
			ПредставлениеСсылок.Добавить(ОбщегоНазначения.ПредметСтрокой(УдаляемаяСсылка.Ключ));
		КонецЦикла;

		ТекстСообщения = НСтр("ru = 'Выбранные элементы в данный момент удаляются, т.к. были помечены на удаление.
							  |Выберите другие значения.
							  |%1'");

		ТекстСообщения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			ТекстСообщения, СтрСоединить(ПредставлениеСсылок, "-" + Символы.ПС));
	КонецЕсли;

	Если СсылкиНаУдаляемыеОбъекты.Количество() > 0 Тогда
		ВызватьИсключение ТекстСообщения;
	КонецЕсли;
КонецПроцедуры

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

#Область УдалениеПомеченныхОбъектов

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

	ДеревоПомеченныхНаУдаление = ДеревоФормыВУниверсальноеДерево(ДеревоУдаляемых, ЧислоДополнительныхРеквизитов(
		НастройкиДополнительныхРеквизитов));

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

	ПрепятствующиеУдалению = РезультатУдаления.ПрепятствующиеУдалению.ВыгрузитьКолонку("УдаляемыйСсылка");
	ИнформацияОТипах = ИнформацияОТипах(ПрепятствующиеУдалению);

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

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

	Результат.ДеревоПомеченныхНаУдаление.Строки.Сортировать("Представление", Истина);
	Результат.СвязиНеудаленных.Сортировать("УдаляемыйСсылка", Новый СравнениеЗначений);

	Результат.ДеревоПомеченныхНаУдаление = ДеревоПомеченныхНаУдалениеБезУдаленных(
		Результат.ДеревоПомеченныхНаУдаление, РезультатУдаления.Удаленные);

	ИзмененныеСтрокиДерева = Результат.ДеревоПомеченныхНаУдаление.Строки.НайтиСтроки(
		Новый Структура("Модифицированность", Истина));
	Для Каждого ГруппаНеУдаленного Из ИзмененныеСтрокиДерева Цикл
		ГруппаНеУдаленного.Представление = ГруппаНеУдаленного.Представление + " (" + Формат(
			ГруппаНеУдаленного.КоличествоСвязей, "ЧН=0; ЧГ=") + ")";
	КонецЦикла;

	Результат.ДеревоПомеченныхНаУдаление = ДополнитьДеревоДополнительнымиРеквизитами(
		Результат.ДеревоПомеченныхНаУдаление, НастройкиДополнительныхРеквизитов);

	Результат.СвязиНеудаленных = ОбъединениеТаблиц(
		ОшибкиПредыдущегоШага(РезультатПредыдущегоШага), Результат.СвязиНеудаленных);

	Результат.КоличествоНеУдаленных = РезультатУдаления.НеУдаленные.Количество();
	Результат.КоличествоУдаленных = РезультатУдаления.Удаленные.Количество();

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

Функция РезультатШагаДополнительнойОбработки(РезультатПредыдущегоШагаПромежуточный)
	Возврат ?(ТипЗнч(РезультатПредыдущегоШагаПромежуточный) <> Тип("ТаблицаЗначений"),
		РезультатОбработкиУдаляемыхОбъектов(), РезультатПредыдущегоШагаПромежуточный);
КонецФункции

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

// Параметры:
//   РезультатПредыдущегоШага - см. СвязиНеудаленных
//
// Возвращаемое значение:
//   см. СвязиНеудаленных
//
Функция ОшибкиПредыдущегоШага(РезультатПредыдущегоШага)
	Результат = СвязиНеудаленных();

	Для Каждого Элемент Из РезультатПредыдущегоШага Цикл
		Если ПустаяСтрока(Элемент.ТекстОшибки) Тогда
			Продолжить;
		КонецЕсли;

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

		ОшибкаПредыдущегоШага.ЭтоОшибка = Истина;
		ОшибкаПредыдущегоШага.НомерКартинки = 11;
	КонецЦикла;

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

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

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

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

	Возврат Результат;

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

Функция УдаляемыеОбъектыИзРезультатаДополнительнойОбработки(РезультатОбработкиОшибок)

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

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

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

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

Процедура ДобавитьСтрокуДерева(ДеревоНеУдаленные, УдаляемыйСсылка, ИнформацияОТипах, УдалятьТехнологическиеОбъекты)

	ИнформацияОТипе = ИнформацияОТипах[ТипЗнч(УдаляемыйСсылка)]; // см. ИнформацияОТипе
	СтрокиУдаляемых = ДеревоНеУдаленные.Строки.НайтиСтроки(Новый Структура("УдаляемыйСсылка", УдаляемыйСсылка), Истина);
	СтрокаДерева = ?(СтрокиУдаляемых.Количество() = 0, Неопределено, СтрокиУдаляемых[0]);

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

		Если ГруппаНеУдаленного = Неопределено Тогда
			ГруппаНеУдаленного = ДеревоНеУдаленные.Строки.Добавить();
			ГруппаНеУдаленного.НомерКартинки   = -1;
			ГруппаНеУдаленного.УдаляемыйСсылка = ИнформацияОТипе.ПолноеИмя;
			ГруппаНеУдаленного.Представление   = ИнформацияОТипе.ПредставлениеСписка;
		КонецЕсли;

		ГруппаНеУдаленного.КоличествоСвязей = ГруппаНеУдаленного.КоличествоСвязей + 1;
		ГруппаНеУдаленного.Модифицированность = Истина;

		СтрокаДерева = ГруппаНеУдаленного.Строки.Добавить();
		СтрокаДерева.УдаляемыйСсылка = УдаляемыйСсылка;
		СтрокаДерева.Представление   = Строка(УдаляемыйСсылка);
		СтрокаДерева.НомерКартинки = НомерКартинки(
			СтрокаДерева.УдаляемыйСсылка, Истина, ИнформацияОТипе.Вид, "Удален");
	КонецЕсли;

	Если СтрокаДерева <> Неопределено Тогда
		СтрокаДерева.Пометка = Истина;
		СтрокаДерева.КоличествоСвязей = СтрокаДерева.КоличествоСвязей + 1;
	КонецЕсли;

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

Процедура ДобавитьСтрокуСвязейНеудаленных(ТаблицаСвязиНеУдаленных, Причина, ИнформацияОТипах,
	Реквизиты, УдалятьТехнологическиеОбъекты)

	НомерКартинки = 0;
	Вид = "";
	ОбнаруженныйСтатус = "";

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

	СтрокаТаблицы = ТаблицаСвязиНеУдаленных.Добавить();
	СтрокаТаблицы.УдаляемыйСсылка    = Причина.УдаляемыйСсылка;
	СтрокаТаблицы.ЭтоОшибка          = ЗначениеЗаполнено(Причина.ОписаниеОшибки);
	СтрокаТаблицы.ОбнаруженныйСсылка = ?(СтрокаТаблицы.ЭтоОшибка, Причина.ОписаниеОшибки, Причина.МестоИспользования);

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

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

		Вид = ИнформацияОТипе.Вид;
	КонецЕсли;

	НомерКартинки = СтрокаТаблицы.НомерКартинки;
	СтрокаТаблицы.НомерКартинки = ?(НомерКартинки <> 0, НомерКартинки, НомерКартинки(СтрокаТаблицы.ОбнаруженныйСсылка,
		СтрокаТаблицы.СсылочногоТипа, Вид, ОбнаруженныйСтатус));

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

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

	ДопустимыеРежимы = ДопустимыеРежимыУдаления();
	Если ДопустимыеРежимы.Найти(РежимУдаления) = Неопределено Тогда
		ТекстОшибки = НСтр("ru = 'Недопустимое значение параметра %1 в %2. 
						   |Ожидалось: %3; 
						   |передано значение: %4 (тип %5).'");

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

	Результат = Новый Структура("ПрепятствующиеУдалению, Удаленные, НеУдаленные, Успешно");

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

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

	ЗаполнитьЗначенияСвойств(Результат, РезультатУдаления);
	Результат.Успешно = Результат.НеУдаленные.Количество() = 0;

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

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

Процедура СкопироватьСтроки(СтрокиПриемник, СтрокиИсточник)
	Для Каждого СтрокаИсточник Из СтрокиИсточник Цикл
		СтрокаПриемник = СтрокиПриемник.Добавить();
		ЗаполнитьЗначенияСвойств(СтрокаПриемник, СтрокаИсточник);
		СкопироватьСтроки(СтрокаПриемник.Строки, СтрокаИсточник.Строки);
	КонецЦикла;
КонецПроцедуры

Функция ИсключенияПоискаСсылокРазрешающихУдалениеСлужебный() Экспорт
	Исключения = Новый Массив;

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

	Возврат Исключения;
КонецФункции

Функция ЭтоТехническийОбъект(Знач ПолноеИмяОбъекта)

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

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

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

	Возврат ПолноеИмяОбъекта = ВРег("Справочник.ИдентификаторыОбъектовМетаданных") Или ПолноеИмяОбъекта = ВРег(
		"Справочник.ИдентификаторыОбъектовРасширений") Или ПолноеИмяОбъекта = ВРег("Справочник.ВерсииРасширений");

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

// Возвращает номер картинки из коллекции для отображения на форме
// 
// Параметры:
//   СсылкаИлиДанные - ЛюбаяСсылка
//   СсылочногоТипа - Булево
//   Вид - Строка
//   Статус - Строка
//
// Возвращаемое значение:
//   Число - номер картинки
//
Функция НомерКартинки(Знач СсылкаИлиДанные, Знач СсылочногоТипа, Знач Вид, Знач Статус) Экспорт

	Вид = ВРег(Вид);
	Если СсылочногоТипа Тогда
		Если Вид = "СПРАВОЧНИК" Или Вид = "ПЛАНВИДОВХАРАКТЕРИСТИК" Тогда
			НомерКартинки = 3;
		ИначеЕсли Вид = "ДОКУМЕНТ" Тогда
			НомерКартинки = 12;
		ИначеЕсли Вид = "ПЛАНСЧЕТОВ" Тогда
			НомерКартинки = 15;
		ИначеЕсли Вид = "ПЛАНВИДОВРАСЧЕТА" Тогда
			НомерКартинки = 17;
		ИначеЕсли Вид = "БИЗНЕСПРОЦЕСС" Тогда
			НомерКартинки = 19;
		ИначеЕсли Вид = "ЗАДАЧА" Тогда
			НомерКартинки = 21;
		ИначеЕсли Вид = "ПЛАНОБМЕНА" Тогда
			НомерКартинки = 23;
		Иначе
			НомерКартинки = -2;
		КонецЕсли;
		Если Статус = "Удален" Тогда
			НомерКартинки = НомерКартинки + 1;
		ИначеЕсли Статус = "Проведен" Тогда
			НомерКартинки = НомерКартинки + 2;
		КонецЕсли;
	Иначе
		Если Вид = "КОНСТАНТА" Тогда
			НомерКартинки = 25;
		ИначеЕсли Вид = "РЕГИСТРСВЕДЕНИЙ" Тогда
			НомерКартинки = 26;
		ИначеЕсли Вид = "РЕГИСТРНАКОПЛЕНИЯ" Тогда
			НомерКартинки = 28;
		ИначеЕсли Вид = "РЕГИСТРБУХГАЛТЕРИИ" Тогда
			НомерКартинки = 34;
		ИначеЕсли Вид = "РЕГИСТРРАСЧЕТА" Тогда
			НомерКартинки = 38;
		ИначеЕсли СсылкаИлиДанные = Неопределено Тогда
			НомерКартинки = 11;
		Иначе
			НомерКартинки = 8;
		КонецЕсли;
	КонецЕсли;

	Возврат НомерКартинки;
КонецФункции

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

	Если МонопольныйРежим() <> ЗначениеМонопольногоРежима Тогда
		УстановитьМонопольныйРежим(ЗначениеМонопольногоРежима);
	КонецЕсли;
КонецПроцедуры

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

	ПериодОбновленияЗначения = 60000;

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

	Возврат ДанныеКонстанты.Значение;
КонецФункции

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

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

	Ошибки = Новый ТаблицаЗначений;
	Ошибки.Колонки.Добавить("УдаляемыйСсылка");
	Ошибки.Колонки.Добавить("ОбнаруженныйСсылка");
	Ошибки.Колонки.Добавить("ТекстОшибки", ТипСтрока);
	Ошибки.Колонки.Добавить("ТребуетсяУдаление", Новый ОписаниеТипов("Булево"));
	Возврат Ошибки
КонецФункции

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

	Для Индекс = 1 По ЧислоДополнительныхРеквизитов Цикл
		Результат.Колонки.Добавить("Реквизит" + Индекс);
	КонецЦикла;

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

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

	Таблица.Колонки.Добавить("НомерКартинки", Новый ОписаниеТипов("Число"));
	Таблица.Колонки.Добавить("СсылочногоТипа", Новый ОписаниеТипов("Булево"));
	Таблица.Колонки.Добавить("ЭтоОшибка", Новый ОписаниеТипов("Булево"));
	Таблица.Колонки.Добавить("ЭтоКонстанта", Новый ОписаниеТипов("Булево"));

	Таблица.Индексы.Добавить("УдаляемыйСсылка");

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

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

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

	Таблица.Индексы.Добавить("УдаляемыйСсылка");

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

Функция Значение(Источник, ИмяСвойства, ЗначениеПоУмолчанию = Неопределено)

	Буфер = Новый Структура(ИмяСвойства, ЗначениеПоУмолчанию);
	ЗаполнитьЗначенияСвойств(Буфер, Источник);
	Возврат Буфер[ИмяСвойства];

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

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

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