Производственные календари / Модуль менеджера

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

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

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

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

// ТехнологияСервиса.ВыгрузкаЗагрузкаДанных

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

// Конец ТехнологияСервиса.ВыгрузкаЗагрузкаДанных

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

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

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

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

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

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

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

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

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

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

// Обновляет справочник Производственные календари из XML файла.
//
// Параметры:
//  ТаблицаКалендарей	 - ТаблицаЗначений	 - таблица с описанием производственных календарей.
//
Процедура ОбновитьПроизводственныеКалендари(ТаблицаКалендарей) Экспорт
	
	Если ТаблицаКалендарей.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("КлассификаторТаблица", ТаблицаКалендарей);
	Запрос.Текст = 
		"ВЫБРАТЬ
		|	ВЫРАЗИТЬ(КлассификаторТаблица.Code КАК СТРОКА(2)) КАК Код,
		|	ВЫРАЗИТЬ(КлассификаторТаблица.Base КАК СТРОКА(2)) КАК КодБазового,
		|	ВЫРАЗИТЬ(КлассификаторТаблица.Description КАК СТРОКА(100)) КАК Наименование
		|ПОМЕСТИТЬ КлассификаторТаблица
		|ИЗ
		|	&КлассификаторТаблица КАК КлассификаторТаблица
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ
		|	КлассификаторТаблица.Код КАК Код,
		|	КлассификаторТаблица.КодБазового КАК КодБазового,
		|	КлассификаторТаблица.Наименование КАК Наименование,
		|	ПроизводственныеКалендари.Ссылка КАК Ссылка,
		|	ЕСТЬNULL(ПроизводственныеКалендари.Код, """") КАК ПроизводственныйКалендарьКод,
		|	ЕСТЬNULL(ПроизводственныеКалендари.Наименование, """") КАК ПроизводственныйКалендарьНаименование,
		|	ЕСТЬNULL(ПроизводственныеКалендари.БазовыйКалендарь.Код, """") КАК ПроизводственныйКалендарьКодБазового
		|ИЗ
		|	КлассификаторТаблица КАК КлассификаторТаблица
		|		ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ПроизводственныеКалендари КАК ПроизводственныеКалендари
		|		ПО КлассификаторТаблица.Код = ПроизводственныеКалендари.Код
		|
		|УПОРЯДОЧИТЬ ПО
		|	КодБазового";
	РезультатЗапроса = Запрос.Выполнить();
	Если РезультатЗапроса.Пустой() Тогда
		Возврат;
	КонецЕсли;
	
	Выборка = РезультатЗапроса.Выбрать();
	Пока Выборка.Следующий() Цикл
		Если СокрЛП(Выборка.Код) = СокрЛП(Выборка.ПроизводственныйКалендарьКод)
			И СокрЛП(Выборка.Наименование) = СокрЛП(Выборка.ПроизводственныйКалендарьНаименование) 
			И СокрЛП(Выборка.КодБазового) = СокрЛП(Выборка.ПроизводственныйКалендарьКодБазового) Тогда
			Продолжить;
		КонецЕсли;
		Если Не ЗначениеЗаполнено(Выборка.Ссылка) Тогда
			Если Не ОбщегоНазначения.РазделениеВключено() И ЗначениеЗаполнено(Выборка.КодБазового) Тогда
				// Зависимые календари не создаем автоматически при обновлении в локальном режиме.
				Продолжить;
			КонецЕсли;
		КонецЕсли;
		НачатьТранзакцию();
		Попытка
			Если Не ЗначениеЗаполнено(Выборка.Ссылка) Тогда
				СправочникОбъект = СоздатьЭлемент();
			Иначе
				БлокировкаДанных = Новый БлокировкаДанных;
				ЭлементБлокировки = БлокировкаДанных.Добавить("Справочник.ПроизводственныеКалендари");
				ЭлементБлокировки.УстановитьЗначение("Ссылка", Выборка.Ссылка);
				БлокировкаДанных.Заблокировать();
				СправочникОбъект = Выборка.Ссылка.ПолучитьОбъект();
			КонецЕсли;
			ЗаполнитьПроизводственныйКалендарь(СправочникОбъект, Выборка);
			ЗаписатьПроизводственныйКалендарь(СправочникОбъект);
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			ВызватьИсключение;
		КонецПопытки;
	КонецЦикла;
	
КонецПроцедуры

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

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

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

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

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

// Преобразовывает данные производственных календарей, представленные в виде XML.
//
// Параметры:
//   ДанныеXML - Структура - извлеченная из файла XML методом ОбщегоНазначения.ПрочитатьXMLВТаблицу.
//   ТаблицаКалендарей - ТаблицаЗначений - список производственных календарей, поддерживаемых в конфигурации.
//   КодыКалендарей - Массив - если не задан, то отбор не будет установлен.
//   ФормироватьПолныйНабор - Булево - если ложь, то будут сформированы только данные по отклонениям от календаря по умолчанию.
//
// Возвращаемое значение:
//  ТаблицаЗначений:
//   * КодПроизводственногоКалендаря - Строка
//   * ВидДня - ПеречислениеСсылка.ВидыДнейПроизводственногоКалендаря
//   * Год - Число
//   * Дата - Дата
//   * ДатаПереноса - Дата
//
Функция ДанныеПроизводственныхКалендарейИзXML(Знач ДанныеXML, ТаблицаКалендарей, КодыКалендарей = Неопределено, ФормироватьПолныйНабор = Истина) Экспорт
	
	ТаблицаДанных = НовыеДанныеПроизводственныхКалендарей();
	
	КлассификаторТаблица = ДанныеXML.Данные;
	Если КлассификаторТаблица.Количество() = 0 Тогда
		Возврат ТаблицаДанных;
	КонецЕсли;
	
	ГодыКалендарей = КлассификаторТаблица.Скопировать(, "Calendar,Year");
	ГодыКалендарей.Свернуть("Calendar,Year");
	Если ФормироватьПолныйНабор Тогда
		ДополнитьКалендариГодыПоТаблицеКалендарей(ГодыКалендарей, ТаблицаКалендарей);
	КонецЕсли;
	
	ОтборСтрок = Новый Структура("Calendar,Year");
	Для Каждого Сочетание Из ГодыКалендарей Цикл
		Если КодыКалендарей <> Неопределено И КодыКалендарей.Найти(Сочетание.Calendar) = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		ДатыГода = Новый Соответствие;
		ЗаполнитьЗначенияСвойств(ОтборСтрок, Сочетание);
		СтрокиДанныхКалендаря = КлассификаторТаблица.НайтиСтроки(ОтборСтрок);
		Для Каждого СтрокаКлассификатора Из СтрокиДанныхКалендаря Цикл
			НоваяСтрока = ТаблицаДанных.Добавить();
			НоваяСтрока.КодПроизводственногоКалендаря = СтрокаКлассификатора.Calendar;
			НоваяСтрока.ВидДня = Перечисления.ВидыДнейПроизводственногоКалендаря[СтрокаКлассификатора.DayType];
			НоваяСтрока.Год = Число(СтрокаКлассификатора.Year);
			НоваяСтрока.Дата = Дата(СтрокаКлассификатора.Date);
			Если ЗначениеЗаполнено(СтрокаКлассификатора.SwapDate) Тогда
				НоваяСтрока.ДатаПереноса = Дата(СтрокаКлассификатора.SwapDate);
			КонецЕсли;
			
			ДатыГода.Вставить(НоваяСтрока.Дата, Истина);
		КонецЦикла;
		КодБазовогоКалендаря = КодБазовогоКалендаря(Сочетание.Calendar, ТаблицаКалендарей);
		Если КодБазовогоКалендаря <> Неопределено Тогда
			ОтборСтрок.Calendar = КодБазовогоКалендаря;
			СтрокиДанныхКалендаря = КлассификаторТаблица.НайтиСтроки(ОтборСтрок);
			Для Каждого СтрокаКлассификатора Из СтрокиДанныхКалендаря Цикл
				СтрокаКлассификатора.Calendar = Сочетание.Calendar;
				
				ПараметрыОтбора = Новый Структура("КодПроизводственногоКалендаря,Дата");
				ПараметрыОтбора.КодПроизводственногоКалендаря = СтрокаКлассификатора.Calendar;
				ПараметрыОтбора.Дата = Дата(СтрокаКлассификатора.Date);
				НайденныеСтроки = ТаблицаДанных.НайтиСтроки(ПараметрыОтбора);
				Если НайденныеСтроки.Количество() > 0 Тогда
					СтрокаКлассификатора.Calendar = КодБазовогоКалендаря;
					Если НоваяСтрока <> Неопределено Тогда
						ДатыГода.Вставить(НоваяСтрока.Дата, Истина);
					КонецЕсли;
					Продолжить;
				КонецЕсли;
				
				НоваяСтрока = ТаблицаДанных.Добавить();
				НоваяСтрока.КодПроизводственногоКалендаря = СтрокаКлассификатора.Calendar;
				НоваяСтрока.ВидДня = Перечисления.ВидыДнейПроизводственногоКалендаря[СтрокаКлассификатора.DayType];
				НоваяСтрока.Год = Число(СтрокаКлассификатора.Year);
				НоваяСтрока.Дата = Дата(СтрокаКлассификатора.Date);
				Если ЗначениеЗаполнено(СтрокаКлассификатора.SwapDate) Тогда
					НоваяСтрока.ДатаПереноса = Дата(СтрокаКлассификатора.SwapDate);
				КонецЕсли;
				
				СтрокаКлассификатора.Calendar = КодБазовогоКалендаря;
				Если НоваяСтрока <> Неопределено Тогда
					ДатыГода.Вставить(НоваяСтрока.Дата, Истина);
				КонецЕсли;
			КонецЦикла;
		КонецЕсли;
		Если Не ФормироватьПолныйНабор Тогда
			Продолжить;
		КонецЕсли;
		НомерГода = Число(Сочетание.Year);
		ДатаДня = Дата(НомерГода, 1, 1);
		Пока ДатаДня <= Дата(НомерГода, 12, 31) Цикл
			Если ДатыГода[ДатаДня] = Неопределено Тогда
				НоваяСтрока = ТаблицаДанных.Добавить();
				НоваяСтрока.КодПроизводственногоКалендаря = Сочетание.Calendar;
				НоваяСтрока.Год = НомерГода;
				НоваяСтрока.Дата = ДатаДня;
				НоваяСтрока.ВидДня = ВидДняПоДате(ДатаДня);
			КонецЕсли;
			ДатаДня = ДатаДня + ДлинаСуток();
		КонецЦикла;
	КонецЦикла;
	
	Возврат ТаблицаДанных;
	
КонецФункции

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

Процедура ДополнитьКалендариГодыПоТаблицеКалендарей(КалендариГоды, ТаблицаКалендарей)

	КалендариГоды.Сортировать("Calendar, Year");
	Для Каждого СтрокаКалендаря Из ТаблицаКалендарей Цикл
		ГодыКалендаря = КалендариГоды.НайтиСтроки(Новый Структура("Calendar", СтрокаКалендаря.Code));
		ГодыБазовогоКалендаря = КалендариГоды.НайтиСтроки(Новый Структура("Calendar", СтрокаКалендаря.Base));
		Если ГодыКалендаря.Количество() = 0 Тогда
			// В макете вообще нет данных этого календаря - посмотрим базовый календарь.
			Если ГодыБазовогоКалендаря.Количество() = 0 Тогда
				// Для базового календаря тоже нет данных - добавим только на текущий год.
				ДобавитьГодКалендаря(КалендариГоды, СтрокаКалендаря.Code, Год(ТекущаяДатаСеанса()));
				Продолжить;
			КонецЕсли;
			Для Каждого СтрокаБазового Из ГодыБазовогоКалендаря Цикл
				ДобавитьГодКалендаря(КалендариГоды, СтрокаКалендаря.Code, СтрокаБазового.Year);
			КонецЦикла;
			Продолжить;
		КонецЕсли;
		// Заполняем за все годы базового календаря, но не ранее минимального года из макета для этого календаря.
		МинимальныйГод = ГодыКалендаря[0].Year;
		Годы = ОбщегоНазначения.ВыгрузитьКолонку(ГодыКалендаря, "Year");
		Для Каждого СтрокаБазового Из ГодыБазовогоКалендаря Цикл
			Если СтрокаБазового.Year >= МинимальныйГод И Годы.Найти(СтрокаБазового.Year) = Неопределено Тогда
				ДобавитьГодКалендаря(КалендариГоды, СтрокаКалендаря.Code, СтрокаБазового.Year);
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;

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

Процедура ДобавитьГодКалендаря(КалендариГоды, КодКалендаря, Год)
	НоваяСтрока = КалендариГоды.Добавить();
	НоваяСтрока.Calendar = КодКалендаря;
	НоваяСтрока.Year = Формат(Год, "ЧГ=");
КонецПроцедуры

Процедура ДобавитьНерабочийПериодВТаблицуИзМакета(СтрокаТаблицы, СтрокаМакета)
	СтрокаТаблицы.КодПроизводственногоКалендаря = СтрокаМакета.Calendar;
	СтрокаТаблицы.НомерПериода = Число(СтрокаМакета.Order);
	СтрокаТаблицы.ДатаНачала = Дата(СтрокаМакета.StartDate);
	СтрокаТаблицы.ДатаОкончания = Дата(СтрокаМакета.EndDate);
	СтрокаТаблицы.Основание = СтрокаМакета.Description;
КонецПроцедуры

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

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

Процедура ЗаполнитьПроизводственныеКалендариПоУмолчаниюДлительнаяОперация(Параметры, АдресРезультата) Экспорт
	
	Календари = ПроизводственныеКалендариПоУмолчанию();
	ПоместитьВоВременноеХранилище(Календари, АдресРезультата);
	
КонецПроцедуры

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

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

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

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

Функция ПроизводственныеКалендариИзФайлаКлассификатора()
	
	ДанныеКлассификатора = КалендарныеГрафики.ДанныеКлассификатора();
	
	ТаблицаКалендарей = ДанныеКлассификатора["ПроизводственныеКалендари"].Данные;
	
	Возврат ТаблицаКалендарей;

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

Функция ДанныеПроизводственныхКалендарейИзФайлаКлассификатора(КодыКалендарей = Неопределено, ФормироватьПолныйНабор = Истина)
	
	ДанныеКлассификатора = КалендарныеГрафики.ДанныеКлассификатора();
	
	Возврат ДанныеПроизводственныхКалендарейИзXML(
		ДанныеКлассификатора["ДанныеПроизводственныхКалендарей"], 
		ДанныеКлассификатора["ПроизводственныеКалендари"].Данные,
		КодыКалендарей, 
		ФормироватьПолныйНабор);
	
КонецФункции

Функция ПериодыНерабочихДнейПоУмолчанию() Экспорт
	
	Если КалендарныеГрафики.ВерсияКалендарей() >= КалендарныеГрафики.ВерсияЗагруженныхКалендарей() Тогда
		Возврат ПериодыНерабочихДнейИзМакета();
	КонецЕсли;
	
	Возврат ПериодыНерабочихДнейИзФайлаКлассификатора();
	
КонецФункции

Функция ПериодыНерабочихДнейИзФайлаКлассификатора()
	
	ДанныеКлассификатора = КалендарныеГрафики.ДанныеКлассификатора();
	
	Возврат ПериодыНерабочихДнейИзXML(
		ДанныеКлассификатора["ПериодыНерабочихДней"], 
		ДанныеКлассификатора["ПроизводственныеКалендари"].Данные);
	
КонецФункции

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

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

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

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

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

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

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

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

	Возврат УсловияОбновления;
	
КонецФункции

Функция ДлинаСуток()
	Возврат 24 * 3600;
КонецФункции

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

// Возвращаемое значение:
//  ТаблицаЗначений:
//   * КодПроизводственногоКалендаря - Строка
//   * НомерПериода - Число
//   * ДатаНачала - Дата
//   * ДатаОкончания - Дата
//   * Основание - Строка
//
Функция НоваяТаблицаПериодовНерабочихДней()

	ТаблицаДанных = Новый ТаблицаЗначений;
	ТаблицаДанных.Колонки.Добавить("КодПроизводственногоКалендаря", Новый ОписаниеТипов("Строка", ,
		Новый КвалификаторыСтроки(2)));
	ТаблицаДанных.Колонки.Добавить("НомерПериода", Новый ОписаниеТипов("Число"));
	ТаблицаДанных.Колонки.Добавить("ДатаНачала", Новый ОписаниеТипов("Дата"));
	ТаблицаДанных.Колонки.Добавить("ДатаОкончания", Новый ОписаниеТипов("Дата"));
	ТаблицаДанных.Колонки.Добавить("Основание", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(150)));
	Возврат ТаблицаДанных;
	
КонецФункции

Процедура ЗаполнитьПостоянныеПраздничныеДни(ВидыДней, ПереносыДней, НомерГода, КодКалендаря, КодБазовогоКалендаря = Неопределено)
	
	// Если нет - заполняем праздники и переносы.
	ПраздничныеДни = ПраздничныеДниПроизводственногоКалендаря(КодКалендаря, НомерГода);
	// Дополним таблицу также праздниками следующего года, 
	// т.к. они влияют на заполнение текущего года (31 декабря - предпраздничный, например).
	ПраздничныеДниСледующегоГода = ПраздничныеДниПроизводственногоКалендаря(КодКалендаря, НомерГода + 1);
	ОбщегоНазначенияКлиентСервер.ДополнитьТаблицу(ПраздничныеДниСледующегоГода, ПраздничныеДни);
	
	Если КодБазовогоКалендаря <> Неопределено Тогда
		// Дополним таблицу также праздниками базового календаря.
		ПраздничныеБазовогоКалендаря = ПраздничныеДниПроизводственногоКалендаря(КодБазовогоКалендаря, НомерГода);
		ОбщегоНазначенияКлиентСервер.ДополнитьТаблицу(ПраздничныеБазовогоКалендаря, ПраздничныеДни);
		ПраздничныеДниСледующегоГода = ПраздничныеДниПроизводственногоКалендаря(КодБазовогоКалендаря, НомерГода + 1);
		ОбщегоНазначенияКлиентСервер.ДополнитьТаблицу(ПраздничныеДниСледующегоГода, ПраздничныеДни);
	КонецЕсли;
	
	// При совпадении выходного и нерабочего праздничного дней 
	// выходной день переносится на следующий после праздничного рабочий день 
	// за исключением выходных дней, совпадающих с нерабочими праздничными днями 
	// в период Новогодних каникул и Рождества Христова.	
	
	Для Каждого СтрокаТаблицы Из ПраздничныеДни Цикл
		ПраздничныйДень = СтрокаТаблицы.Дата;
		// Отметим как предпраздничный день, 
		// рабочий день непосредственно предшествующий праздничному дню.
		Если СтрокаТаблицы.ДобавлятьПредпраздничный Тогда
			ДатаПредпраздничногоДня = ПраздничныйДень - ДлинаСуток();
			Если Год(ДатаПредпраздничногоДня) = НомерГода Тогда
				// Предпраздничные дни другого года пропускаем.
				Если ВидыДней[ДатаПредпраздничногоДня] = Перечисления.ВидыДнейПроизводственногоКалендаря.Рабочий 
					И ПраздничныеДни.Найти(ДатаПредпраздничногоДня, "Дата") = Неопределено Тогда
					ВидыДней.Вставить(ДатаПредпраздничногоДня, Перечисления.ВидыДнейПроизводственногоКалендаря.Предпраздничный);
				КонецЕсли;
			КонецЕсли;
		КонецЕсли;
		Если Год(ПраздничныйДень) <> НомерГода Тогда
			// Праздничные дни другого года далее также пропускаем.
			Продолжить;
		КонецЕсли;
		Если ВидыДней[ПраздничныйДень] <> Перечисления.ВидыДнейПроизводственногоКалендаря.Рабочий 
			И СтрокаТаблицы.ПереноситьВыходной Тогда
			// Если праздничный день выпадает на выходной, 
			// и выходной, на который выпадает этот праздник, переносится - 
			// переносим выходной на ближайший рабочий день.
			ДатаДня = ПраздничныйДень;
			Пока Истина Цикл
				ДатаДня = ДатаДня + ДлинаСуток();
				Если ВидыДней[ДатаДня] = Перечисления.ВидыДнейПроизводственногоКалендаря.Рабочий 
					И ПраздничныеДни.Найти(ДатаДня, "Дата") = Неопределено Тогда
					ВидыДней.Вставить(ДатаДня, ВидыДней[ПраздничныйДень]);
					ПереносыДней.Вставить(ДатаДня, ПраздничныйДень);
					ПереносыДней.Вставить(ПраздничныйДень, ДатаДня);
					Прервать;
				КонецЕсли;
			КонецЦикла;
		КонецЕсли;
		Если СтрокаТаблицы.ТолькоНерабочий Тогда
			ВидыДней.Вставить(ПраздничныйДень, Перечисления.ВидыДнейПроизводственногоКалендаря.Нерабочий);
		Иначе
			ВидыДней.Вставить(ПраздничныйДень, Перечисления.ВидыДнейПроизводственногоКалендаря.Праздник);
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

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

Функция ВидДняПоДате(Дата)
	
	НомерДняНедели = ДеньНедели(Дата);
	
	Если НомерДняНедели <= 5 Тогда
		Возврат Перечисления.ВидыДнейПроизводственногоКалендаря.Рабочий;
	КонецЕсли;
	
	Если НомерДняНедели = 6 Тогда
		Возврат Перечисления.ВидыДнейПроизводственногоКалендаря.Суббота;
	КонецЕсли;
	
	Если НомерДняНедели = 7 Тогда
		Возврат Перечисления.ВидыДнейПроизводственногоКалендаря.Воскресенье;
	КонецЕсли;
	
КонецФункции

Функция КодБазовогоКалендаря(КодКалендаря, КлассификаторКалендарей)
	
	СтрокаКалендаря = КлассификаторКалендарей.Найти(КодКалендаря, "Code");
	
	Если СтрокаКалендаря = Неопределено Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Если Не ЗначениеЗаполнено(СтрокаКалендаря["Base"]) Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Возврат СтрокаКалендаря["Base"];
	
КонецФункции

Функция КодыЗависимыхКалендарей(КодБазовогоКалендаря, КлассификаторКалендарей)
	Возврат ОбщегоНазначения.ВыгрузитьКолонку(
		КлассификаторКалендарей.НайтиСтроки(Новый Структура("Base", КодБазовогоКалендаря)), "Code");
КонецФункции

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

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

#Область ПечатнаяФормаПроизводственногоКалендаря

Функция ПечатнаяФормаПроизводственногоКалендаря(ПараметрыПодготовкиПечатнойФормы)
	
	ТабличныйДокумент = Новый ТабличныйДокумент;
	
	Макет = ПолучитьМакет("ПФ_MXL_ПроизводственныйКалендарь");
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.Печать") Тогда
		МодульУправлениеПечатью = ОбщегоНазначения.ОбщийМодуль("УправлениеПечатью");
		Макет = МодульУправлениеПечатью.МакетПечатнойФормы("Справочник.ПроизводственныеКалендари.ПФ_MXL_ПроизводственныйКалендарь");
	КонецЕсли;
	
	ПроизводственныйКалендарь = ПараметрыПодготовкиПечатнойФормы.ПроизводственныйКалендарь;
	НомерГода = ПараметрыПодготовкиПечатнойФормы.НомерГода;
	
	ЗаголовокПечати = Макет.ПолучитьОбласть("Заголовок");
	ЗаголовокПечати.Параметры.ПроизводственныйКалендарь = ПроизводственныйКалендарь;
	ЗаголовокПечати.Параметры.Год = Формат(НомерГода, "ЧГ=");
	ТабличныйДокумент.Вывести(ЗаголовокПечати);
	
	// Начальные значения, независимо от результата выполнения запроса.
	ЗаГод = ОписаниеГруппыПоказателей();
	
	НерабочиеПериоды = КалендарныеГрафики.ПериодыНерабочихДней(
		ПроизводственныйКалендарь, Новый СтандартныйПериод(Дата(НомерГода, 1, 1), Дата(НомерГода, 12, 31)));
	НерабочиеДаты = Новый Массив;
	Представление = "";
	Для Каждого НерабочийПериод Из НерабочиеПериоды Цикл
		ОбщегоНазначенияКлиентСервер.ДополнитьМассив(НерабочиеДаты, НерабочийПериод.Даты);
		Представление = Представление + ?(ПустаяСтрока(Представление), "", Символы.ПС) + НерабочийПериод.Представление;
	КонецЦикла;
	Если НерабочиеПериоды.Количество() > 0 Тогда
		ОбластьПредупреждения = Макет.ПолучитьОбласть("НерабочиеПериоды");
		ОбластьПредупреждения.Параметры.Представление = Представление;
		ТабличныйДокумент.Вывести(ОбластьПредупреждения);
	КонецЕсли;
	
	ВидыНерабочихДней = Новый Массив;
	ВидыНерабочихДней.Добавить(Перечисления.ВидыДнейПроизводственногоКалендаря.Суббота);
	ВидыНерабочихДней.Добавить(Перечисления.ВидыДнейПроизводственногоКалендаря.Воскресенье);
	ВидыНерабочихДней.Добавить(Перечисления.ВидыДнейПроизводственногоКалендаря.Праздник);
	ВидыНерабочихДней.Добавить(Перечисления.ВидыДнейПроизводственногоКалендаря.Нерабочий);
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("Год", НомерГода);
	Запрос.УстановитьПараметр("ПроизводственныйКалендарь", ПроизводственныйКалендарь);
	Запрос.УстановитьПараметр("НерабочиеДаты", НерабочиеДаты);
	Запрос.Текст = 
		"ВЫБРАТЬ
		|	ГОД(ДанныеКалендаря.Дата) КАК ГодКалендаря,
		|	КВАРТАЛ(ДанныеКалендаря.Дата) КАК КварталКалендаря,
		|	МЕСЯЦ(ДанныеКалендаря.Дата) КАК МесяцКалендаря,
		|	КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ДанныеКалендаря.Дата) КАК КалендарныеДни,
		|	СУММА(ВЫБОР
		|			КОГДА ДанныеКалендаря.Дата В (&НерабочиеДаты)
		|				ТОГДА 1
		|			ИНАЧЕ 0
		|		КОНЕЦ) КАК ДниНерабочихПериодов,
		|	ДанныеКалендаря.ВидДня КАК ВидДня
		|ИЗ
		|	РегистрСведений.ДанныеПроизводственногоКалендаря КАК ДанныеКалендаря
		|ГДЕ
		|	ДанныеКалендаря.Год = &Год
		|	И ДанныеКалендаря.ПроизводственныйКалендарь = &ПроизводственныйКалендарь
		|
		|СГРУППИРОВАТЬ ПО
		|	ДанныеКалендаря.ВидДня,
		|	ГОД(ДанныеКалендаря.Дата),
		|	КВАРТАЛ(ДанныеКалендаря.Дата),
		|	МЕСЯЦ(ДанныеКалендаря.Дата)
		|
		|УПОРЯДОЧИТЬ ПО
		|	ГодКалендаря,
		|	КварталКалендаря,
		|	МесяцКалендаря
		|ИТОГИ ПО
		|	ГодКалендаря,
		|	КварталКалендаря,
		|	МесяцКалендаря";
	Результат = Запрос.Выполнить();
	
	ВыборкаПоГоду = Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
	Пока ВыборкаПоГоду.Следующий() Цикл
		ВыборкаПоКварталу = ВыборкаПоГоду.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
		Пока ВыборкаПоКварталу.Следующий() Цикл
			НомерКвартала = Макет.ПолучитьОбласть("Квартал");
			НомерКвартала.Параметры.НомерКвартала = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = '%1 квартал'"), ВыборкаПоКварталу.КварталКалендаря);
			ТабличныйДокумент.Вывести(НомерКвартала);
			ШапкаКвартала = Макет.ПолучитьОбласть("ШапкаКвартала");
			ТабличныйДокумент.Вывести(ШапкаКвартала);
			ЗаКвартал = ОписаниеГруппыПоказателей();
			Если ВыборкаПоКварталу.КварталКалендаря = 1 
				Или ВыборкаПоКварталу.КварталКалендаря = 3 Тогда
				ЗаПолугодие = ОписаниеГруппыПоказателей();
			КонецЕсли;
			Если ВыборкаПоКварталу.КварталКалендаря = 1 Тогда
				ЗаГод = ОписаниеГруппыПоказателей();
			КонецЕсли;
			ВыборкаПоМесяцу = ВыборкаПоКварталу.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
			Пока ВыборкаПоМесяцу.Следующий() Цикл
				ЗаМесяц = ОписаниеГруппыПоказателей();
				ВыборкаПоВидуДня = ВыборкаПоМесяцу.Выбрать(ОбходРезультатаЗапроса.Прямой);
				Пока ВыборкаПоВидуДня.Следующий() Цикл
					Если ВидыНерабочихДней.Найти(ВыборкаПоВидуДня.ВидДня) <> Неопределено Тогда
						НакопитьЗначение(ЗаМесяц.Основной.ВыходныеДни, ВыборкаПоВидуДня.КалендарныеДни);
						НакопитьЗначение(ЗаМесяц.Нерабочий.ВыходныеДни, ВыборкаПоВидуДня.ДниНерабочихПериодов);
					ИначеЕсли ВыборкаПоВидуДня.ВидДня = Перечисления.ВидыДнейПроизводственногоКалендаря.Рабочий Тогда 
						НакопитьЗначение(ЗаМесяц.Основной.РабочееВремя40, ВыборкаПоВидуДня.КалендарныеДни * 8);
						НакопитьЗначение(ЗаМесяц.Основной.РабочееВремя36, ВыборкаПоВидуДня.КалендарныеДни * 36 / 5);
						НакопитьЗначение(ЗаМесяц.Основной.РабочееВремя24, ВыборкаПоВидуДня.КалендарныеДни * 24 / 5);
						НакопитьЗначение(ЗаМесяц.Основной.РабочиеДни, ВыборкаПоВидуДня.КалендарныеДни);
						НакопитьЗначение(ЗаМесяц.Нерабочий.РабочееВремя40, ВыборкаПоВидуДня.ДниНерабочихПериодов * 8);
						НакопитьЗначение(ЗаМесяц.Нерабочий.РабочееВремя36, ВыборкаПоВидуДня.ДниНерабочихПериодов * 36 / 5);
						НакопитьЗначение(ЗаМесяц.Нерабочий.РабочееВремя24, ВыборкаПоВидуДня.ДниНерабочихПериодов * 24 / 5);
						НакопитьЗначение(ЗаМесяц.Нерабочий.РабочиеДни, ВыборкаПоВидуДня.ДниНерабочихПериодов);
					ИначеЕсли ВыборкаПоВидуДня.ВидДня = Перечисления.ВидыДнейПроизводственногоКалендаря.Предпраздничный Тогда
						НакопитьЗначение(ЗаМесяц.Основной.РабочееВремя40, ВыборкаПоВидуДня.КалендарныеДни * 7);
						НакопитьЗначение(ЗаМесяц.Основной.РабочееВремя36, ВыборкаПоВидуДня.КалендарныеДни * (36 / 5 - 1));
						НакопитьЗначение(ЗаМесяц.Основной.РабочееВремя24, ВыборкаПоВидуДня.КалендарныеДни * (24 / 5 - 1));
						НакопитьЗначение(ЗаМесяц.Основной.РабочиеДни, ВыборкаПоВидуДня.КалендарныеДни);
						НакопитьЗначение(ЗаМесяц.Нерабочий.РабочееВремя40, ВыборкаПоВидуДня.ДниНерабочихПериодов * 7);
						НакопитьЗначение(ЗаМесяц.Нерабочий.РабочееВремя36, ВыборкаПоВидуДня.ДниНерабочихПериодов * (36 / 5 - 1));
						НакопитьЗначение(ЗаМесяц.Нерабочий.РабочееВремя24, ВыборкаПоВидуДня.ДниНерабочихПериодов * (24 / 5 - 1));
						НакопитьЗначение(ЗаМесяц.Нерабочий.РабочиеДни, ВыборкаПоВидуДня.ДниНерабочихПериодов);
					КонецЕсли;
					НакопитьЗначение(ЗаМесяц.Основной.КалендарныеДни, ВыборкаПоВидуДня.КалендарныеДни);
					НакопитьЗначение(ЗаМесяц.Нерабочий.ВыходныеДни, - ВыборкаПоВидуДня.ДниНерабочихПериодов);
				КонецЦикла;
				НакопитьКолонку(ЗаКвартал, ЗаМесяц);
				НакопитьКолонку(ЗаПолугодие, ЗаМесяц);
				НакопитьКолонку(ЗаГод, ЗаМесяц);
				КолонкаМесяца = Макет.ПолучитьОбласть("КолонкаМесяца");
				ЗаполнитьПараметрыОбласти(КолонкаМесяца.Параметры, ЗаМесяц);
				КолонкаМесяца.Параметры.ИмяМесяца = Формат(Дата(НомерГода, ВыборкаПоМесяцу.МесяцКалендаря, 1), "ДФ='ММММ'"); // АПК:1367
				ТабличныйДокумент.Присоединить(КолонкаМесяца);
			КонецЦикла;
			КолонкаМесяца = Макет.ПолучитьОбласть("КолонкаМесяца");
			ЗаполнитьПараметрыОбласти(КолонкаМесяца.Параметры, ЗаКвартал);
			КолонкаМесяца.Параметры.ИмяМесяца = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = '%1 квартал'"), ВыборкаПоКварталу.КварталКалендаря);
			ТабличныйДокумент.Присоединить(КолонкаМесяца);
			Если ВыборкаПоКварталу.КварталКалендаря = 2 
				Или ВыборкаПоКварталу.КварталКалендаря = 4 Тогда 
				КолонкаМесяца = Макет.ПолучитьОбласть("КолонкаМесяца");
				ЗаполнитьПараметрыОбласти(КолонкаМесяца.Параметры, ЗаПолугодие);
				КолонкаМесяца.Параметры.ИмяМесяца = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = '%1 полугодие'"), ВыборкаПоКварталу.КварталКалендаря / 2);
				ТабличныйДокумент.Присоединить(КолонкаМесяца);
			КонецЕсли;
		КонецЦикла;
		КолонкаМесяца = Макет.ПолучитьОбласть("КолонкаМесяца");
		ЗаполнитьПараметрыОбласти(КолонкаМесяца.Параметры, ЗаГод);
		КолонкаМесяца.Параметры.ИмяМесяца = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = '%1 год'"), Формат(ВыборкаПоГоду.ГодКалендаря, "ЧГ="));
		ТабличныйДокумент.Присоединить(КолонкаМесяца);
	КонецЦикла;
	
	КолонкаМесяца = Макет.ПолучитьОбласть("Среднемесячный");
	ЗаполнитьПараметрыОбласти(КолонкаМесяца.Параметры, ЗаГод);
	КолонкаМесяца.Параметры.ИмяМесяца = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = '%1 год'"), Формат(НомерГода, "ЧГ="));
	ТабличныйДокумент.Вывести(КолонкаМесяца);
	
	КолонкаМесяца = Макет.ПолучитьОбласть("КолонкаМесяцаСр");
	КолонкаМесяца.Параметры.РабочееВремя40 = Формат(ЗаГод.Основной.РабочееВремя40 / 12, "ЧДЦ=2; ЧГ=0");
	КолонкаМесяца.Параметры.РабочееВремя36 = Формат(ЗаГод.Основной.РабочееВремя36 / 12, "ЧДЦ=2; ЧГ=0");
	КолонкаМесяца.Параметры.РабочееВремя24 = Формат(ЗаГод.Основной.РабочееВремя24 / 12, "ЧДЦ=2; ЧГ=0");
	КолонкаМесяца.Параметры.ИмяМесяца = НСтр("ru = 'Среднемесячное количество'");
	ТабличныйДокумент.Присоединить(КолонкаМесяца);
	
	Возврат ТабличныйДокумент;
	
КонецФункции

Функция ОписаниеГруппыПоказателей()
	Описание = Новый Структура("Основной, Нерабочий");
	Описание.Основной = ОписаниеКолонки();
	Описание.Нерабочий = ОписаниеКолонки();
	Возврат Описание;
КонецФункции

Функция ОписаниеКолонки()
	Описание = Новый Структура(
		"КалендарныеДни,
		|РабочиеДни,
		|ВыходныеДни,
		|РабочееВремя40,
		|РабочееВремя36,
		|РабочееВремя24");
	Описание.КалендарныеДни = 0;
	Описание.РабочиеДни = 0;
	Описание.ВыходныеДни = 0;
	Описание.РабочееВремя40 = 0;
	Описание.РабочееВремя36 = 0;
	Описание.РабочееВремя24 = 0;
	Возврат Описание;
КонецФункции

Процедура НакопитьКолонку(Колонка1, Колонка2)
	Для Каждого Группы Из Колонка1 Цикл
		Для Каждого Показатели Из Группы.Значение Цикл
			Колонка1[Группы.Ключ][Показатели.Ключ] = Колонка1[Группы.Ключ][Показатели.Ключ] + Колонка2[Группы.Ключ][Показатели.Ключ];
		КонецЦикла
	КонецЦикла;
КонецПроцедуры

Процедура НакопитьЗначение(Накопленное, Значение)
	Накопленное = Накопленное + Значение;
КонецПроцедуры

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

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

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

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

#КонецЕсли

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