Операции закрытия месяца / Модуль менеджера

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

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

#Область СостояниеЭтапов

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

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

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

#Область ОписаниеЭтапов

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

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

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

#Область РасчетЭтапов

// Регистрирует ошибки, обнаруженные в ходе расчета.
//
// Параметры:
//	ПараметрыОбработчика - см. ИнициализироватьПараметрыОбработчикаЭтапа
//
Процедура ЗарегистрироватьПроблемыРасчетаЭтапа(ПараметрыОбработчика) Экспорт

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

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

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

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

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

		Если Не ФормаИзНастроек = Неопределено Тогда
			ВыбраннаяФорма = ФормаИзНастроек;
			СтандартнаяОбработка = Ложь;
		КонецЕсли;
		
	КонецЕсли;

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

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

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

#Область ОписаниеДереваЭтапов

#Область ОписаниеГруппЭтапов

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

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

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

#Область ОписаниеЭтапов

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

		Для Каждого ПредшествующийЭтап Из ТекущийЭтап.ЗависитОтЭтапов Цикл

			СтрокаПредшествующегоЭтапа = ТаблицаЭтапов.Найти(ПредшествующийЭтап, "Код");
			
			Если НЕ ЗначениеЗаполнено(СтрокаПредшествующегоЭтапа) Тогда
				Продолжить;
			КонецЕсли;
			
			СтрокаПредшествующегоЭтапа.ВлияетНаЭтапы.Добавить(ТекущийЭтап.Код);
			
		КонецЦикла;
		
		НомерЭтапа = НомерЭтапа + 1;
		
	КонецЦикла;
	
КонецПроцедуры

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

#Область ОписаниеПоясненийКЭтапам

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

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

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

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

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

#Область СостояниеЭтапов

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

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

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

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

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

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

			ЕстьЗаблокированные = Ложь;
			
			Для Каждого ТекущаяОрганизация Из ТекущаяГруппа Цикл
				Если НастройкиБлокировок.ЗаблокированныеОрганизации.Найти(ТекущаяОрганизация) <> Неопределено Тогда
					ЕстьЗаблокированные = Истина;
				КонецЕсли;
			КонецЦикла;
			
			Если ЕстьЗаблокированные Тогда
				
				ОбщегоНазначенияКлиентСервер.ДополнитьМассив(СтруктураДанных.ЗаблокированныеОрганизации, ТекущаяГруппа, Истина);
				ОбщегоНазначенияКлиентСервер.ДополнитьМассив(СтруктураДанных.ПараметрыРасчета.ЗаблокированныеОрганизации, ТекущаяГруппа, Истина);
				
				Для Каждого ТекущаяОрганизация Из ТекущаяГруппа Цикл
					ОбщегоНазначенияКлиентСервер.УдалитьЗначениеИзМассива(СтруктураДанных.ПараметрыРасчета.МассивОрганизаций, ТекущаяОрганизация);
				КонецЦикла;
				
			КонецЕсли;
			
		КонецЦикла;
		
		Если НЕ ЗначениеЗаполнено(СтруктураДанных.ПараметрыРасчета.МассивОрганизаций) Тогда
			
			СтруктураДанных.ПериодЗаблокированЧастично = Истина;
			
			СтруктураДанных.ПараметрыРасчета.МассивОрганизаций.Очистить();
			ОбщегоНазначенияКлиентСервер.ДополнитьМассив(
				СтруктураДанных.ПараметрыРасчета.МассивОрганизаций,
				СтруктураДанных.ПараметрыРасчета.МассивОрганизацийИсходный,
				Истина); // в логике механизма МассивОрганизаций не может быть пустым - вернем его исходное заполнение, расчет все равно будет невозможен
			
		КонецЕсли;
		
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Истина);
	
	Если ПараметрыЗапуска.ОбновлятьКэшиРегламентныхЗаданий Тогда
		
		КоличествоДанных = РегистрыСведений.РегламентныеЗаданияЗакрытияМесяца.ОбновитьКэшСвойстваИспользованиеРегламентногоЗадания();
		
		// Зафиксируем общий замер
		ОценкаПроизводительности.ЗафиксироватьЗамерДлительнойОперации(
			ОписаниеОбщегоЗамераПроизводительности,
			КоличествоДанныхДляЗамера(КоличествоДанных),
			"ОбновлятьКэшиРегламентныхЗаданий");
		
	КонецЕсли;
	
	Если ПараметрыЗапуска.ПроверятьЗавершениеРасчетов Тогда
		
		КоличествоДанных = РегистрыСведений.ВыполнениеОперацийЗакрытияМесяца.ОбновитьСостояниеАктивныхРасчетов();
		
		// Зафиксируем общий замер
		ОценкаПроизводительности.ЗафиксироватьЗамерДлительнойОперации(
			ОписаниеОбщегоЗамераПроизводительности,
			КоличествоДанныхДляЗамера(КоличествоДанных),
			"ПроверятьЗавершениеРасчетов");
		
	КонецЕсли;
	
	Если ПараметрыЗапуска.ОбновлятьСписокПроверок Тогда
		ЗаполнитьСправочникПроверокСостоянияСистемы(ПараметрыЗапуска, ОписаниеОбщегоЗамераПроизводительности);
	КонецЕсли;
	
	ЗакрытиеМесяцаСервер.ПроверитьНомерЗадания();
	
	Если СтруктураДанных.ПериодЗаблокирован И НЕ ПараметрыЗапуска.ФормироватьОперацииЗакрытогоПериода Тогда
		СтрокиЭтапов = Новый Массив; // определение состояния этапов не требуется
	ИначеЕсли ЗначениеЗаполнено(ПараметрыЗапуска.ОбновитьТолькоОдинЭтап) Тогда
		СтрокиЭтапов = СтруктураДанных.ТаблицаЭтапы.НайтиСтроки(Новый Структура("Код", ПараметрыЗапуска.ОбновитьТолькоОдинЭтап));
	ИначеЕсли ЗначениеЗаполнено(ПараметрыЗапуска.ОбновляемыеЭтапы) Тогда
		
		СтрокиЭтапов = Новый Массив;
		ЭтапыДляОтбора = Новый Соответствие;
		
		Для Каждого ОбновляемыйЭтап Из ПараметрыЗапуска.ОбновляемыеЭтапы Цикл
			
			ЭтапыДляОтбора.Вставить(ОбновляемыйЭтап, Истина);
			
			Если ПараметрыЗапуска.УчетСостоянияПредшествующихЭтапов Тогда
				
				СтрокаЭтапа = СтруктураДанных.ТаблицаЭтапы.Найти(ОбновляемыйЭтап, "Код");
				Если СтрокаЭтапа = Неопределено Тогда
					Продолжить;
				КонецЕсли;
				
				Для Каждого ПредшествующийЭтап Из СтрокаЭтапа.ЗависитОтЭтапов Цикл
					ЭтапыДляОтбора.Вставить(ПредшествующийЭтап, Истина);
				КонецЦикла;
				
			КонецЕсли;
			
		КонецЦикла;
		
		Для Каждого ОбновляемыйЭтап Из СтруктураДанных.ТаблицаЭтапы Цикл
			
			Если ЭтапыДляОтбора.Получить(ОбновляемыйЭтап.Код) <> Неопределено Тогда
				СтрокиЭтапов.Добавить(ОбновляемыйЭтап);
			КонецЕсли;
			
		КонецЦикла;
		
	Иначе
		СтрокиЭтапов = СтруктураДанных.ТаблицаЭтапы;
	КонецЕсли;
	
	ДатаНачалаРасчета = СтруктураДанных.ПараметрыРасчета.ПериодРегистрации;
	
	// Последовательный запуск проверок этапов и проверок учета в системе
	
	ВсегоЭтапов = СтрокиЭтапов.Количество();
	Счетчик = 1;
	
	// Получение данных предыдущего анализа состояния закрытия месяца
	
	СостоянияЭтаповИзКеша = Новый Структура;
	СостоянияЭтаповИзКеша.Вставить("СостоянияЭтапов", Новый Соответствие());
	СостоянияЭтаповИзКеша.Вставить("ДатаАктуальностиДанных", ПараметрыЗапуска.СтруктураДанных.ДатаНачалаРасчета);
		
	ПрочитатьРезультатАнализаЭтапов(ПараметрыЗапуска, СостоянияЭтаповИзКеша);
	
	Если ПараметрыЗапуска.ОбновитьКэшСтатусовОпераций Тогда
		СостоянияЭтаповИзКеша.Вставить("СостоянияЭтапов", Новый Соответствие());
	КонецЕсли;
		
	ТребуетсяОбновлениеКеша = Ложь;
	
	Для Каждого ТекущаяСтрока Из СтрокиЭтапов Цикл
		
		ДанныеКеша = СостоянияЭтаповИзКеша.СостоянияЭтапов.Получить(ТекущаяСтрока.Код);
		
		Если ДанныеКеша <> Неопределено Тогда
			ТекущаяСтрока.СостояниеИзКеша = ДанныеКеша.Состояние;
		Иначе
			ТребуетсяОбновлениеКеша = Истина;
		КонецЕсли;
		
		ОбработатьЭтап(
			ТекущаяСтрока,
			ПараметрыЗапуска,
			СтруктураДанных,
			ОписаниеОбщегоЗамераПроизводительности,
			ТекстДлительностьЭтапов);
			
		ВозможнаГиперссылкаДляСостояния = 
			ТекущаяСтрока.Состояние = Перечисления.СостоянияОперацийЗакрытияМесяца.НеВыполнено
			ИЛИ ТекущаяСтрока.Состояние = Перечисления.СостоянияОперацийЗакрытияМесяца.ВыполненоСОшибками
			ИЛИ (ТекущаяСтрока.Состояние = Перечисления.СостоянияОперацийЗакрытияМесяца.ВыполненоУспешно
				 И НЕ ТекущаяСтрока.ВыполняетсяВручную);
		
		ТекущаяСтрока.ГиперссылкаВыполнить = ВозможнаГиперссылкаДляСостояния
			И ЗначениеЗаполнено(ТекущаяСтрока.ДействиеВыполнить) И НЕ ПустаяСтрока(ТекущаяСтрока.ТекстВыполнить);
		
		ТекущаяСтрока.ГиперссылкаПодробнее = ВозможнаГиперссылкаДляСостояния
			И (НЕ ТекущаяСтрока.ВыполняетсяВручную ИЛИ ЗначениеЗаполнено(ТекущаяСтрока.ДействиеПодробнее))
			И НЕ ПустаяСтрока(ТекущаяСтрока.ТекстПодробнее);
			
		ДлительныеОперации.СообщитьПрогресс(Цел((Счетчик/ВсегоЭтапов)*100),,
			Новый Структура("ОбновитьКэшСтатусовОпераций, ДатаАктуальностиДанных",
				ПараметрыЗапуска.ОбновитьКэшСтатусовОпераций,
				СостоянияЭтаповИзКеша.ДатаАктуальностиДанных));
		Счетчик = Счетчик + 1;
		
	КонецЦикла;
	
	Если ТребуетсяОбновлениеКеша Тогда
		СохранитьРезультатАнализаЭтапов(ПараметрыЗапуска);
		СтруктураДанных.Удалить("СостоянияЭтаповИзКэша");
	ИначеЕсли СтрокиЭтапов.Количество() Тогда
		СтруктураДанных.Вставить("СостоянияЭтаповИзКэша");
	КонецЕсли;
		
	СтруктураДанных.Вставить("ДатаАктуальностиДанных", СостоянияЭтаповИзКеша.ДатаАктуальностиДанных);
	
	// Последовательный запуск проверок этапов и проверок учета в системе
	
	СтруктураДанных.ДатаНачалаРасчета = ДатаНачалаРасчета;
	
	// Заполним свойства групп этапов.
	ЗаполнитьСвойстваГруппЭтапов(СтруктураДанных);
	
	// Проверим даты запрета изменения.
	Если ПараметрыЗапуска.ПроверятьДатуЗапрета Тогда
		
		КоличествоДанных = ПроверитьДатыЗапретаИзменения(СтруктураДанных);
		
		// Зафиксируем общий замер
		ОценкаПроизводительности.ЗафиксироватьЗамерДлительнойОперации(
			ОписаниеОбщегоЗамераПроизводительности,
			КоличествоДанныхДляЗамера(КоличествоДанных),
			"ПроверитьДатыЗапретаИзменения");
			
	КонецЕсли;
	
	// Проверим блокировку периода
	Если НЕ СтруктураДанных.ИзмененияЗапрещены И (СтруктураДанных.ПериодЗаблокирован ИЛИ СтруктураДанных.ПериодЗаблокированЧастично) Тогда
		
		СтруктураДанных.ИзмененияЗапрещены = Истина;
		СтруктураДанных.ПоясненияКЗапретуИзменений = НСтр("ru='Установлена блокировка от изменений'");
		
	КонецЕсли;
	
	СтруктураДанных.Вставить("РасшифровкаБлокировки", Новый ХранилищеЗначения(НастройкиБлокировок.ТаблицаБлокировок, Новый СжатиеДанных(9)));
	СтруктураДанных.Вставить("РасшифровкаЗаданий",    Новый ХранилищеЗначения(НастройкиБлокировок.ТаблицаЗаданийКЗакрытию, Новый СжатиеДанных(9)));
		
	// Очистим неиспользуемые записи регистра сведений ЗаданияКЗакрытиюМесяца
	Если ПараметрыЗапуска.ОчищатьНеиспользуемыеЗаписиЗаданий Тогда
		
		КоличествоДанных = ОчиститьНеиспользуемыеЗаписиЗаданий(СтруктураДанных);
		
		// Зафиксируем общий замер
		ОценкаПроизводительности.ЗафиксироватьЗамерДлительнойОперации(
			ОписаниеОбщегоЗамераПроизводительности,
			КоличествоДанныхДляЗамера(КоличествоДанных),
			"ОчиститьНеиспользуемыеЗаписиЗаданий");
			
	КонецЕсли;
	
	Если ЗначениеЗаполнено(ПараметрыЗапуска.АдресХранилища) Тогда
		
		// Запущено как фоновое задание - вернем данные не через параметр, а через временное хранилище.
		ПоместитьВоВременноеХранилище(СтруктураДанных, ПараметрыЗапуска.АдресХранилища);
	
		ВремяОкончания = ТекущаяУниверсальнаяДатаВМиллисекундах();
		
		ТекстДляЖурнала = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru='Время формирования списка: %1
				|Период: %2
				|Организации: %3'", ОбщегоНазначения.КодОсновногоЯзыка()),
			РасчетСебестоимостиПротоколРасчета.ПредставлениеВремени((ВремяОкончания - ВремяНачала)/1000),
			РасчетСебестоимостиПротоколРасчета.ПредставлениеПериодаРасчета(СтруктураДанных.ПараметрыРасчета.ПериодРегистрации),
			РасчетСебестоимостиПрикладныеАлгоритмы.ПредставлениеОрганизаций(СтруктураДанных.ПараметрыРасчета.МассивОрганизаций, ", ", Истина));
			
		ТекстДляЖурнала = ТекстДляЖурнала + "
			|
			|" + ТекстДлительностьЭтапов;
		
		ЗаписьЖурналаРегистрации(
			ЗакрытиеМесяцаСервер.ИмяСобытияЖурналаРегистрации(НСтр("ru = 'Формировании списка операций'", ОбщегоНазначения.КодОсновногоЯзыка())),
			УровеньЖурналаРегистрации.Информация,
			,
			,
			ТекстДляЖурнала);
		
	КонецЕсли;
	
	Попытка
		ОценкаПроизводительности.ЗакончитьЗамерДлительнойОперации(
			ОписаниеОбщегоЗамераПроизводительности,
			КоличествоДанныхДляЗамера(1));
	Исключение
		ОписаниеОбщегоЗамераПроизводительности = Неопределено;
	КонецПопытки;
	
КонецПроцедуры

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

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

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

Процедура УвеличитьКоличествоОбработанныхДанныхДляЗамера(ПараметрыОбработчика, КоличествоДанных = 1) Экспорт
	
	Если НЕ ПараметрыОбработчика.Свойство("КоличествоДанных") Тогда
		Возврат;
	КонецЕсли;
	
	ПараметрыОбработчика.КоличествоДанных =
		ПараметрыОбработчика.КоличествоДанных + ?(КоличествоДанных <= 0, 1, КоличествоДанных);
	
КонецПроцедуры

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

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

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

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

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

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

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

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

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

	СостоянияЭтапов = Новый Соответствие;

	Пока РезультатЗапроса.Следующий() Цикл
		
		Этап = СостоянияЭтапов.Получить(РезультатЗапроса.ОперацияЗакрытияМесяца);

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

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

Процедура УстановитьСостояниеЭтапа(ТекущееСостояние, НовоеСостояние)
	
	ПриоритетыСостояний = Новый Соответствие;
	ПриоритетыСостояний.Вставить(Перечисления.СостоянияОперацийЗакрытияМесяца.Неопределено, 1);
	ПриоритетыСостояний.Вставить(Перечисления.СостоянияОперацийЗакрытияМесяца.НеТребуется, 2);
	ПриоритетыСостояний.Вставить(Перечисления.СостоянияОперацийЗакрытияМесяца.ВыполненоУспешно, 3);
	ПриоритетыСостояний.Вставить(Перечисления.СостоянияОперацийЗакрытияМесяца.ОжидаетВыполненияПредыдущихОпераций, 4);
	ПриоритетыСостояний.Вставить(Перечисления.СостоянияОперацийЗакрытияМесяца.НеВыполнено, 5);
	ПриоритетыСостояний.Вставить(Перечисления.СостоянияОперацийЗакрытияМесяца.ВыполненоСОшибками, 6);
	
	Если ПриоритетыСостояний.Получить(ТекущееСостояние) < ПриоритетыСостояний.Получить(НовоеСостояние) Тогда
		ТекущееСостояние = НовоеСостояние;
	КонецЕсли;
	
КонецПроцедуры

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

#Область ОформлениеЭтапов

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

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

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

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

#Область РасчетЭтапов

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

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

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

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

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

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

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


#Область МетодыДляЗамераПроизводительности

// Используется для замера времени определения состояния этапов.
//
Функция КоличествоДанныхДляЗамера(Количество)
	
	// Удельное время получается очень маленьким из-за чего возможны ошибки округления.
	// Для их предотвращения уменьшим количество т.е. увеличим удельное время замера.
	Возврат ?(Количество = 0, 1, Количество) / 100;
	
КонецФункции

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

#Область Организации

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

Функция НазваниеГруппыОрганизацийИнтеркампани() Экспорт
	Возврат НСтр("ru='Интеркампани'");
КонецФункции

Функция НазваниеГруппыОрганизацийОСиНМА() Экспорт
	Возврат НСтр("ru='Правила учета ОС и НМА'");
КонецФункции

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

#Область УстановкаБлокировкиФункционалаПриОбновленииИБ

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

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

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

#КонецЕсли

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