Анализ журнала регистрации / Модуль менеджера

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

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

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

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

// СтандартныеПодсистемы.ВариантыОтчетов

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

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

// Конец СтандартныеПодсистемы.ВариантыОтчетов

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

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

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

// Функция получает информацию по активности пользователей из журнала регистрации
// по переданному периоду.
//
// Параметры:
//    ПараметрыОтчета - Структура:
//    * ДатаНачала          - Дата   - начало периода, по которому будет собираться информация.
//    * ДатаОкончания       - Дата   - окончание периода, по которому будет собираться информация.
//    * Пользователь        - Строка - имя пользователя, по которому проводить анализ.
//                                     Для варианта отчета "Активность пользователя".
//    * ПользователиИГруппы - СписокЗначений - где значение - группа(ы) пользователей и(или)
//                                     пользователь(и), по которым проводить анализ.
//                                     Для варианта отчета "Анализ активности пользователей".
//    * ВариантОтчета       - Строка - "АктивностьПользователя" или "АнализАктивностиПользователей".
//    * ВыводитьЗадачи      - Булево - получать или нет информацию по задачам из журнала регистрации.
//    * ВыводитьСправочники - Булево - получать или нет информацию по справочникам из журнала регистрации.
//    * ВыводитьДокументы   - Булево - получать или нет информацию документов из журнала регистрации.
//    * ВыводитьБизнесПроцессы - Булево - получать или нет информацию по бизнес процессам из журнала регистрации.
//
// Возвращаемое значение:
//  ТаблицаЗначений - таблица, содержащая не сгруппированную информацию по активности
//     пользователей из журнала регистрации.
//
Функция ДанныеИзЖурналаРегистрации(ПараметрыОтчета) Экспорт
	
	// Подготовка параметров отчета.
	ДатаНачала = ПараметрыОтчета.ДатаНачала;
	ДатаОкончания = ПараметрыОтчета.ДатаОкончания;
	Пользователь = ПараметрыОтчета.Пользователь;
	ПользователиИГруппы = ПараметрыОтчета.ПользователиИГруппы;
	Подразделение = ПараметрыОтчета.Подразделение;
	ВариантОтчета = ПараметрыОтчета.ВариантОтчета;
	
	Если ВариантОтчета = "АктивностьПользователя" Тогда
		ВыводитьСлужебныхПользователей = Истина;
		ВыводитьБизнесПроцессы = ПараметрыОтчета.ВыводитьБизнесПроцессы;
		ВыводитьЗадачи = ПараметрыОтчета.ВыводитьЗадачи;
		ВыводитьСправочники = ПараметрыОтчета.ВыводитьСправочники;
		ВыводитьДокументы = ПараметрыОтчета.ВыводитьДокументы;
	Иначе
		ВыводитьСлужебныхПользователей = ПараметрыОтчета.ВыводитьСлужебныхПользователей;
		ВыводитьСправочники = Истина;
		ВыводитьДокументы = Истина;
		ВыводитьБизнесПроцессы = Ложь;
		ВыводитьЗадачи = Ложь;
	КонецЕсли;
	
	// Формируем таблицу исходных данных.
	ИсходныеДанные = Новый ТаблицаЗначений();
	ИсходныеДанные.Колонки.Добавить("Дата", Новый ОписаниеТипов("Дата", , , Новый КвалификаторыДаты(ЧастиДаты.Дата)));
	ИсходныеДанные.Колонки.Добавить("Неделя", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(10)));
	ИсходныеДанные.Колонки.Добавить("Пользователь");
	ИсходныеДанные.Колонки.Добавить("Подразделение");
	ИсходныеДанные.Колонки.Добавить("ПодразделениеПредставление");
	ИсходныеДанные.Колонки.Добавить("ЧасовРаботы", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15,4)));
	ИсходныеДанные.Колонки.Добавить("КоличествоЗапусков", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10)));
	ИсходныеДанные.Колонки.Добавить("СозданоДокументов", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10)));
	ИсходныеДанные.Колонки.Добавить("СозданоСправочников", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10)));
	ИсходныеДанные.Колонки.Добавить("ИзмененоДокументов", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10)));
	ИсходныеДанные.Колонки.Добавить("СозданоБизнесПроцессов",	Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10)));
	ИсходныеДанные.Колонки.Добавить("СозданоЗадач", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10)));
	ИсходныеДанные.Колонки.Добавить("ИзмененоБизнесПроцессов", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10)));
	ИсходныеДанные.Колонки.Добавить("ИзмененоЗадач", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10)));
	ИсходныеДанные.Колонки.Добавить("ИзмененоСправочников",	Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10)));
	ИсходныеДанные.Колонки.Добавить("Ошибок", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10)));
	ИсходныеДанные.Колонки.Добавить("Предупреждений", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10)));
	ИсходныеДанные.Колонки.Добавить("ВидОбъекта", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(50)));
	ИсходныеДанные.Колонки.Добавить("ОбъектСправочникДокумент");
	
	// Расчет максимального количества одновременных сессий.
	ОдновременноСессий = Новый ТаблицаЗначений();
	ОдновременноСессий.Колонки.Добавить("ДатаОдновременноРаботавшихПользователей",
		Новый ОписаниеТипов("Дата", , , Новый КвалификаторыДаты(ЧастиДаты.Дата)));
	ОдновременноСессий.Колонки.Добавить("ОдновременноРаботавшихПользователей",
		Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10)));
	ОдновременноСессий.Колонки.Добавить("СписокОдновременноРаботавшихПользователей");
	
	ДанныеЖурналаРегистрации = Новый ТаблицаЗначений;
	
	Уровни = Новый Массив;
	Уровни.Добавить(УровеньЖурналаРегистрации.Информация);
	
	События = Новый Массив;
	События.Добавить("_$Session$_.Start"); //  Начало сессии
	События.Добавить("_$Session$_.Finish"); //  Конец сессии  
	События.Добавить("_$Data$_.New"); // Добавление данных
	События.Добавить("_$Data$_.Update"); // Изменение данных
	
	ИмяПриложения = Новый Массив;
	ИмяПриложения.Добавить("1CV8C");
	ИмяПриложения.Добавить("WebClient");
	ИмяПриложения.Добавить("1CV8");
	ИмяПриложения.Добавить("BackgroundJob");
	ИмяПриложения.Добавить("MobileClient");
	ИмяПриложения.Добавить("HTTPServiceConnection");
	ИмяПриложения.Добавить("WSConnection");
	ИмяПриложения.Добавить("ODataConnection");
	ИмяПриложения.Добавить("COMConnection");
	
	ДатыВЧасовомПоясеСервера = ОбщегоНазначенияКлиентСервер.СвойствоСтруктуры(ПараметрыОтчета,
		"ДатыВЧасовомПоясеСервера", Ложь);
	Если ДатыВЧасовомПоясеСервера Тогда
		СмещениеВремениСервера = 0;
	Иначе
		СмещениеВремениСервера = ЖурналРегистрации.СмещениеВремениСервера();
	КонецЕсли;
	
	ПользовательФильтр = Новый Массив;
	ВыбранныеПодразделения = Неопределено;
	ПодразделенияПользователей = Неопределено;
	
	// Получаем список пользователей.
	Если ВариантОтчета = "АктивностьПользователя" Тогда
		ПользовательФильтр.Добавить(ПользовательДляОтбора(Пользователь));
	ИначеЕсли ВариантОтчета = "АнализАктивностиПодразделений" Тогда
		ВыбранныеПодразделения = ВыбранныеПодразделения(Подразделение);
		ПодразделенияПользователей = ПодразделенияПользователей(ДатаНачала,
			СмещениеВремениСервера, ВыбранныеПодразделения);
		ЗаполнитьПользователейДляАнализаИзПодразделения(ПользовательФильтр,
			ВыбранныеПодразделения, ПодразделенияПользователей);
	Иначе
		ЗаполнитьПользователейДляАнализа(ПользовательФильтр, ПользователиИГруппы);
	КонецЕсли;
	
	Если ПользовательФильтр.Количество() = 0 Тогда
		Возврат Новый Структура("АнализАктивностиПользователей, ОдновременноСессий, ОтчетПустой",
			ИсходныеДанные, ОдновременноСессий, Истина);
	КонецЕсли;
	
	ОтборЖурналаРегистрации = Новый Структура;
	ОтборЖурналаРегистрации.Вставить("ДатаНачала", ДатаНачала + СмещениеВремениСервера);
	ОтборЖурналаРегистрации.Вставить("ДатаОкончания", ДатаОкончания + СмещениеВремениСервера);
	ОтборЖурналаРегистрации.Вставить("ИмяПриложения", ИмяПриложения);
	ОтборЖурналаРегистрации.Вставить("Уровень", Уровни);
	ОтборЖурналаРегистрации.Вставить("Событие", События);
	
	Если ПользовательФильтр.Найти("ВсеПользователи") = Неопределено Тогда
		ОтборЖурналаРегистрации.Вставить("Пользователь", ПользовательФильтр);
	Иначе
		ПользовательФильтр = Неопределено;
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Истина);
	ВыгрузитьЖурналРегистрации(ДанныеЖурналаРегистрации, ОтборЖурналаРегистрации);
	УстановитьПривилегированныйРежим(Ложь);
	
	ОтчетПустой = (ДанныеЖурналаРегистрации.Количество() = 0);
	
	// Добавляем соответствие УникальныйИдентификатор-ПользовательСсылка для дальнейшего использования.
	СоответствиеИдентификаторыПользователей = УникальныеИдентификаторыПользователей(ДанныеЖурналаРегистрации,
		ВыводитьСлужебныхПользователей);
	
	Сеансы = Новый ТаблицаЗначений;
	Сеансы.Колонки.Добавить("НомерСеанса");
	Сеансы.Колонки.Добавить("СобытиеНачала");
	Сеансы.Колонки.Добавить("СобытиеЗавершения");
	Сеансы.Колонки.Добавить("Пользователь");
	Сеансы.Колонки.Добавить("Подразделение");
	Сеансы.Колонки.Добавить("ПодразделениеПредставление");
	Сеансы.Колонки.Добавить("ДатаПервогоСобытияСеанса");
	Сеансы.Колонки.Добавить("ДатаПоследнегоСобытияСеанса");
	Сеансы.Индексы.Добавить("НомерСеанса");
	
	// Расчет данных, необходимых для составления отчета.
	Для Каждого СтрокаДанныхЖурналаРегистрации Из ДанныеЖурналаРегистрации Цикл
		СтрокаДанныхЖурналаРегистрации.Дата = СтрокаДанныхЖурналаРегистрации.Дата - СмещениеВремениСервера;
		
		Если Не ЗначениеЗаполнено(СтрокаДанныхЖурналаРегистрации.Сеанс)
			Или Не ЗначениеЗаполнено(СтрокаДанныхЖурналаРегистрации.Дата) Тогда
			Продолжить;
		КонецЕсли;
		
		ИмяПользователяСсылка = СоответствиеИдентификаторыПользователей[СтрокаДанныхЖурналаРегистрации.Пользователь];
		Если ИмяПользователяСсылка = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		ПодразделениеПользователяИБ = Новый Структура("Подразделение, ПодразделениеПредставление");
		Если ПодразделенияПользователей <> Неопределено Тогда
			ЗаполнитьПодразделениеПользователяИБ(ПодразделениеПользователяИБ, СтрокаДанныхЖурналаРегистрации.Дата,
				СтрокаДанныхЖурналаРегистрации.Пользователь, ПодразделенияПользователей);
			Если ВыбранныеПодразделения <> Неопределено
			   И ВыбранныеПодразделения.Найти(ПодразделениеПользователяИБ.Подразделение) = Неопределено Тогда
				Продолжить;
			КонецЕсли;
		КонецЕсли;
		
		// Подготовка к расчету продолжительности работы пользователей и количества запусков программы.
		Сеанс = Сеансы.Найти(СтрокаДанныхЖурналаРегистрации.Сеанс, "НомерСеанса");
		СеансДобавлен = Ложь;
		Если СтрокаДанныхЖурналаРегистрации.Событие = "_$Session$_.Start" Тогда
			Если Сеанс <> Неопределено Тогда
				Сеанс.НомерСеанса = Неопределено;
			КонецЕсли;
			Сеанс = Сеансы.Добавить();
			Сеанс.НомерСеанса   = СтрокаДанныхЖурналаРегистрации.Сеанс;
			Сеанс.СобытиеНачала = СтрокаДанныхЖурналаРегистрации;
			СеансДобавлен = Истина;
			
		ИначеЕсли СтрокаДанныхЖурналаРегистрации.Событие = "_$Session$_.Finish" Тогда
			Если Сеанс = Неопределено Тогда
				Сеанс = Сеансы.Добавить();
				СеансДобавлен = Истина;
			КонецЕсли;
			Сеанс.НомерСеанса = Неопределено;
			Сеанс.СобытиеЗавершения = СтрокаДанныхЖурналаРегистрации;
		Иначе
			Если Сеанс = Неопределено Тогда
				Сеанс = Сеансы.Добавить();
				Сеанс.ДатаПервогоСобытияСеанса = СтрокаДанныхЖурналаРегистрации.Дата;
				СеансДобавлен = Истина;
			КонецЕсли;
			Сеанс.ДатаПоследнегоСобытияСеанса = СтрокаДанныхЖурналаРегистрации.Дата;
		КонецЕсли;
		Если СеансДобавлен Тогда
			Сеанс.Пользователь = ИмяПользователяСсылка;
			ЗаполнитьЗначенияСвойств(Сеанс, ПодразделениеПользователяИБ);
		КонецЕсли;

		СобытиеМетаданные = СтрокаДанныхЖурналаРегистрации.Метаданные;
		СтрокаИсходныхДанных = Неопределено;
		
		// Расчет количества созданных документов и справочников.
		Если СтрокаДанныхЖурналаРегистрации.Событие = "_$Data$_.New" Тогда
			Если СтрНайти(СобытиеМетаданные, "Документ.") > 0 И ВыводитьДокументы Тогда
				СтрокаИсходныхДанных = ИсходныеДанные.Добавить();
				СтрокаИсходныхДанных.СозданоДокументов = 1;
			КонецЕсли;
			Если СтрНайти(СобытиеМетаданные, "Справочник.") > 0 И ВыводитьСправочники Тогда
				СтрокаИсходныхДанных = ИсходныеДанные.Добавить();
				СтрокаИсходныхДанных.СозданоСправочников = 1;
			КонецЕсли;
		КонецЕсли;
		
		// Расчет количества измененных документов и справочников.
		Если СтрокаДанныхЖурналаРегистрации.Событие = "_$Data$_.Update" Тогда
			Если СтрНайти(СобытиеМетаданные, "Документ.") > 0 И ВыводитьДокументы Тогда
				СтрокаИсходныхДанных = ИсходныеДанные.Добавить();
				СтрокаИсходныхДанных.ИзмененоДокументов = 1;
			КонецЕсли;
			Если СтрНайти(СобытиеМетаданные, "Справочник.") > 0 И ВыводитьСправочники Тогда
				СтрокаИсходныхДанных = ИсходныеДанные.Добавить();
				СтрокаИсходныхДанных.ИзмененоСправочников = 1;
			КонецЕсли;
		КонецЕсли;
		
		// Расчет количества созданных БизнесПроцессов и Задач.
		Если СтрокаДанныхЖурналаРегистрации.Событие = "_$Data$_.New" Тогда
			Если СтрНайти(СобытиеМетаданные, "БизнесПроцесс.") > 0  И ВыводитьБизнесПроцессы Тогда
				СтрокаИсходныхДанных = ИсходныеДанные.Добавить();
				СтрокаИсходныхДанных.СозданоБизнесПроцессов = 1;
			КонецЕсли;
			Если СтрНайти(СобытиеМетаданные, "Задача.") > 0 И ВыводитьЗадачи Тогда
				СтрокаИсходныхДанных = ИсходныеДанные.Добавить();
				СтрокаИсходныхДанных.СозданоЗадач = 1;
			КонецЕсли;
		КонецЕсли;
		
		// Расчет количества измененных БизнесПроцессов и задач.
		Если СтрокаДанныхЖурналаРегистрации.Событие = "_$Data$_.Update" Тогда
			Если СтрНайти(СобытиеМетаданные, "БизнесПроцесс.") > 0 И ВыводитьБизнесПроцессы Тогда
				СтрокаИсходныхДанных = ИсходныеДанные.Добавить();
				СтрокаИсходныхДанных.ИзмененоБизнесПроцессов = 1;
			КонецЕсли;
			Если СтрНайти(СобытиеМетаданные, "Задача.") > 0 И ВыводитьЗадачи Тогда
				СтрокаИсходныхДанных = ИсходныеДанные.Добавить();
				СтрокаИсходныхДанных.ИзмененоЗадач = 1;
			КонецЕсли;
		КонецЕсли;
		
		Если СтрокаИсходныхДанных <> Неопределено Тогда
			СтрокаИсходныхДанных.Дата = СтрокаДанныхЖурналаРегистрации.Дата;
			СтрокаИсходныхДанных.Неделя = СтрокаНеделяГода(СтрокаДанныхЖурналаРегистрации.Дата); 
			СтрокаИсходныхДанных.ВидОбъекта = СтрокаДанныхЖурналаРегистрации.ПредставлениеМетаданных;
			СтрокаИсходныхДанных.ОбъектСправочникДокумент = СтрокаДанныхЖурналаРегистрации.Данные;
			СтрокаИсходныхДанных.Пользователь = ИмяПользователяСсылка;
			ЗаполнитьЗначенияСвойств(СтрокаИсходныхДанных, ПодразделениеПользователяИБ);
		КонецЕсли;
		
	КонецЦикла;
	
	// Расчет продолжительности работы пользователей и количества запусков программы.
	Для Каждого Сеанс Из Сеансы Цикл
		Если Сеанс.СобытиеНачала <> Неопределено Тогда
			Начало = Сеанс.СобытиеНачала.Дата;
		ИначеЕсли ЗначениеЗаполнено(Сеанс.ДатаПервогоСобытияСеанса) Тогда
			Начало = Сеанс.ДатаПервогоСобытияСеанса;
		Иначе
			Начало = Сеанс.СобытиеЗавершения.Дата;
		КонецЕсли;
		Если Сеанс.СобытиеЗавершения <> Неопределено Тогда
			Завершение = Сеанс.СобытиеЗавершения.Дата;
		ИначеЕсли ЗначениеЗаполнено(Сеанс.ДатаПоследнегоСобытияСеанса) Тогда
			Завершение = Сеанс.ДатаПоследнегоСобытияСеанса;
		Иначе
			Завершение = Начало;
		КонецЕсли;
		КоличествоЗапусков = 1;
		Продолжать = Истина;
		Пока Продолжать Цикл
			Дата = Начало;
			Если НачалоДня(Начало) < НачалоДня(Завершение) Тогда
				Начало = НачалоДня(Начало) + 86400;
				ЧасовРаботы = (Начало - Дата) / 3600;
			Иначе
				Продолжать = Ложь;
				ЧасовРаботы = (Завершение - Начало) / 3600;
			КонецЕсли;
			СтрокаИсходныхДанных = ИсходныеДанные.Добавить();
			СтрокаИсходныхДанных.Дата = Дата;
			СтрокаИсходныхДанных.Неделя = СтрокаНеделяГода(Дата);
			СтрокаИсходныхДанных.Пользователь = Сеанс.Пользователь;
			СтрокаИсходныхДанных.Подразделение = Сеанс.Подразделение;
			СтрокаИсходныхДанных.ПодразделениеПредставление = Сеанс.ПодразделениеПредставление;
			СтрокаИсходныхДанных.КоличествоЗапусков = КоличествоЗапусков;
			СтрокаИсходныхДанных.ЧасовРаботы = ?(ЧасовРаботы = 0, 0.0001, ЧасовРаботы);
			КоличествоЗапусков = 0;
		КонецЦикла;
	КонецЦикла;
	
	Если ВариантОтчета = "АнализАктивностиПользователей" Тогда
	
		МассивПользователей 	= Новый Массив;
		МаксМассивПользователей = Новый Массив;
		ОдновременноРаботавшихПользователей  = 0;
		Счетчик                 = 0;
		ТекущаяДата             = Неопределено;
		
		Для Каждого СтрокаДанныхЖурналаРегистрации Из ДанныеЖурналаРегистрации Цикл
			
			Если Не ЗначениеЗаполнено(СтрокаДанныхЖурналаРегистрации.Дата) Тогда
				Продолжить;
			КонецЕсли;
			
			ИмяПользователяСсылка = СоответствиеИдентификаторыПользователей[СтрокаДанныхЖурналаРегистрации.Пользователь];
			Если ИмяПользователяСсылка = Неопределено Тогда
				Продолжить;
			КонецЕсли;
			
			ДатаОдновременноРаботавшихПользователей = НачалоДня(СтрокаДанныхЖурналаРегистрации.Дата);
			
			// При смене дня обнуляем все данные по одновременным сессиям и заполняем данные за прошедший день.
			Если ТекущаяДата <> ДатаОдновременноРаботавшихПользователей Тогда
				Если ОдновременноРаботавшихПользователей <> 0 Тогда
					СформироватьСтрокуОдновременноСессий(ОдновременноСессий, МаксМассивПользователей, 
						ОдновременноРаботавшихПользователей, ТекущаяДата);
				КонецЕсли;
				ОдновременноРаботавшихПользователей = 0;
				Счетчик    = 0;
				МассивПользователей.Очистить();
				ТекущаяДата = ДатаОдновременноРаботавшихПользователей;
			КонецЕсли;
			
			Если СтрокаДанныхЖурналаРегистрации.Событие = "_$Session$_.Start" Тогда
				Счетчик = Счетчик + 1;
				МассивПользователей.Добавить(ИмяПользователяСсылка);
			ИначеЕсли СтрокаДанныхЖурналаРегистрации.Событие = "_$Session$_.Finish" Тогда
				ИндексПользователя = МассивПользователей.Найти(ИмяПользователяСсылка);
				Если Не ИндексПользователя = Неопределено Тогда 
					МассивПользователей.Удалить(ИндексПользователя);
					Счетчик = Счетчик - 1;
				КонецЕсли;
			КонецЕсли;
			
			// Проверяем значение счетчика и сравниваем с максимальным значением.
			Счетчик = Макс(Счетчик, 0);
			Если Счетчик > ОдновременноРаботавшихПользователей Тогда
				МаксМассивПользователей = Новый Массив;
				Для Каждого Элемент Из МассивПользователей Цикл
					МаксМассивПользователей.Добавить(Элемент);
				КонецЦикла;
			КонецЕсли;
			ОдновременноРаботавшихПользователей = Макс(ОдновременноРаботавшихПользователей, Счетчик);
			
		КонецЦикла;
		
		Если ОдновременноРаботавшихПользователей <> 0 Тогда
			СформироватьСтрокуОдновременноСессий(ОдновременноСессий, МаксМассивПользователей, 
				ОдновременноРаботавшихПользователей, ТекущаяДата);
		КонецЕсли;
		
		// Расчет количества ошибок и предупреждений.
		ДанныеЖурналаРегистрации = ИнформацияПоОшибкамЖурналаРегистрации(ДатаНачала,
			ДатаОкончания, СмещениеВремениСервера, ПользовательФильтр);
		
		ОтчетПустой =  ОтчетПустой Или (ДанныеЖурналаРегистрации.Количество() = 0);
		
		Для Каждого СтрокаДанныхЖурналаРегистрации Из ДанныеЖурналаРегистрации Цикл
			
			ИмяПользователяСсылка = СоответствиеИдентификаторыПользователей[СтрокаДанныхЖурналаРегистрации.Пользователь];
			Если ИмяПользователяСсылка = Неопределено Тогда
				Продолжить;
			КонецЕсли;
			
			Если СтрокаДанныхЖурналаРегистрации.Уровень = УровеньЖурналаРегистрации.Ошибка Тогда
				СтрокаИсходныхДанных = ИсходныеДанные.Добавить();
				СтрокаИсходныхДанных.Ошибок = 1;
			КонецЕсли;
			
			Если СтрокаДанныхЖурналаРегистрации.Уровень = УровеньЖурналаРегистрации.Предупреждение Тогда
				СтрокаИсходныхДанных = ИсходныеДанные.Добавить();
				СтрокаИсходныхДанных.Предупреждений = 1;
			КонецЕсли;
			
			Если СтрокаИсходныхДанных <> Неопределено Тогда
				СтрокаИсходныхДанных.Дата = СтрокаДанныхЖурналаРегистрации.Дата;
				СтрокаИсходныхДанных.Неделя = СтрокаНеделяГода(СтрокаДанныхЖурналаРегистрации.Дата); 
				СтрокаИсходныхДанных.Пользователь = ИмяПользователяСсылка;
				Если ПодразделенияПользователей <> Неопределено Тогда
					ЗаполнитьПодразделениеПользователяИБ(СтрокаИсходныхДанных, СтрокаДанныхЖурналаРегистрации.Дата,
						СтрокаДанныхЖурналаРегистрации.Пользователь, ПодразделенияПользователей);
				КонецЕсли;
			КонецЕсли;
			
		КонецЦикла;
		
	КонецЕсли;
	
	Возврат Новый Структура("АнализАктивностиПользователей, ОдновременноСессий, ОтчетПустой",
		ИсходныеДанные, ОдновременноСессий, ОтчетПустой);
	
КонецФункции

#Область АнализАктивностиПользователей

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

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

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

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

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

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

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

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

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

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

Функция СтрокаНеделяГода(ДатаГода)
	Возврат СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Неделя %1'"), НеделяГода(ДатаГода));
КонецФункции

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

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

#Область ПродолжительностьРаботыРегламентныхЗаданий

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

	ТекущееСобытие			 = Неопределено;
	ОбщаяПродолжительностьРЗ = 0;
	Точка					 = Неопределено;
	СтрокаКоличествоЗапусков = Неопределено;
	ЗапусковРегламентногоЗадания = 0;
	ПризнакСменыТочки        = Ложь;
	
	// Заполняем диаграмму ганта	
	Для Каждого СтрокаРегламентныеЗадания Из ТаблицаСеансыРегламентныхЗаданий Цикл
		ДлительностьИнтервалаРегламентногоЗадания =
			СтрокаРегламентныеЗадания.ДатаОкончанияЗадания - СтрокаРегламентныеЗадания.ДатаЗапускаЗадания;
		Если ДлительностьИнтервалаРегламентногоЗадания >= МинимальнаяПродолжительностьСеансовРегламентныхЗаданий Тогда
			Если ТекущееСобытие <> СтрокаРегламентныеЗадания.НазваниеСобытия Тогда
				Если ТекущееСобытие <> Неопределено
					И ПризнакСменыТочки Тогда
					РасшифровкаТочки = Точка.Расшифровка; // Массив
					РасшифровкаТочки.Добавить(ЗапусковРегламентногоЗадания);
					РасшифровкаТочки.Добавить(ОбщаяПродолжительностьРЗ);
					РасшифровкаТочки.Добавить(ДатаНачала);
					РасшифровкаТочки.Добавить(ДатаОкончания);
					ТочкаНазвание = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = '%1 (%2 из %3)'"),
						Точка.Значение,
						ЗапусковРегламентногоЗадания,
						Строка(СтрокаКоличествоЗапусков.Запусков));
					Точка.Значение = ТочкаНазвание;
				КонецЕсли;
				СтрокаКоличествоЗапусков = КоличествоЗапусков.Найти(
					СтрокаРегламентныеЗадания.НазваниеСобытия, "НазваниеСобытия");
				// Не заполняем расшифровку для фоновых заданий.
				Если СтрокаРегламентныеЗадания.СобытиеМетаданные <> "" Тогда 
					ТочкаНазвание = СтрокаРегламентныеЗадания.НазваниеСобытия;
					Точка = ДиаграммаГанта.УстановитьТочку(ТочкаНазвание);
					РасшифровкаТочки  = Новый Массив;
					ИнтервалНачало	  = Новый Массив;
					ИнтервалКонец	  = Новый Массив;
					СеансРегламентногоЗадания = Новый Массив;
					РасшифровкаТочки.Добавить("РасшифровкаТочки");
					РасшифровкаТочки.Добавить(СтрокаРегламентныеЗадания.СобытиеМетаданные);
					РасшифровкаТочки.Добавить(СтрокаРегламентныеЗадания.НазваниеСобытия);
					РасшифровкаТочки.Добавить(СтрокаКоличествоЗапусков.Отменено);
					РасшифровкаТочки.Добавить(СтрокаКоличествоЗапусков.ОшибкаВыполнения);                                                             
					РасшифровкаТочки.Добавить(ИнтервалНачало);
					РасшифровкаТочки.Добавить(ИнтервалКонец);
					РасшифровкаТочки.Добавить(СеансРегламентногоЗадания);
					РасшифровкаТочки.Добавить(МинимальнаяПродолжительностьСеансовРегламентныхЗаданий);
					Точка.Расшифровка = РасшифровкаТочки;
					ТекущееСобытие = СтрокаРегламентныеЗадания.НазваниеСобытия;
					ОбщаяПродолжительностьРЗ = 0;				
					ЗапусковРегламентногоЗадания = 0;
					Точка.Картинка = БиблиотекаКартинок.РегламентноеЗадание;
				ИначеЕсли Не ЗначениеЗаполнено(СтрокаРегламентныеЗадания.СобытиеМетаданные) Тогда
					ТочкаНазвание = НСтр("ru = 'Фоновые задания'");
					Точка = ДиаграммаГанта.УстановитьТочку(ТочкаНазвание);
					ОбщаяПродолжительностьРЗ = 0;
				КонецЕсли;
			КонецЕсли;
			Значение = ДиаграммаГанта.ПолучитьЗначение(Точка, Серия);
			Интервал = Значение.Добавить();
			Интервал.Начало = СтрокаРегламентныеЗадания.ДатаЗапускаЗадания;
			Интервал.Конец = СтрокаРегламентныеЗадания.ДатаОкончанияЗадания;
			Интервал.Текст = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = '%1 - %2'"),
				Формат(Интервал.Начало, "ДЛФ=T"),
				Формат(Интервал.Конец, "ДЛФ=T"));
			ПризнакСменыТочки = Ложь;
			// Не заполняем расшифровку для фоновых заданий.
			Если СтрокаРегламентныеЗадания.СобытиеМетаданные <> "" Тогда
				ИнтервалНачало.Добавить(СтрокаРегламентныеЗадания.ДатаЗапускаЗадания);
				ИнтервалКонец.Добавить(СтрокаРегламентныеЗадания.ДатаОкончанияЗадания);
				СеансРегламентногоЗадания.Добавить(СтрокаРегламентныеЗадания.Сеанс);
				ОбщаяПродолжительностьРЗ = ДлительностьИнтервалаРегламентногоЗадания + ОбщаяПродолжительностьРЗ;
				ЗапусковРегламентногоЗадания = ЗапусковРегламентногоЗадания + 1;
				ПризнакСменыТочки = Истина;
			КонецЕсли;
		КонецЕсли;
	КонецЦикла; 
	
	Если ЗапусковРегламентногоЗадания <> 0
		И ЗначениеЗаполнено(Точка.Расшифровка) Тогда
		// Присваиваем расшифровку последней точке.
		РасшифровкаТочки = Точка.Расшифровка; // Массив
		РасшифровкаТочки.Добавить(ЗапусковРегламентногоЗадания);
		РасшифровкаТочки.Добавить(ОбщаяПродолжительностьРЗ);
		РасшифровкаТочки.Добавить(ДатаНачала);
		РасшифровкаТочки.Добавить(ДатаОкончания);	
		ТочкаНазвание = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = '%1 (%2 из %3)'"),
			Точка.Значение,
			ЗапусковРегламентногоЗадания,
			Строка(СтрокаКоличествоЗапусков.Запусков));
		Точка.Значение = ТочкаНазвание;
	КонецЕсли;
		
	// Устанавливаем настройки отображения диаграммы.
	ЦветаДиаграммыГанта(ДатаНачала, ДиаграммаГанта, ОдновременноСессий, ЦветаФона);
	ПериодАнализа = ДатаОкончания - ДатаНачала;
	ШкалаВремениДиаграммыГанта(ДиаграммаГанта, ПериодАнализа);
	
	КоличествоКолонок = ДиаграммаГанта.Точки.Количество();
	Область.Рисунки.ДиаграммаГанта.Высота				 = 15 + 10 * КоличествоКолонок;
	Область.Рисунки.ДиаграммаГанта.Ширина 				 = 450;
	ДиаграммаГанта.АвтоОпределениеПолногоИнтервала	 = Ложь; 
	ДиаграммаГанта.ОтображениеИнтервала   			 = ОтображениеИнтервалаДиаграммыГанта.Плоский;
	ДиаграммаГанта.ОбластьЛегенды.Расположение       = РасположениеЛегендыДиаграммы.Нет;
	ДиаграммаГанта.РастягиваниеПоВертикали 			 = РастягиваниеПоВертикалиДиаграммыГанта.РастягиватьСтрокиИДанные;
	ДиаграммаГанта.УстановитьПолныйИнтервал(ДатаНачала, ДатаОкончания);
	ДиаграммаГанта.Обновление = Истина;

	Отчет.Вывести(Область);
	
	Результат.Вставить("Отчет", Отчет);
	Результат.Вставить("ОтчетПустой", ОтчетПустой);
	Возврат Результат;
КонецФункции

// Функция получает информацию по регламентным заданиям из журнала регистрации.
//
// Параметры:
//   ПараметрыЗаполнения - Структура - набор параметров, необходимых для построения отчета:
//   * ДатаНачала    - Дата - начало периода, по которому будет собираться информация.
//   * ДатаОкончания - Дата - окончание периода, по которому будет собираться информация.
//   * РазмерОдновременноСессий	- Число - минимальное количество одновременно работавших регламентных
// 		заданий для отображения в таблице.
//   * МинимальнаяПродолжительностьСеансовРегламентныхЗаданий - Число - минимальная продолжительность
// 		сеансов регламентных заданий в секундах.
//   * ОтображатьФоновыеЗадания - Булево - если истина, на диаграмме ганта будет выводиться строка с 
// 		интервалами сеансов фоновых заданий.
//   * СкрытьРегламентныеЗадания - СписокЗначений - список регламентных заданий, которые необходимо исключить из отчета.
//
// Возвращаемое значение
//   ТаблицаЗначений - таблица, содержащая информацию по работе регламентных заданий
//     из журнала регистрации.
//
Функция ДанныеДляОтчетаПоПродолжительностиРаботыРегламентныхЗаданий(ПараметрыЗаполнения)
	
	ДатаНачала = ПараметрыЗаполнения.ДатаНачала;
	ДатаОкончания = ПараметрыЗаполнения.ДатаОкончания;
	РазмерОдновременноСессий = ПараметрыЗаполнения.РазмерОдновременноСессий;
	ОтображатьФоновыеЗадания = ПараметрыЗаполнения.ОтображатьФоновыеЗадания;
	МинимальнаяПродолжительностьСеансовРегламентныхЗаданий =
		ПараметрыЗаполнения.МинимальнаяПродолжительностьСеансовРегламентныхЗаданий;
	СкрытьРегламентныеЗадания = ПараметрыЗаполнения.СкрытьРегламентныеЗадания;
	СмещениеВремениСервера = ПараметрыЗаполнения.СмещениеВремениСервера;
	
	Уровни = Новый Массив;
	Уровни.Добавить(УровеньЖурналаРегистрации.Информация);
	Уровни.Добавить(УровеньЖурналаРегистрации.Предупреждение);
	Уровни.Добавить(УровеньЖурналаРегистрации.Ошибка);
	
	СобытияРЗ = Новый Массив;
	СобытияРЗ.Добавить("_$Job$_.Start");
	СобытияРЗ.Добавить("_$Job$_.Cancel");
	СобытияРЗ.Добавить("_$Job$_.Fail");
	СобытияРЗ.Добавить("_$Job$_.Succeed");
	СобытияРЗ.Добавить("_$Job$_.Finish");
	СобытияРЗ.Добавить("_$Job$_.Error");
	
	УстановитьПривилегированныйРежим(Истина);
	ОтборЖурнала = Новый Структура;
	ОтборЖурнала.Вставить("Уровень", Уровни);
	ОтборЖурнала.Вставить("ДатаНачала", ДатаНачала + СмещениеВремениСервера);
	ОтборЖурнала.Вставить("ДатаОкончания", ДатаОкончания + СмещениеВремениСервера);
	ОтборЖурнала.Вставить("Событие", СобытияРЗ);
	
	ДанныеЖурналаРегистрации = Новый ТаблицаЗначений;
	ВыгрузитьЖурналРегистрации(ДанныеЖурналаРегистрации, ОтборЖурнала);
	ОтчетПустой = (ДанныеЖурналаРегистрации.Количество() = 0);
	
	Если СмещениеВремениСервера <> 0 Тогда
		Для Каждого СтрокаТаблицы Из ДанныеЖурналаРегистрации Цикл
			СтрокаТаблицы.Дата = СтрокаТаблицы.Дата - СмещениеВремениСервера;
		КонецЦикла;
	КонецЕсли;
	
	// Формируем данные для отбора по регламентным заданиям.
	СписокВсехРегламентныхЗаданий = РегламентныеЗаданияСервер.НайтиЗадания(Новый Структура);
	СоответствиеМетаданныеИдентификатор = Новый Соответствие;
	СоответствиеМетаданныеНазвание = Новый Соответствие;
	СоответствиеНаименованиеИдентификатор = Новый Соответствие;
	УстановитьПривилегированныйРежим(Ложь);
	
	Для Каждого РегЗадание Из СписокВсехРегламентныхЗаданий Цикл
		СоответствиеМетаданныеИдентификатор.Вставить(РегЗадание.Метаданные, Строка(РегЗадание.УникальныйИдентификатор));
		СоответствиеНаименованиеИдентификатор.Вставить(РегЗадание.Наименование, Строка(РегЗадание.УникальныйИдентификатор));
		Если РегЗадание.Наименование <> "" Тогда
			СоответствиеМетаданныеНазвание.Вставить(РегЗадание.Метаданные, РегЗадание.Наименование);
		Иначе
			СоответствиеМетаданныеНазвание.Вставить(РегЗадание.Метаданные, РегЗадание.Метаданные.Синоним);
		КонецЕсли;
	КонецЦикла;
	
	// Заполняем параметры, необходимые для определения одновременно работавших регламентных заданий.
	ПараметрыОдновременноСессий = Новый Структура;
	ПараметрыОдновременноСессий.Вставить("ДанныеЖурналаРегистрации", ДанныеЖурналаРегистрации);
	ПараметрыОдновременноСессий.Вставить("СоответствиеНаименованиеИдентификатор", СоответствиеНаименованиеИдентификатор);
	ПараметрыОдновременноСессий.Вставить("СоответствиеМетаданныеИдентификатор", СоответствиеМетаданныеИдентификатор);
	ПараметрыОдновременноСессий.Вставить("СоответствиеМетаданныеНазвание", СоответствиеМетаданныеНазвание);
	ПараметрыОдновременноСессий.Вставить("СкрытьРегламентныеЗадания", СкрытьРегламентныеЗадания);
	ПараметрыОдновременноСессий.Вставить("МинимальнаяПродолжительностьСеансовРегламентныхЗаданий",
		МинимальнаяПродолжительностьСеансовРегламентныхЗаданий);
	
	// Максимальное количество одновременных сессий	регламентных заданий.
	ОдновременноСессий = ОдновременноРегламентныхЗаданий(ПараметрыОдновременноСессий);
	
	// Отбираем нужные значения из таблицы ОдновременноСессий.
	ОдновременноСессий.Сортировать("ОдновременноРегламентныхЗаданий Убыв");
	
	СтрокаИтогоОдновременноРегламентныхЗаданий = Неопределено;
	ИтогоОдновременноРегламентныхЗаданий = Новый ТаблицаЗначений();
	ИтогоОдновременноРегламентныхЗаданий.Колонки.Добавить("ДатаОдновременноРегламентныхЗаданий", 
		Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(50)));
	ИтогоОдновременноРегламентныхЗаданий.Колонки.Добавить("ОдновременноРегламентныхЗаданий", 
		Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10))); 
	ИтогоОдновременноРегламентныхЗаданий.Колонки.Добавить("СписокРегламентныхЗаданий");
	
	Для Каждого СтрокаОдновременноСессий Из ОдновременноСессий Цикл
		Если СтрокаОдновременноСессий.ОдновременноРегламентныхЗаданий >= РазмерОдновременноСессий
			И СтрокаОдновременноСессий.ОдновременноРегламентныхЗаданий >= 2 Тогда
			СтрокаИтогоОдновременноРегламентныхЗаданий = ИтогоОдновременноРегламентныхЗаданий.Добавить();
			СтрокаИтогоОдновременноРегламентныхЗаданий.ДатаОдновременноРегламентныхЗаданий = 
				СтрокаОдновременноСессий.ДатаОдновременноРегламентныхЗаданий;
			СтрокаИтогоОдновременноРегламентныхЗаданий.ОдновременноРегламентныхЗаданий = 
				СтрокаОдновременноСессий.ОдновременноРегламентныхЗаданий;
			СтрокаИтогоОдновременноРегламентныхЗаданий.СписокРегламентныхЗаданий = 
				СтрокаОдновременноСессий.СписокРегламентныхЗаданий;
		КонецЕсли;
	КонецЦикла;
	
	ДанныеЖурналаРегистрации.Сортировать("Метаданные, Данные, Дата, Сеанс");
	
	// Заполняем параметры, необходимые для получения данных по каждому сеансу регламентных заданий.
	ПараметрыСеансыРегламентныхЗаданий = Новый Структура;
	ПараметрыСеансыРегламентныхЗаданий.Вставить("ДанныеЖурналаРегистрации", ДанныеЖурналаРегистрации);
	ПараметрыСеансыРегламентныхЗаданий.Вставить("СоответствиеНаименованиеИдентификатор", СоответствиеНаименованиеИдентификатор);
	ПараметрыСеансыРегламентныхЗаданий.Вставить("СоответствиеМетаданныеИдентификатор", СоответствиеМетаданныеИдентификатор);
	ПараметрыСеансыРегламентныхЗаданий.Вставить("СоответствиеМетаданныеНазвание", СоответствиеМетаданныеНазвание);
	ПараметрыСеансыРегламентныхЗаданий.Вставить("ОтображатьФоновыеЗадания", ОтображатьФоновыеЗадания);
	ПараметрыСеансыРегламентныхЗаданий.Вставить("СкрытьРегламентныеЗадания", СкрытьРегламентныеЗадания);
	
	// Регламентные задания
	ТаблицаСеансыРегламентныхЗаданий = 
		СеансыРегламентныхЗаданий(ПараметрыСеансыРегламентныхЗаданий).ТаблицаСеансыРегламентныхЗаданий;
	КоличествоЗапусков = СеансыРегламентныхЗаданий(ПараметрыСеансыРегламентныхЗаданий).КоличествоЗапусков;
	
	Результат = Новый Структура;
	Результат.Вставить("ТаблицаСеансыРегламентныхЗаданий", ТаблицаСеансыРегламентныхЗаданий);
	Результат.Вставить("ИтогоОдновременноРегламентныхЗаданий", ИтогоОдновременноРегламентныхЗаданий);
	Результат.Вставить("КоличествоЗапусков", КоличествоЗапусков);
	Результат.Вставить("ОтчетПустой", ОтчетПустой);
	
	Возврат Результат;
КонецФункции

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

Функция СеансыРегламентныхЗаданий(ПараметрыСеансыРегламентныхЗаданий)

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

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

// Параметры:
//   Расшифровка - Массив - расшифровка регламентного задания.
//
Функция РасшифровкаРегламентногоЗадания(Расшифровка) Экспорт
	Результат = Новый Структура;
	Отчет = Новый ТабличныйДокумент;
	ОтмененоЗаданий = 0;
	ОшибкаВыполнения = 0;
	
	ДатаЗапускаЗадания = Расшифровка.Получить(5);
	ДатаОкончанияЗадания = Расшифровка.Получить(6);
	СписокСеансов = Расшифровка.Получить(7);
	Макет = ПолучитьМакет("РасшифровкаРегламентныхЗаданий");
	
	Область = ОписаниеОбластиМакета(Макет, "Заголовок");
	ДатаНачала = Расшифровка.Получить(11);
	ДатаОкончания = Расшифровка.Получить(12);
	Область.Параметры.ДатаНачала = ДатаНачала;
	Область.Параметры.ДатаОкончания = ДатаОкончания;
	Если Расшифровка.Получить(8) = 0 Тогда
		РежимОтображенияИнтервалов = НСтр("ru = 'Включено отображение интервалов с нулевой продолжительностью'");
	Иначе
		РежимОтображенияИнтервалов = НСтр("ru = 'Отключено отображение интервалов с нулевой продолжительностью'");
	КонецЕсли;
	Область.Параметры.РежимОтображенияСеансов = РежимОтображенияИнтервалов;
	Отчет.Вывести(Область);
	
	Отчет.Вывести(Макет.ПолучитьОбласть("ПустаяСтрока"));
	
	Область = ОписаниеОбластиМакета(Макет, "Таблица");
	Область.Параметры.ТипЗадания = НСтр("ru = 'Регламентное'");
	Область.Параметры.НазваниеСобытия = Расшифровка.Получить(2);
	Область.Параметры.Запусков = Расшифровка.Получить(9);
	ОтмененоЗаданий = Расшифровка.Получить(3);
	ОшибкаВыполнения = Расшифровка.Получить(4);
	Если ОтмененоЗаданий = 0 Тогда
		Область.Параметры.Отменено = "0";
	Иначе
		Область.Параметры.Отменено = ОтмененоЗаданий;
	КонецЕсли;
	Если ОшибкаВыполнения = 0 Тогда 
		Область.Параметры.ОшибкаВыполнения = "0";
	Иначе
		Область.Параметры.ОшибкаВыполнения = ОшибкаВыполнения;
	КонецЕсли;
	ОбщаяПродолжительностьРЗ = Расшифровка.Получить(10);
	ОбщаяПродолжительностьРЗИтог = ПродолжительностьРегламентногоЗадания(ОбщаяПродолжительностьРЗ);
	Область.Параметры.ОбщаяПродолжительностьРЗ = ОбщаяПродолжительностьРЗИтог;
	Отчет.Вывести(Область);
	
	Отчет.Вывести(Макет.ПолучитьОбласть("ПустаяСтрока")); 
	
	Отчет.Вывести(Макет.ПолучитьОбласть("ЗаголовокИнтервалов"));
		
	Отчет.Вывести(Макет.ПолучитьОбласть("ПустаяСтрока"));
	
	Отчет.Вывести(Макет.ПолучитьОбласть("ШапкаТаблицы"));
	
	// Заполняем таблицу интервалов.
	РазмерМассива = ДатаЗапускаЗадания.Количество();
	НомерИнтервала = 1; 	
    Отчет.НачатьГруппуСтрок(, Ложь);
	Для Индекс = 0 По РазмерМассива-1 Цикл
		Область = ОписаниеОбластиМакета(Макет, "ТаблицаИнтервалов");
		НачалоИнтервала = ДатаЗапускаЗадания.Получить(Индекс);
		КонецИнтервала = ДатаОкончанияЗадания.Получить(Индекс);
		ПродолжительностьРЗ = ПродолжительностьРегламентногоЗадания(КонецИнтервала - НачалоИнтервала);
		Область.Параметры.НомерИнтервала = НомерИнтервала;
		Область.Параметры.НачалоИнтервала = Формат(НачалоИнтервала, "ДЛФ=T");
		Область.Параметры.КонецИнтервала = Формат(КонецИнтервала, "ДЛФ=T");
		Область.Параметры.ПродолжительностьРЗ = ПродолжительностьРЗ;
		Область.Параметры.Сеанс = СписокСеансов.Получить(Индекс);
		Область.Параметры.РасшифровкаИнтервала = Новый Массив;
		Область.Параметры.РасшифровкаИнтервала.Добавить(НачалоИнтервала);
		Область.Параметры.РасшифровкаИнтервала.Добавить(КонецИнтервала);
		Область.Параметры.РасшифровкаИнтервала.Добавить(СписокСеансов.Получить(Индекс));
		Отчет.Вывести(Область);
		НомерИнтервала = НомерИнтервала + 1;
	КонецЦикла;
	Отчет.ЗакончитьГруппуСтрок();
	
	Результат.Вставить("Отчет", Отчет);
	Возврат Результат;
КонецФункции

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

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

Функция ПродолжительностьРегламентногоЗадания(ПродолжительностьРЗ)
	Если ПродолжительностьРЗ = 0 Тогда
		ОбщаяПродолжительностьРЗ = "0";
	ИначеЕсли ПродолжительностьРЗ <= 60 Тогда
		ОбщаяПродолжительностьРЗ = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = '%1 сек'"), ПродолжительностьРЗ);
	ИначеЕсли 60 < ПродолжительностьРЗ <= 3600 Тогда
		ПродолжительностьМинуты  = Формат(ПродолжительностьРЗ/60, "ЧДЦ=0");
		ПродолжительностьСекунды = Формат((Формат(ПродолжительностьРЗ/60, "ЧДЦ=2")
			- Цел(ПродолжительностьРЗ/60)) * 60, "ЧДЦ=0");
		ОбщаяПродолжительностьРЗ = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = '%1 мин %2 сек'"), ПродолжительностьМинуты, ПродолжительностьСекунды);
	ИначеЕсли ПродолжительностьРЗ > 3600 Тогда
		ПродолжительностьЧасы    = Формат(ПродолжительностьРЗ/60/60, "ЧДЦ=0");
		ПродолжительностьМинуты  = (Формат(ПродолжительностьРЗ/60/60, "ЧДЦ=2") - Цел(ПродолжительностьРЗ/60/60))*60;
		ПродолжительностьМинуты  = Формат(ПродолжительностьМинуты, "ЧДЦ=0");
		ОбщаяПродолжительностьРЗ = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = '%1 ч %2 мин'"), ПродолжительностьЧасы, ПродолжительностьМинуты);
	КонецЕсли;
	
	Возврат ОбщаяПродолжительностьРЗ;
КонецФункции

Функция МетаданныеРегламентногоЗадания(ДанныеРегламентногоЗадания)
	Если ДанныеРегламентногоЗадания <> "" Тогда
		Возврат Метаданные.РегламентныеЗадания.Найти(
			СтрЗаменить(ДанныеРегламентногоЗадания, "РегламентноеЗадание." , ""));
	КонецЕсли;
КонецФункции

Функция НазваниеИУникальныйИдентификаторСеансаРегламентногоЗадания(СтрокаДанныхЖурналаРегистрации,
			СоответствиеНаименованиеИдентификатор, СоответствиеМетаданныеИдентификатор, СоответствиеМетаданныеНазвание)
	Если Не СтрокаДанныхЖурналаРегистрации.Данные = "" Тогда
		УникальныйИдентификаторРегламентногоЗадания = СоответствиеНаименованиеИдентификатор[
														СтрокаДанныхЖурналаРегистрации.Данные];
		НазваниеСеанса = СтрокаДанныхЖурналаРегистрации.Данные;
	Иначе 
		УникальныйИдентификаторРегламентногоЗадания = СоответствиеМетаданныеИдентификатор[
			МетаданныеРегламентногоЗадания(СтрокаДанныхЖурналаРегистрации.Метаданные)];
		НазваниеСеанса = СоответствиеМетаданныеНазвание[МетаданныеРегламентногоЗадания(
														СтрокаДанныхЖурналаРегистрации.Метаданные)];
	КонецЕсли;
													
	Возврат Новый Структура("НазваниеСеанса, УникальныйИдентификаторРегламентногоЗадания",
								НазваниеСеанса, УникальныйИдентификаторРегламентногоЗадания)
КонецФункции

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

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

#Область КонтрольЖурналаРегистрации

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

// Получить представление физического места размещения информационной базы для отображения администратору.
//
// Возвращаемое значение:
//   Строка - представление информационной базы.
//
// Пример:
// - для ИБ в файлом режиме: \\FileServer\1c_ib\
// - для ИБ в серверном режиме: ServerName:1111 / information_base_name.
//
Функция ПредставлениеИнформационнойБазы()
	
	СтрокаСоединенияСБД = СтрокаСоединенияИнформационнойБазы();
	
	Если ОбщегоНазначения.ИнформационнаяБазаФайловая(СтрокаСоединенияСБД) Тогда
		Возврат Сред(СтрокаСоединенияСБД, 6, СтрДлина(СтрокаСоединенияСБД) - 6);
	КонецЕсли;
		
	// Прибавить к имени сервера имя пути информационной базы.
	ПозицияПоиска = СтрНайти(ВРег(СтрокаСоединенияСБД), "SRVR=");
	Если ПозицияПоиска <> 1 Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	ПозицияТочкиСЗапятой = СтрНайти(СтрокаСоединенияСБД, ";");
	НачальнаяПозицияКопирования = 6 + 1;
	КонечнаяПозицияКопирования = ПозицияТочкиСЗапятой - 2; 
	
	ИмяСервера = Сред(СтрокаСоединенияСБД, НачальнаяПозицияКопирования, КонечнаяПозицияКопирования - НачальнаяПозицияКопирования + 1);
	
	СтрокаСоединенияСБД = Сред(СтрокаСоединенияСБД, ПозицияТочкиСЗапятой + 1);
	
	// Позиция имени сервера
	ПозицияПоиска = СтрНайти(ВРег(СтрокаСоединенияСБД), "REF=");
	Если ПозицияПоиска <> 1 Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	НачальнаяПозицияКопирования = 6;
	ПозицияТочкиСЗапятой = СтрНайти(СтрокаСоединенияСБД, ";");
	КонечнаяПозицияКопирования = ПозицияТочкиСЗапятой - 2; 
	
	ИмяИБНаСервере = Сред(СтрокаСоединенияСБД, НачальнаяПозицияКопирования, КонечнаяПозицияКопирования - НачальнаяПозицияКопирования + 1);
	ПутьКБД = ИмяСервера + "/ " + ИмяИБНаСервере;
	Возврат ПутьКБД;
	
КонецФункции

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

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

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

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

#КонецЕсли

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