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

// Параметры формы:
//     НаборСсылок - Массив из ЛюбаяСсылка - элементы для замены на другой элемент ссылочного типа.
//

#Область ОписаниеПеременных

&НаКлиенте
Перем ОтчетыДляОтправки; // Массив из ОтчетОбОшибке 

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

#Область ОбработчикиСобытийФормы

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

&НаКлиенте
Процедура ПриОткрытии(Отказ)
	ПриАктивацииШагаМастера();
	ОтчетыДляОтправки = Новый Соответствие;
КонецПроцедуры

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

&НаКлиенте
Процедура ПриЗакрытии(ЗавершениеРаботы)

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

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

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

#Область ОбработчикиСобытийЭлементовШапкиФормы

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

&НаКлиенте
Процедура СсылкаПодробнееНажатие(Элемент)
	СтандартныеПодсистемыКлиент.ПоказатьПодробнуюИнформацию(Неопределено, Элемент.Подсказка);
КонецПроцедуры

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

#Область ОбработчикиСобытийЭлементовТаблицыФормыМестаИспользования

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

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

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

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

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

#Область ОбработчикиСобытийЭлементовТаблицыФормыНеудачныеЗамены

&НаКлиенте
Процедура НеудачныеЗаменыПриАктивизацииСтроки(Элемент)

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

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

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

&НаКлиенте
Процедура ДекорацияОтправитьОтчетОбОшибкеНажатие(Элемент)

	ТекущиеДанные = Элементы.НеудачныеЗамены.ТекущиеДанные;
	Если ТекущиеДанные = Неопределено Тогда
		Возврат;
	КонецЕсли;

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

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

#Область ОбработчикиКомандФормы

&НаКлиенте
Процедура ОбработчикКнопкиМастера(Команда)
	
	Если Команда.Имя = НастройкиМастера.КнопкаДалее Тогда
		ШагМастераДалее();
	ИначеЕсли Команда.Имя = НастройкиМастера.КнопкаНазад Тогда
		ШагМастераНазад();
	ИначеЕсли Команда.Имя = НастройкиМастера.КнопкаОтмена Тогда
		ШагМастераОтмена();
	КонецЕсли;
	
КонецПроцедуры

&НаКлиенте
Процедура ОткрытьЭлементМестаИспользования(Команда)
	ТекущиеДанные = Элементы.МестаИспользования.ТекущиеДанные;
	Если ТекущиеДанные <> Неопределено Тогда
		ПоказатьЗначение(, ТекущиеДанные.Ссылка);
	КонецЕсли;
КонецПроцедуры

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

&НаКлиенте
Процедура ВсеМестаИспользования(Команда)
	
	Если МестаИспользования.Количество() > 0 Тогда 
		ПоискИУдалениеДублейКлиент.ПоказатьМестаИспользования(МестаИспользования);
	КонецЕсли;
	
КонецПроцедуры

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

&НаКлиенте
Процедура ОткрытьЭлементНеудачнойЗамены(Команда)
	ТекущиеДанные = Элементы.НеудачныеЗамены.ТекущиеДанные;
	Если ТекущиеДанные <> Неопределено Тогда
		ПоказатьЗначение(, ТекущиеДанные.Ссылка);
	КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура РазвернутьВсеНеудачныеЗамены(Команда)
	ДеревоФормы = Элементы.НеудачныеЗамены;
	Для Каждого Элемент Из НеудачныеЗамены.ПолучитьЭлементы() Цикл
		ДеревоФормы.Развернуть(Элемент.ПолучитьИдентификатор(), Истина);
	КонецЦикла;
КонецПроцедуры

&НаКлиенте
Процедура СвернутьВсеНеудачныеЗамены(Команда)
	ДеревоФормы = Элементы.НеудачныеЗамены;
	Для Каждого Элемент Из НеудачныеЗамены.ПолучитьЭлементы() Цикл
		ДеревоФормы.Свернуть(Элемент.ПолучитьИдентификатор());
	КонецЦикла;
КонецПроцедуры

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

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

#Область ПереключениеСтраницМастера

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

// Добавляет шаг мастера. Переходы между страницами будут происходить согласно порядку добавления.
//
// Параметры:
//   Страница - ГруппаФормы - страница, содержащая элементы шага.
//
// Возвращаемое значение:
//   Структура - описание настроек страницы, где:
//       * ИмяСтраницы - Строка - имя страницы.
//       * КнопкаДалее - Структура - описание кнопки "Далее", где:
//           ** Заголовок - Строка - заголовок кнопки. По умолчанию - "Далее >".
//           ** Подсказка - Строка - подсказка для кнопки. По умолчанию соответствует заголовку кнопки.
//           ** Видимость - Булево - когда Истина то кнопка видна. По умолчанию - Истина.
//           ** Доступность - Булево - когда Истина то кнопку можно нажимать. По умолчанию - Истина.
//           ** КнопкаПоУмолчанию - Булево - когда Истина то кнопка будет основной кнопкой формы. По умолчанию - Истина.
//       * КнопкаНазад - Структура - описание кнопки "Назад", где:
//           ** Заголовок - Строка - заголовок кнопки. По умолчанию - "< Назад".
//           ** Подсказка - Строка - подсказка для кнопки. По умолчанию соответствует заголовку кнопки.
//           ** Видимость - Булево - когда Истина то кнопка видна. По умолчанию - Истина.
//           ** Доступность - Булево - когда Истина то кнопку можно нажимать. По умолчанию - Истина.
//           ** КнопкаПоУмолчанию - Булево - когда Истина то кнопка будет основной кнопкой формы. По умолчанию - Ложь.
//       * КнопкаОтмена - Структура - описание кнопки "Отмена", где:
//           ** Заголовок - Строка - заголовок кнопки. По умолчанию: "Отмена".
//           ** Подсказка - Строка - подсказка для кнопки. По умолчанию соответствует заголовку кнопки.
//           ** Видимость - Булево - когда Истина то кнопка видна. По умолчанию: Истина.
//           ** Доступность - Булево - когда Истина то кнопку можно нажимать. По умолчанию - Истина.
//           ** КнопкаПоУмолчанию - Булево - когда Истина то кнопка будет основной кнопкой формы. По умолчанию - Ложь.
//
&НаСервере
Функция ДобавитьШагМастера(Знач Страница)
	ОписаниеШага = Новый Структура;
	ОписаниеШага.Вставить("Индекс", 0);
	ОписаниеШага.Вставить("ИмяСтраницы", "");
	ОписаниеШага.Вставить("КнопкаНазад", КнопкаМастера());
	ОписаниеШага.Вставить("КнопкаДалее", КнопкаМастера());
	ОписаниеШага.Вставить("КнопкаОтмена", КнопкаМастера());
	ОписаниеШага.ИмяСтраницы = Страница.Имя;
	
	ОписаниеШага.КнопкаНазад.Заголовок = НСтр("ru = '< Назад'");
	
	ОписаниеШага.КнопкаДалее.КнопкаПоУмолчанию = Истина;
	ОписаниеШага.КнопкаДалее.Заголовок = НСтр("ru = 'Далее >'");
	
	ОписаниеШага.КнопкаОтмена.Заголовок = НСтр("ru = 'Отмена'");
	
	НастройкиМастера.Шаги.Добавить(ОписаниеШага);
	
	ОписаниеШага.Индекс = НастройкиМастера.Шаги.ВГраница();
	Возврат ОписаниеШага;
КонецФункции

&НаКлиентеНаСервереБезКонтекста
Процедура УстановитьВидимостьДоступность(Форма)
	
	Элементы = Форма.Элементы;
	НастройкиМастера = Форма.НастройкиМастера;
	ТекущийШаг = НастройкиМастера.ТекущийШаг;
	
	// Переключение страницы.
	Элементы[НастройкиМастера.ГруппаСтраниц].ТекущаяСтраница = Элементы[ТекущийШаг.ИмяСтраницы];
	
	// Обновление кнопок.
	ОбновитьСвойстваКнопкиМастера(Элементы[НастройкиМастера.КнопкаДалее],  ТекущийШаг.КнопкаДалее);
	ОбновитьСвойстваКнопкиМастера(Элементы[НастройкиМастера.КнопкаНазад],  ТекущийШаг.КнопкаНазад);
	ОбновитьСвойстваКнопкиМастера(Элементы[НастройкиМастера.КнопкаОтмена], ТекущийШаг.КнопкаОтмена);
	
КонецПроцедуры

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

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

#Область СобытияМастера

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

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

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

&НаКлиенте
Процедура ШагМастераОтмена()
	
	ТекущаяСтраница = Элементы.ШагиМастера.ТекущаяСтраница;
	Если ТекущаяСтраница = Элементы.ШагОбъединение Тогда
		НастройкиМастера.ПоказатьДиалогПередЗакрытием = Ложь;
	КонецЕсли;
	
	Если Открыта() Тогда
		Закрыть();
	КонецЕсли;
	
КонецПроцедуры

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

#Область СлужебныеПроцедурыОбъединенияЭлементов

&НаСервере
Процедура УстановитьУсловноеОформление()

	УсловноеОформление.Элементы.Очистить();

	//

	Элемент = УсловноеОформление.Элементы.Добавить();

	ПолеЭлемента = Элемент.Поля.Элементы.Добавить();
	ПолеЭлемента.Поле = Новый ПолеКомпоновкиДанных(Элементы.МестаИспользованияОсновной.Имя);

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

	Элемент.Оформление.УстановитьЗначениеПараметра("Отображать", Ложь);

	//

	Элемент = УсловноеОформление.Элементы.Добавить();

	ПолеЭлемента = Элемент.Поля.Элементы.Добавить();
	ПолеЭлемента.Поле = Новый ПолеКомпоновкиДанных(Элементы.МестаИспользованияСсылка.Имя);

	ПолеЭлемента = Элемент.Поля.Элементы.Добавить();
	ПолеЭлемента.Поле = Новый ПолеКомпоновкиДанных(Элементы.МестаИспользованияКод.Имя);

	ПолеЭлемента = Элемент.Поля.Элементы.Добавить();
	ПолеЭлемента.Поле = Новый ПолеКомпоновкиДанных(Элементы.МестаИспользованияМестИспользования.Имя);

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

	Элемент.Оформление.УстановитьЗначениеПараметра("Шрифт", ШрифтыСтиля.ОсновнойЭлементСписка);

	//

	Элемент = УсловноеОформление.Элементы.Добавить();

	ПолеЭлемента = Элемент.Поля.Элементы.Добавить();
	ПолеЭлемента.Поле = Новый ПолеКомпоновкиДанных(Элементы.МестаИспользованияНеИспользуется.Имя);

	ОтборЭлемента = Элемент.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
	ОтборЭлемента.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("МестаИспользования.МестИспользования");
	ОтборЭлемента.ВидСравнения = ВидСравненияКомпоновкиДанных.МеньшеИлиРавно;
	ОтборЭлемента.ПравоеЗначение = 0;

	Элемент.Оформление.УстановитьЗначениеПараметра("Видимость", Истина);
	Элемент.Оформление.УстановитьЗначениеПараметра("Отображать", Истина);

	//

	Элемент = УсловноеОформление.Элементы.Добавить();

	ПолеЭлемента = Элемент.Поля.Элементы.Добавить();
	ПолеЭлемента.Поле = Новый ПолеКомпоновкиДанных(Элементы.МестаИспользованияНеИспользуется.Имя);

	ОтборЭлемента = Элемент.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
	ОтборЭлемента.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("МестаИспользования.МестИспользования");
	ОтборЭлемента.ВидСравнения = ВидСравненияКомпоновкиДанных.Больше;
	ОтборЭлемента.ПравоеЗначение = 0;

	Элемент.Оформление.УстановитьЗначениеПараметра("Видимость", Ложь);
	Элемент.Оформление.УстановитьЗначениеПараметра("Отображать", Ложь);

	//

	Элемент = УсловноеОформление.Элементы.Добавить();

	ПолеЭлемента = Элемент.Поля.Элементы.Добавить();
	ПолеЭлемента.Поле = Новый ПолеКомпоновкиДанных(Элементы.МестаИспользованияМестИспользования.Имя);

	ОтборЭлемента = Элемент.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
	ОтборЭлемента.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("МестаИспользования.МестИспользования");
	ОтборЭлемента.ВидСравнения = ВидСравненияКомпоновкиДанных.МеньшеИлиРавно;
	ОтборЭлемента.ПравоеЗначение = 0;

	Элемент.Оформление.УстановитьЗначениеПараметра("Видимость", Ложь);
	Элемент.Оформление.УстановитьЗначениеПараметра("Отображать", Ложь);

	//

	Элемент = УсловноеОформление.Элементы.Добавить();

	ПолеЭлемента = Элемент.Поля.Элементы.Добавить();
	ПолеЭлемента.Поле = Новый ПолеКомпоновкиДанных(Элементы.НеудачныеЗаменыКод.Имя);

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

	//

	Элемент = УсловноеОформление.Элементы.Добавить();

	ПолеЭлемента = Элемент.Поля.Элементы.Добавить();
	ПолеЭлемента.Поле = Новый ПолеКомпоновкиДанных(Элементы.МестаИспользованияМестИспользования.Имя);

	ОтборЭлемента = Элемент.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
	ОтборЭлемента.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("МестаИспользования.МестИспользования");
	ОтборЭлемента.ВидСравнения = ВидСравненияКомпоновкиДанных.Больше;
	ОтборЭлемента.ПравоеЗначение = 0;

	Элемент.Оформление.УстановитьЗначениеПараметра("Видимость", Истина);
	Элемент.Оформление.УстановитьЗначениеПараметра("Отображать", Истина);

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

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

&НаСервереБезКонтекста
Функция ПроверитьОбъединяемыеСсылки(Знач НаборСсылок)
	
	КоличествоСсылок = НаборСсылок.Количество();
	Если КоличествоСсылок < 2 Тогда
		ВызватьИсключение НСтр("ru = 'Для объединения укажите несколько элементов.'");
	КонецЕсли;
	
	ПервыйЭлемент = НаборСсылок[0];	
	ОсновныеМетаданные = ПервыйЭлемент.Метаданные();
	ВыполнитьПроверкуПравДоступа("Изменение", ОсновныеМетаданные);
	
	Характеристики = Новый Структура("Владельцы, Иерархический, ВидИерархии", Новый Массив, Ложь);
	ЗаполнитьЗначенияСвойств(Характеристики, ОсновныеМетаданные);
	
	ЕстьВладельцы = Характеристики.Владельцы.Количество() > 0;
	ЕстьГруппы    = Характеристики.Иерархический И Характеристики.ВидИерархии = Метаданные.СвойстваОбъектов.ВидИерархии.ИерархияГруппИЭлементов;
	
	ТекстЗапроса = 
		"ВЫБРАТЬ Ссылка КАК Ссылка,
		|&Владелец КАК Владелец,
		|&ЭтоГруппа КАК ЭтоГруппа
		|ПОМЕСТИТЬ ЗаменяемыеСсылки
		|ИЗ #ИмяТаблицы ГДЕ Ссылка В (&НаборСсылок)
		|ИНДЕКСИРОВАТЬ ПО Владелец, ЭтоГруппа
		|;
		|ВЫБРАТЬ 
		|	КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Владелец) КАК КоличествоВладельцев,
		|	МИНИМУМ(Владелец)              КАК ОбщийВладелец,
		|	МАКСИМУМ(ЭтоГруппа)            КАК ЕстьГруппы,
		|	КОЛИЧЕСТВО(Ссылка)             КАК КоличествоСсылок
		|ИЗ
		|	ЗаменяемыеСсылки";
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ИмяТаблицы", ОсновныеМетаданные.ПолноеИмя());
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&Владелец", ?(ЕстьВладельцы, "Владелец", "НЕОПРЕДЕЛЕНО"));
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ЭтоГруппа", ?(ЕстьГруппы, "ЭтоГруппа", "ЛОЖЬ"));
	
	Запрос = Новый Запрос(ТекстЗапроса);
	Запрос.УстановитьПараметр("НаборСсылок", НаборСсылок);
	
	Контроль = Запрос.Выполнить().Выгрузить()[0];
	Если Контроль.ЕстьГруппы Тогда
		ВызватьИсключение НСтр("ru = 'Один из объединяемых элементов является группой.
			|Группы не могут быть объединены.'");
	ИначеЕсли Контроль.КоличествоВладельцев > 1 Тогда 
		ВызватьИсключение НСтр("ru = 'У объединяемых элементов различные владельцы.
			|Такие элементы не могут быть объединены.'");
	ИначеЕсли Контроль.КоличествоСсылок <> КоличествоСсылок Тогда
		ВызватьИсключение НСтр("ru = 'Все объединяемые элементы должны быть одного типа.'");
	КонецЕсли;

	Возврат ?(ЕстьВладельцы, Контроль.ОбщийВладелец, Неопределено);
	
КонецФункции

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

&НаСервере
Процедура СформироватьПодсказкуОбъединения()

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

&НаКлиенте
Функция СообщениеОЗавершении()
	Возврат СтроковыеФункцииКлиентСервер.СтрокаСЧисломДляЛюбогоЯзыка(
		НСтр("ru = ';%1 элемент объединен в:;;%1 элемента объединено в:;%1 элементов объединено в:;%1 элемента объединено в:'"),
		МестаИспользования.Количество());
КонецФункции

&НаКлиенте
Процедура СформироватьНадписьНеудачныеЗамены()
	
	Элементы.РезультатНеудачныеЗамены.Заголовок = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Объединение элементов не выполнено. В некоторых местах использования не может быть произведена
			|автоматическая замена на ""%1"".'"),
		ОсновнойЭлемент);
	
КонецПроцедуры

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

&НаСервереБезКонтекста
Функция ИмяФормыВыбораПоСсылке(Знач Ссылка)
	Мета = Метаданные.НайтиПоТипу(ТипЗнч(Ссылка));
	Возврат ?(Мета = Неопределено, Неопределено, Мета.ПолноеИмя() + ".ФормаВыбора");
КонецФункции

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

// Возвращаемое значение:
//   Строка - код справочника, если он существует. 
//   Неопределено - если кода нет.
//
&НаСервереБезКонтекста
Функция КодОбъекта(Знач Ссылка, КэшМетаданных)
	Данные = ОписаниеМетаданных(Ссылка, КэшМетаданных);
	Возврат ?(Данные.ЕстьКод, Ссылка.Код, Неопределено);
КонецФункции

// Возвращаемое значение:
//   СправочникСсылка - владелец справочника, если он существует. 
//   Неопределено - если владельца нет.
//
&НаСервереБезКонтекста
Функция ВладелецОбъекта(Знач Ссылка, КэшМетаданных)
	Данные = ОписаниеМетаданных(Ссылка, КэшМетаданных);
	Возврат ?(Данные.ЕстьВладелец, Ссылка.Владелец, Неопределено);
КонецФункции

&НаСервереБезКонтекста
Функция ОписаниеМетаданных(Знач Ссылка, КэшМетаданных)
	
	МетаданныеОбъекта = Ссылка.Метаданные();
	Данные = КэшМетаданных[МетаданныеОбъекта];
	
	Если Данные = Неопределено Тогда
		Тест = Новый Структура("ДлинаКода, Владельцы", 0, Новый Массив);
		ЗаполнитьЗначенияСвойств(Тест, МетаданныеОбъекта);
		
		Данные = Новый Структура;
		Данные.Вставить("ЕстьКод", Тест.ДлинаКода > 0);
		Данные.Вставить("ЕстьВладелец", Тест.Владельцы.Количество() > 0);
		
		КэшМетаданных[МетаданныеОбъекта] = Данные;
	КонецЕсли;
	
	Возврат Данные;
КонецФункции

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

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

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

#Область РаботаСДлительнымиОперациями

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

&НаСервере
Функция ОпределитьМестаИспользования()
	
	НастройкиЗапуска = ДлительныеОперации.ПараметрыВыполненияВФоне(УникальныйИдентификатор);
	НастройкиЗапуска.НаименованиеФоновогоЗадания = НСтр("ru = 'Поиск и удаление дублей: Определение мест использования'");
	Возврат ДлительныеОперации.ВыполнитьВФоне("ПоискИУдалениеДублей.ОпределитьМестаИспользования", 
		МестаИспользования.Выгрузить(, "Ссылка").ВыгрузитьКолонку(0), НастройкиЗапуска);
		
КонецФункции

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

&НаКлиенте
Процедура НачатьЗаменуСсылок()
	
	ОтчетыДляОтправки = Новый Соответствие;

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

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

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

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

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

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

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

&НаКлиенте
Процедура ПослеПодтвержденияОтменыЗадания(Ответ, ПараметрыВыполнения) Экспорт
	Если Ответ = КодВозвратаДиалога.Прервать Тогда
		НастройкиМастера.ПоказатьДиалогПередЗакрытием = Ложь;
		Закрыть();
	КонецЕсли;
КонецПроцедуры

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

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

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

// Параметры:
//  КнопкаМастера - см. КнопкаМастера
//  Описание - Строка
//
&НаКлиентеНаСервереБезКонтекста
Процедура ОбновитьСвойстваКнопкиМастера(КнопкаМастера, Описание)
	
	ЗаполнитьЗначенияСвойств(КнопкаМастера, Описание);
	КнопкаМастера.РасширеннаяПодсказка.Заголовок = Описание.Подсказка;
	
КонецПроцедуры

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

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