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

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

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

// Значения объекта до записи для использования в обработчике события ПриЗаписи.
Перем ЭтоНовый, СтарыйРодитель, СтарыеВсеОбъектыАвторизации, СтарыйСоставРолей;

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

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

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

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

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

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

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

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

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

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

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

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

#Иначе
ВызватьИсключение НСтр("ru = 'Недопустимый вызов объекта на клиенте.'");
#КонецЕсли