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

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

// Формирует отчеты и отправляет их согласно настройкам в указанную папку файлов, в папку на компьютере, 
// по электронной почте или на FTP-ресурс.
//
// Параметры:
//   Рассылка - СправочникСсылка.РассылкиОтчетов - выполняемая рассылка отчетов.
//   ПараметрыЖурнала - см. РассылкаОтчетов.ПараметрыЖурнала.
//   ДополнительныеНастройки - Структура:
//       * Получатели - Соответствие из КлючИЗначение:
//           ** Ключ - СправочникСсылка - получатель.
//           ** Значение - Строка - набор e-mail адресов получателя в строке с разделителями.
//
// Возвращаемое значение:
//   Булево - признак успешного выполнения рассылки.
//
Функция ВыполнитьРассылкуОтчетов(Рассылка, ПараметрыЖурнала = Неопределено, ДополнительныеНастройки = Неопределено) Экспорт

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

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

	Если ТипЗнч(ПараметрыЖурнала.Метаданные) = Тип("Строка") И ЗначениеЗаполнено(ПараметрыЖурнала.Метаданные) Тогда
		ПараметрыЖурнала.Метаданные = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ПараметрыЖурнала.Метаданные);
	КонецЕсли;
	
	Если ДеревоОтчетов.Строки.Количество() = 0 Тогда

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

			// Объединение общих и личных вложений.
			ВложенияПолучателя = ОбъединитьМассивы(ОбщиеВложения, ЛичныеВложения);

			// Формирование представления отчетов.
			СформироватьПредставлениеОтчетовДляПолучателя(ПараметрыДоставки, СтрокаПолучатель);

			Если ПараметрыДоставки.УстановитьПаролиЗашифровать Тогда
				// Получение индивидуального пароля на архив и сертификата для шифрования
				Если ПараметрыДоставки.Архивировать Тогда
					ПарольАрхива = ПаролиАрхивовПолучателей.Получить(СтрокаПолучатель.Ключ);
					ПараметрыДоставки.ПарольАрхива = "";
					Если ПарольАрхива <> Неопределено Тогда
						ПараметрыДоставки.ПарольАрхива = ПарольАрхива;
					КонецЕсли;
				КонецЕсли;

				Если ДоступноШифрованиеВложений() Тогда
					ПараметрыОтбора = Новый Структура("ПолучательРассылки", СтрокаПолучатель.Ключ);
					НайденныеСтроки = СертификатыШифрованияПолучателей.НайтиСтроки(ПараметрыОтбора);
					ПараметрыДоставки.СертификатДляШифрования = ?(НайденныеСтроки.Количество() > 0,
						НайденныеСтроки[0].СертификатДляШифрования, Неопределено);
				КонецЕсли;

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

			// Архивация вложений.
			АрхивацияВложений(ВложенияПолучателя, ПараметрыДоставки, СтрокаПолучатель.Значение, ПараметрыЖурнала);

		Иначе
			ВложенияПолучателя = Новый Массив;
		КонецЕсли;

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

			ДополнительныеСведения = "";
			Если ОтправлятьСкрытыеКопииОтправителю Тогда
				ДополнительныеСведения = НСтр("ru = 'Копия направлена отправителю.'");
			КонецЕсли;
			ТекстСообщения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Отчеты отправлены ''%1'' от %2. %3'"), ПредставлениеПолучателя, ПредставлениеОтправителя,
				ДополнительныеСведения);

			ЗаписьЖурнала(ПараметрыЖурнала, , ТекстСообщения);
				
			ПараметрыДоставки.Получатели.Удалить(СтрокаПолучатель.Ключ);
		КонецЕсли;
	КонецЦикла;
	
	// Отправка общих отчетов.
	Если ОбщиеВложения.Количество() > 0 Или ПараметрыДоставки.ОтчетыДляТекстаПисьма.Количество() > 0 Тогда
		// Представление отчетов.
		СформироватьПредставлениеОтчетовДляПолучателя(ПараметрыДоставки, СтрокаПолучатель);
		
		Если (ПараметрыДоставки.ИспользоватьЭлектроннуюПочту И ПараметрыДоставки.ПрикреплятьОтчетыВоВложения)
			Или  ПараметрыДоставки.ИспользоватьПапку Или ПараметрыДоставки.ИспользоватьСетевойКаталог 
			Или ПараметрыДоставки.ИспользоватьFTPРесурс Тогда

			УстановитьПривилегированныйРежим(Истина);
			ПараметрыДоставки.ПарольАрхива = ОбщегоНазначения.ПрочитатьДанныеИзБезопасногоХранилища(
			ПараметрыЖурнала.Данные, "ПарольАрхива");
			УстановитьПривилегированныйРежим(Ложь);

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

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

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

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

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

// Конструктор для значения параметра ПараметрыДоставки функции ВыполнитьРассылку.
//
// Возвращаемое значение:
//   Структура - настройки способа доставки отчетов. Состав свойств может отличаться для разных способов доставки.
//     Общие свойства:
//       * Автор - СправочникСсылка.Пользователи - автор рассылки.
//       * ИспользоватьПапку            - Булево - доставлять отчеты в папку подсистемы "Работа с файлами".
//       * ИспользоватьСетевойКаталог   - Булево - доставлять отчеты в папку файловой системы.
//       * ИспользоватьFTPРесурс        - Булево - доставлять отчеты на FTP.
//       * ИспользоватьЭлектроннуюПочту - Булево - доставлять отчеты по электронной почте.
//
//     Свойства, когда ИспользоватьПапку = Истина:
//       * Папка - СправочникСсылка.ПапкиФайлов - папка подсистемы "Работа с файлами".
//
//     Свойства, когда ИспользоватьСетевойКаталог = Истина:
//       * СетевойКаталогWindows - Строка - каталог файловой системы (локальный на сервере или сетевой).
//       * СетевойКаталогLinux   - Строка - каталог файловой системы (локальный на сервере или сетевой).
//
//     Свойства, когда ИспользоватьFTPРесурс = Истина:
//       * Владелец            - СправочникСсылка.РассылкиОтчетов
//       * Сервер              - Строка - имя FTP сервера.
//       * Порт                - Число  - порт FTP сервера.
//       * Логин               - Строка - имя пользователя FTP сервера.
//       * Пароль              - Строка - пароль пользователя FTP сервера.
//       * Каталог             - Строка - путь к каталогу на FTP сервере.
//       * ПассивноеСоединение - Булево - использовать пассивное соединение.
//
//     Свойства, когда ИспользоватьЭлектроннуюПочту = Истина:
//       * УчетнаяЗапись - СправочникСсылка.УчетныеЗаписиЭлектроннойПочты - для отправки почтового сообщения.
//       * Получатели - Соответствие из КлючИЗначение - набор получателей и их e-mail адресов:
//           ** Ключ - СправочникСсылка - получатель.
//           ** Значение - Строка - email-адреса получателя, разделенные запятыми.
//
//     Дополнительные свойства:
//       * Архивировать - Булево - архивировать все файлы сформированных отчетов в один архив.
//                                 Архивация может потребоваться, например, при рассылке графиков в формате html.
//       * ИмяАрхива    - Строка - имя архива.
//       * ПарольАрхива - Строка - пароль архива.
//       * ТранслитерироватьИменаФайлов - Булево - признак необходимости транслитерации имен файлов отчетов рассылки.
//       * СертификатДляШифрования - СправочникСсылка.СертификатыКлючейЭлектроннойПодписиИШифрования - если внедрена
//           подсистема ЭлектроннаяПодпись - Неопределено
//
//     Необязательные свойства, когда ИспользоватьЭлектроннуюПочту = Истина:
//       * Персонализирована - Булево - рассылка персонализирована получателями.
//           Значение по умолчанию Ложь.
//           Если установить значение Истина, то каждый получатель получит отчет с отбором по нему.
//           Для этого в отчетах следует установить отбор "[Получатель]" по реквизиту, совпадающем с типом получателя.
//           Применимо только только при доставке по почте,
//           поэтому когда устанавливается в Истина, то другие способы доставки отключаются.
//       * ТолькоУведомить - Булево - Ложь - отправлять только уведомления (не присоединять сформированные отчеты).
//       * СкрытыеКопии    - Булево - Ложь - если Истина, то при отправке вместо "Кому" заполняется "СкрытыеКопии".
//       * ШаблонТемы      - Строка -       тема письма.
//       * ШаблонТекста    - Строка -       тело письма.
//       * ПараметрыФорматов - Соответствие из КлючИЗначение:
//           ** Ключ - ПеречислениеСсылка.ФорматыСохраненияОтчетов
//           ** Значение - Структура:
//                *** Расширение - Строка
//                *** ТипФайла - ТипФайлаТабличногоДокумента
//                *** Имя - Строка
//       * ПараметрыПисьма - Структура - содержит всю необходимую информацию о письме:
//           ** Кому - Массив
//                   - Строка - интернет адреса получателей письма.
//                   - Массив - коллекция структур адресов:
//                       *** Адрес - Строка - почтовый адрес (должно быть обязательно заполнено).
//                       *** Представление - Строка - имя адресата.
//                   - Строка - интернет-адреса получателей письма, разделитель - ";".
//            ** ПолучателиСообщения - Массив - массив структур, описывающий получателей:
//                 *** Адрес - Строка - почтовый адрес получателя сообщения.
//                 *** Представление - Строка - представление адресата.
//            ** Копии - Массив
//                     - Строка - адреса получателей копий письма. См. описание поля Кому.
//            ** СкрытыеКопии - Массив
//                            - Строка - адреса получателей скрытых копий письма. См. описание поля Кому.
//            ** Тема       - Строка - (обязательный) тема почтового сообщения.
//            ** Тело       - Строка - (обязательный) текст почтового сообщения (простой текст в кодировке win-1251).
//            ** Вложения - Массив - файлы, которые необходимо приложить к письму (описания в виде структур):
//                 *** Представление - Строка - имя файла вложения;
//                 *** АдресВоВременномХранилище - Строка - адрес двоичных данных вложения во временном хранилище.
//                 *** Кодировка - Строка - кодировка вложения (используется, если отличается от кодировки письма).
//                 *** Идентификатор - Строка - (необязательный) используется для отметки картинок, отображаемых в теле письма.
//            ** АдресОтвета - Строка - E-mail адрес, на который будут приходить ответы на рассылку.
//            ** ИдентификаторыОснований - Строка - идентификаторы оснований данного письма.
//            ** ОбрабатыватьТексты  - Булево - необходимость обрабатывать тексты письма при отправке.
//            ** УведомитьОДоставке  - Булево - необходимость запроса уведомления о доставке.
//            ** УведомитьОПрочтении - Булево - необходимость запроса уведомления о прочтении.
//            ** ТипТекста - Строка
//                         - ПеречислениеСсылка.ТипыТекстовЭлектронныхПисем
//                         - ТипТекстаПочтовогоСообщения - определяет тип переданного теста допустимые значения:
//                             HTML/ТипыТекстовЭлектронныхПисем.HTML - текст почтового сообщения в формате HTML.
//                             ПростойТекст/ТипыТекстовЭлектронныхПисем.ПростойТекст - простой текст почтового сообщения.
//                                                                       Отображается "как есть" (значение по умолчанию).
//                             РазмеченныйТекст/ТипыТекстовЭлектронныхПисем.РазмеченныйТекст - текст почтового сообщения
//                                                                                             в формате Rich Text.
//
Функция ПараметрыДоставки() Экспорт
	
	ПараметрыДоставки = РассылкаОтчетовКлиентСервер.ПараметрыДоставки();
	ПараметрыДоставки.ДатаВыполнения = ТекущаяДатаСеанса();
	ПараметрыДоставки.Автор = Пользователи.ТекущийПользователь();
	ПараметрыДоставки.КаталогВременныхФайлов = ФайловаяСистема.ОбщийКаталогВременныхФайлов("RP" + НомерСеансаИнформационнойБазы()) + ПолучитьРазделительПутиСервера();
	ПараметрыДоставки.ПараметрыПисьма.Важность = ВажностьИнтернетПочтовогоСообщения.Обычная;
	
	Если ПолучитьФункциональнуюОпцию("ХранитьИсториюРассылкиОтчетов") Тогда
		ПараметрыДоставки.ПараметрыПисьма.УведомитьОДоставке = Истина;
	КонецЕсли;
	
	Возврат ПараметрыДоставки;
	
КонецФункции

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

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

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

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

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

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

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

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

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

#Область ПанелиАдминистрирования

// Возвращает Истина если у пользователя есть право на сохранение рассылок отчетов.
Функция ПравоДобавления() Экспорт
	Возврат ТекстОшибкиПроверкиПраваДобавления() = "";
КонецФункции

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

#Область ОбработчикиСобытийПодсистемКонфигурации

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

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

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

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

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

// См. ОчередьЗаданийПереопределяемый.ПриОпределенииПсевдонимовОбработчиков.
Процедура ПриОпределенииПсевдонимовОбработчиков(СоответствиеИменПсевдонимам) Экспорт
	
	СоответствиеИменПсевдонимам.Вставить(Метаданные.РегламентныеЗадания.РассылкаОтчетов.ИмяМетода);
	СоответствиеИменПсевдонимам.Вставить(Метаданные.РегламентныеЗадания.ОчисткаИсторииРассылкиОтчетов.ИмяМетода);
	
КонецПроцедуры

// См. УправлениеДоступомПереопределяемый.ПриЗаполненииСписковСОграничениемДоступа.
Процедура ПриЗаполненииСписковСОграничениемДоступа(Списки) Экспорт
	
	Списки.Вставить(Метаданные.Справочники.РассылкиОтчетов, Истина);
	
КонецПроцедуры

// Смотри также ОбновлениеИнформационнойБазыПереопределяемый.ПриОпределенииНастроек
//
// Параметры:
//  Объекты - Массив из ОбъектМетаданных
//
Процедура ПриОпределенииОбъектовСНачальнымЗаполнением(Объекты) Экспорт
	
	Объекты.Добавить(Метаданные.Справочники.РассылкиОтчетов);
	
КонецПроцедуры

// См. ОбновлениеИнформационнойБазыБСП.ПриДобавленииОбработчиковОбновления.
Процедура ПриДобавленииОбработчиковОбновления(Обработчики) Экспорт
	
	Обработчик = Обработчики.Добавить();
	Обработчик.Версия    = "3.1.8.98";
	Обработчик.Процедура = "РассылкаОтчетов.УстановитьКоличествоМесяцевХраненияИсторииРассылкиОтчетов";
	Обработчик.РежимВыполнения = "Оперативно";
	Обработчик.ОбщиеДанные      = Ложь;
	Обработчик.НачальноеЗаполнение = Истина;
	
	Обработчик = Обработчики.Добавить();
	Обработчик.Версия          = "3.1.9.21";
	Обработчик.Идентификатор   = Новый УникальныйИдентификатор("a3675668-c1c4-4012-a007-8df47dbed76d");
	Обработчик.Процедура       = "Справочники.РассылкиОтчетов.ОбработатьДанныеДляПереходаНаНовуюВерсию";
	Обработчик.РежимВыполнения = "Отложенно";
	Обработчик.ПроцедураЗаполненияДанныхОбновления = "Справочники.РассылкиОтчетов.ЗарегистрироватьДанныеКОбработкеДляПереходаНаНовуюВерсию";
	Обработчик.ИзменяемыеОбъекты  = "Справочник.РассылкиОтчетов";
	Обработчик.БлокируемыеОбъекты = "Справочник.РассылкиОтчетов";
	Обработчик.ПроцедураПроверки  = "ОбновлениеИнформационнойБазы.ДанныеОбновленыНаНовуюВерсиюПрограммы";
	Обработчик.Комментарий = НСтр("ru = 'Установка шаблонов наименований отчетов по умолчанию в рассылках отчетов, 
		|установка флага ""Прикреплять отчеты во вложения"". До завершения обработки, не рекомендуется использовать рассылки отчетов.'");
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.Мультиязычность") Тогда
		Обработчик.ПриоритетыВыполнения = ОбновлениеИнформационнойБазы.ПриоритетыВыполненияОбработчика();
		НоваяСтрока = Обработчик.ПриоритетыВыполнения.Добавить();
		НоваяСтрока.Процедура = "МультиязычностьСервер.ОбработатьДанныеДляПереходаНаНовуюВерсию";
		НоваяСтрока.Порядок = "До";
	КонецЕсли;
	
КонецПроцедуры

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	Результат = ВариантыОтчетов.ПараметрыФормированияОтчета();
	ОбщегоНазначенияКлиентСервер.ДополнитьСтруктуру(Результат, ПараметрыФормированияОтчета, Истина);

	ЗаполнитьЗначенияСвойств(ПараметрыОтчета, ДополнительныеПараметры);
	Результат.Подключение = ПараметрыОтчета;
	
	Возврат Результат;
	
КонецФункции

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

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

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

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

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

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

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

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

	Запрос.УстановитьПараметр("РассылкаОтчетов",Рассылка);
	Запрос.УстановитьПараметр("ЗапускРассылки", ПоследнийЗапускНачало);
	Запрос.УстановитьПараметр("НомерСеанса", НомерСеанса);
	
	РезультатЗапроса = Запрос.Выполнить();
	
	ВыборкаПолучатели = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
		
	Пока ВыборкаПолучатели.Следующий() Цикл
		Выборка = ВыборкаПолучатели.Выбрать();
		АдресЭлектроннойПочты = "";
		Пока Выборка.Следующий() Цикл
			ТекущийАдрес = ?(ПустаяСтрока(АдресЭлектроннойПочты), "",
				АдресЭлектроннойПочты + "; ");
			АдресЭлектроннойПочты = ТекущийАдрес + Выборка.АдресЭлектроннойПочты;
		КонецЦикла; 
		Получатели.Вставить(ВыборкаПолучатели.Получатель, АдресЭлектроннойПочты);
	КонецЦикла;
	
	Возврат Получатели;
	
КонецФункции

// Формирует представление способов доставки в соответствии с параметрами доставки.
//
// Параметры:
//   ПараметрыДоставки - см. ВыполнитьРассылку.ПараметрыДоставки.
//
// Возвращаемое значение:
//   Строка
//
Функция ПредставлениеСпособовДоставки(ПараметрыДоставки) Экспорт
	Префикс = НСтр("ru = 'Результат'");
	ТекстПредставления = "";
	Суффикс = "";
	
	Если Не ПараметрыДоставки.ТолькоУведомить Тогда
		
		ТекстПредставления = ТекстПредставления 
		+ ?(ТекстПредставления = "", Префикс, " " + НСтр("ru = 'и'")) 
		+ " "
		+ НСтр("ru = 'отправлен по почте (см. вложения)'");
		
	КонецЕсли;
	
	Если ПараметрыДоставки.ВыполненаВПапку Тогда
		
		ТекстПредставления = ТекстПредставления 
		+ ?(ТекстПредставления = "", Префикс, " " + НСтр("ru = 'и'")) 
		+ " "
		+ НСтр("ru = 'доставлен в папку'")
		+ " ";
		
		Ссылка = ПолучитьНавигационнуюСсылкуИнформационнойБазы() +"#"+ ПолучитьНавигационнуюСсылку(ПараметрыДоставки.Папка);
		
		Если ПараметрыДоставки.ПисьмоВФорматеHTML Тогда
			ТекстПредставления = ТекстПредставления 
			+ "<a href = '"
			+ Ссылка
			+ "'>" 
			+ Строка(ПараметрыДоставки.Папка)
			+ "</a>";
		Иначе
			ТекстПредставления = ТекстПредставления 
			+ """"
			+ Строка(ПараметрыДоставки.Папка)
			+ """";
			Суффикс = Суффикс + ":" + Символы.ПС + "<" + Ссылка + ">";
		КонецЕсли;
		
	КонецЕсли;
	
	Если ПараметрыДоставки.ВыполненаВСетевойКаталог Тогда
		
		ТекстПредставления = ТекстПредставления 
		+ ?(ТекстПредставления = "", Префикс, " " + НСтр("ru = 'и'")) 
		+ " "
		+ НСтр("ru = 'доставлен в сетевой каталог'")
		+ " ";
		
		Если ПараметрыДоставки.ПисьмоВФорматеHTML Тогда
			ТекстПредставления = ТекстПредставления 
			+ "<a href = '"
			+ ПараметрыДоставки.СетевойКаталогWindows
			+ "'>" 
			+ ПараметрыДоставки.СетевойКаталогWindows
			+ "</a>";
		Иначе
			ТекстПредставления = ТекстПредставления 
			+ "<"
			+ ПараметрыДоставки.СетевойКаталогWindows
			+ ">";
		КонецЕсли;
		
	КонецЕсли;
	
	Если ПараметрыДоставки.ВыполненаНаFTP Тогда
		
		ТекстПредставления = ТекстПредставления 
		+ ?(ТекстПредставления = "", Префикс, " " + НСтр("ru = 'и'")) 
		+ " "
		+ НСтр("ru = 'доставлен на FTP ресурс'")
		+ " ";
		
		Ссылка = "ftp://"
		+ ПараметрыДоставки.Сервер 
		+ ":"
		+ Формат(ПараметрыДоставки.Порт, "ЧН=0; ЧГ=0") 
		+ ПараметрыДоставки.Каталог;
		
		Если ПараметрыДоставки.ПисьмоВФорматеHTML Тогда
			ТекстПредставления = ТекстПредставления 
			+ "<a href = '"
			+ Ссылка
			+ "'>" 
			+ Ссылка
			+ "</a>";
		Иначе
			ТекстПредставления = ТекстПредставления 
			+ "<"
			+ Ссылка
			+ ">";
		КонецЕсли;
		
	КонецЕсли;
	
	ТекстПредставления = ТекстПредставления + ?(Суффикс = "", ".", Суффикс);
	
	Возврат ТекстПредставления;
КонецФункции

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

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

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

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

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

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

			ПараметрыФормата = ПараметрыДоставки.ПараметрыФорматов.Получить(Формат);

			Если ПараметрыФормата = Неопределено Тогда
				Продолжить;
			КонецЕсли;

			ПолноеИмяФайла = ПолноеИмяФайлаПоШаблону(
			КаталогПолучателя, СтрокаОтчет.Ключ, ПараметрыФормата, ПараметрыДоставки,
				ПараметрыОтчета.ШаблонНаименования, Период);

			НайтиСвободноеИмяФайла(ПолноеИмяФайла);

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

				Если ПараметрыФормата.ТипФайла = Неопределено Тогда
					ЗаписьЖурнала(ПараметрыЖурнала, УровеньЖурналаРегистрации.Ошибка,
						СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ЗаголовокОшибки, СтрокаОтчет.Ключ,
						ПараметрыФормата.Имя), НСтр("ru = 'Формат не поддерживается.'"));
					Продолжить;
				КонецЕсли;

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

			СтрокаФайл.Настройки = Новый Структура("ФайлСКаталогом, ИмяФайла, ПолноеИмяФайла, ИмяКаталога, ПолноеИмяКаталога, 
				|Формат, Имя, Расширение, ТипФайла, Ссылка");

			СтрокаФайл.Настройки.Формат = Формат;
			ЗаполнитьЗначенияСвойств(СтрокаФайл.Настройки, ПараметрыФормата, "Имя, Расширение, ТипФайла");

			СтрокаФайл.Настройки.ИмяФайла          = ВременныйФайл.Имя;
			СтрокаФайл.Настройки.ПолноеИмяФайла    = ВременныйФайл.ПолноеИмя;
			СтрокаФайл.Настройки.ИмяКаталога       = ВременныйФайл.ИмяБезРасширения + "_files";
			СтрокаФайл.Настройки.ПолноеИмяКаталога = ВременныйФайл.Путь + СтрокаФайл.Настройки.ИмяКаталога + ПолучитьРазделительПутиСервера();

			КаталогФайла = Новый Файл(СтрокаФайл.Настройки.ПолноеИмяКаталога);

			СтрокаФайл.Настройки.ФайлСКаталогом = (КаталогФайла.Существует() И КаталогФайла.ЭтоКаталог());

			Если СтрокаФайл.Настройки.ФайлСКаталогом И Не ПараметрыДоставки.Архивировать Тогда
			// Каталог вместе с файлом архивируются, а вместо файла отправляется архив файла с каталогом.
				ИмяАрхива       = ВременныйФайл.ИмяБезРасширения + ".zip";
				ПолноеИмяАрхива = КаталогПолучателя + ИмяАрхива;

				РежимСохранения = РежимСохраненияПутейZIP.СохранятьОтносительныеПути;
				РежимОбработки  = РежимОбработкиПодкаталоговZIP.ОбрабатыватьРекурсивно;

				ЗаписьZipФайла = Новый ЗаписьZipФайла(ПолноеИмяАрхива);
				ЗаписьZipФайла.Добавить(СтрокаФайл.Настройки.ПолноеИмяФайла, РежимСохранения, РежимОбработки);
				ЗаписьZipФайла.Добавить(СтрокаФайл.Настройки.ПолноеИмяКаталога, РежимСохранения, РежимОбработки);
				ЗаписьZipФайла.Записать();

				СтрокаФайл.Ключ     = ИмяАрхива;
				СтрокаФайл.Значение = ПолноеИмяАрхива;
			КонецЕсли;

			КаталогФайла = Неопределено;
			ВременныйФайл = Неопределено;

			ПредставлениеФорматов = ПредставлениеФорматов + ?(ПредставлениеФорматов = "", "", ", ") + ?(
				ПараметрыДоставки.ДобавлятьСсылки = "КФорматам", "<a href = '" + СтрокаФайл.Значение + "'>", "")
				+ ПараметрыФормата.Имя + ?(ПараметрыДоставки.ДобавлятьСсылки = "КФорматам", "</a>", "");
			
		//
			Если ПараметрыДоставки.ДобавлятьСсылки = "ПослеОтчетов" Тогда
				ПредставлениеОтчета = ПредставлениеОтчета + Символы.ПС + "<" + СтрокаФайл.Значение + ">";
			КонецЕсли;

			Если ПараметрыДоставки.ИспользоватьЭлектроннуюПочту И ПараметрыДоставки.ВставлятьОтчетыВТекстПисьма Тогда
				Если (ПараметрыДоставки.ПисьмоВФорматеHTML И Формат = Перечисления.ФорматыСохраненияОтчетов.HTML)
				   Или (НЕ ПараметрыДоставки.ПисьмоВФорматеHTML И Формат = Перечисления.ФорматыСохраненияОтчетов.TXT) Тогда
					ПараметрыОтчетаДляТекстаПисьма = ПараметрыОтчетаДляТекстаПисьма(КаталогПолучателя, ПредставлениеОтчета,
						ПараметрыОтчета.ШаблонНаименования, ПолучательСсылка, СтрокаОтчет.Значение);
					ПодготовитьОтчетДляТекстаПисьма(ПараметрыДоставки, ПараметрыОтчетаДляТекстаПисьма,
						ПараметрыЖурнала, ПолноеИмяФайла);
					ОтчетПодготовленДляТекстаПисьма = Истина;
				КонецЕсли;
			КонецЕсли;

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

	КонецЕсли;
	
	// Представление конкретного отчета.
	ПредставлениеОтчета = СтрЗаменить(ПредставлениеОтчета, "[ПредставлениеФорматов]", ПредставлениеФорматов);
	СтрокаОтчет.Настройки.Вставить("ПредставлениеВПисьме", ПредставлениеОтчета);
	
КонецПроцедуры

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

Процедура ПодготовитьОтчетДляТекстаПисьма(ПараметрыДоставки, ПараметрыОтчета, ПараметрыЖурнала, ПолноеИмяФайла = Неопределено)

	Если ПолноеИмяФайла = Неопределено Тогда
		ПолноеИмяФайла = ПутьКВременномуФайлаОтчетаДляТекстаПисьма(ПараметрыДоставки, ПараметрыОтчета, ПараметрыЖурнала);
	КонецЕсли;

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

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

Функция ПутьКВременномуФайлаОтчетаДляТекстаПисьма(ПараметрыДоставки, ПараметрыОтчета, ПараметрыЖурнала)

	Формат = ?(ПараметрыДоставки.ПисьмоВФорматеHTML, Перечисления.ФорматыСохраненияОтчетов.HTML,
		Перечисления.ФорматыСохраненияОтчетов.TXT);

	ПараметрыФормата = ПараметрыДоставки.ПараметрыФорматов.Получить(Формат);
	Если ПараметрыФормата = Неопределено Тогда
		Возврат Неопределено;
	КонецЕсли;

	ПолноеИмяФайла = ПолноеИмяФайлаПоШаблону(ПараметрыОтчета.КаталогПолучателя, ПараметрыОтчета.ПредставлениеОтчета,
		ПараметрыФормата, ПараметрыДоставки, ПараметрыОтчета.ШаблонНаименования, Неопределено);

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

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

	Возврат ПолноеИмяФайла;

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

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

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

						КоличествоСимволовБезРасширения = СтрНайти(Отчет.ПолноеИмяФайла, ".html",
							НаправлениеПоиска.СКонца);
						КоличествоСимволовБезРасширения = ?(КоличествоСимволовБезРасширения > 0,
							КоличествоСимволовБезРасширения - 1, КоличествоСимволовБезРасширения);
						ПолноеИмяКаталогаКартинок = Лев(Отчет.ПолноеИмяФайла, КоличествоСимволовБезРасширения) + "_files";
						КаталогКартинок = Новый Файл(ПолноеИмяКаталогаКартинок);
						Если КаталогКартинок.Существует() И КаталогКартинок.ЭтоКаталог() Тогда
							ИмяКаталога = КаталогКартинок.ИмяБезРасширения;
							ФайлыКартинок = НайтиФайлы(ПолноеИмяКаталогаКартинок, ПолучитьМаскуВсеФайлы());
							Для Каждого ФайлКартинки Из ФайлыКартинок Цикл
								Если СчетчикКартинок > 150 Тогда
									Прервать;
								КонецЕсли;
								СчетчикКартинок = СчетчикКартинок + 1;
								НовоеИмяКартинки = ФайлКартинки.ИмяБезРасширения + "Report" + СчетчикОтчетов;
								ТекстОтчета = СтрЗаменить(ТекстОтчета, ИмяКаталога + "/" + ФайлКартинки.Имя,
									НовоеИмяКартинки);
								ПараметрыДоставки.Картинки.Вставить(НовоеИмяКартинки,
									Новый Картинка(ФайлКартинки.ПолноеИмя));
							КонецЦикла;
						КонецЕсли;
					КонецЕсли;
					ТекстВсехОтчетов = ТекстВсехОтчетов + Символы.ПС + "<br>" + "<br>" + ТекстОтчета;
				Иначе
					ТекстВсехОтчетов = ТекстВсехОтчетов + Символы.ПС + Символы.ПС + Символы.ПС
						+ "------------------------------------------" + Символы.ПС + ТекстОтчета;
				КонецЕсли;
			КонецЦикла;
			ПараметрыПисьма.Тело = ПараметрыПисьма.Тело + Символы.ПС + ТекстВсехОтчетов;
		ИначеЕсли ПараметрыДоставки.ПараметрыПисьма.Вложения.Количество() = 0 Тогда
			Возврат;
		КонецЕсли;
	КонецЕсли;
	
	Если Получатель = Неопределено Тогда
		Если ПараметрыДоставки.Получатели.Количество() = 0 Тогда
			Возврат;
		КонецЕсли;
		
		// Шаблоны не персонализированы - склеивание E-Mail адресов получателей и совместная доставка.
		Кому = "";
		Для Каждого КлючИЗначение Из ПараметрыДоставки.Получатели Цикл
			Кому = Кому + ?(Кому = "", "", ", ") + КлючИЗначение.Значение;
		КонецЦикла;

		ПараметрыПисьма.Вставить(КлючАдресаДоставки, Кому);
			
		// Отправляем письмо
		ОтправитьПочтовоеСообщение(ПараметрыДоставки, ПараметрыПисьма, СтрокаПолучатель, ПараметрыЖурнала);
	Иначе
		// Доставка конкретному получателю.
		
		// Тема и тело сообщения
		Если ПараметрыДоставки.ЗаполнитьПолучателяВШаблонеТемы Тогда
			ПараметрыПисьма.Тема = СтрЗаменить(ПараметрыПисьма.Тема, "[Получатель]", Строка(Получатель));
		КонецЕсли;
		Если ПараметрыДоставки.ЗаполнитьПолучателяВШаблонеСообщения Тогда
			ПараметрыПисьма.Тело = СтрЗаменить(ПараметрыПисьма.Тело, "[Получатель]", Строка(Получатель));
		КонецЕсли;
		
		// Получатель
		ПараметрыПисьма.Вставить(КлючАдресаДоставки, ПараметрыДоставки.Получатели[Получатель]);
		
		// Отправляем письмо
		ОтправитьПочтовоеСообщение(ПараметрыДоставки, ПараметрыПисьма, СтрокаПолучатель, ПараметрыЖурнала);
	КонецЕсли;  
			
КонецПроцедуры

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

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

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

			ЗафиксироватьРезультатВыполненияРассылкиНаЭлектроннуюПочту(ПараметрыДоставки, ПараметрыЖурнала,
				Получатель.Ключ, АдресаПолучателя, РезультатОтправки, ДополнительныеПараметры);
		КонецЦикла;
	КонецЕсли;

КонецПроцедуры
	
Процедура ОтправитьПисьмоВзаимодействие(ПараметрыДоставки, ПараметрыПисьма, СтрокаПолучатель, ПараметрыЖурнала)

	МодульВзаимодействия = ОбщегоНазначения.ОбщийМодуль("Взаимодействия");
	Сообщение = ПараметрыСообщенияДляПодсистемыВзаимодействия(ПараметрыДоставки, ПараметрыПисьма);

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

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

				ЗафиксироватьРезультатВыполненияРассылкиНаЭлектроннуюПочту(ПараметрыДоставки, ПараметрыЖурнала,
					Получатель.Ключ, АдресаПолучателя, РезультатОтправки, ДополнительныеПараметры);
			КонецЦикла;
		КонецЕсли;
		Возврат;
	КонецЕсли;

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

	ВызватьИсключение РезультатОтправки.ОписаниеОшибки;

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

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

		ДополнительнаяИнформация = "";
		Если ОтправлятьСкрытыеКопииОтправителю Тогда
			ДополнительнаяИнформация = НСтр("ru = 'Копия направлена отправителю.'");
		КонецЕсли;
		
		ДополнительныеПараметры = Новый Структура;
		ДополнительныеПараметры.Вставить("ПредставлениеОтправителя", ПредставлениеОтправителя);
		ДополнительныеПараметры.Вставить("ДополнительнаяИнформация", ДополнительнаяИнформация);
		ДополнительныеПараметры.Вставить("СтрокаПолучатель", СтрокаПолучатель);
		ДополнительныеПараметры.Вставить("ИспользуетсяПочтовыйКлиент", ИспользуетсяПочтовыйКлиент);
		
		Возврат ДополнительныеПараметры;
	
КонецФункции

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

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

Функция ПараметрыСообщенияДляПодсистемыВзаимодействия(ПараметрыДоставки, ПараметрыПисьма)

	МодульВзаимодействия = ОбщегоНазначения.ОбщийМодуль("Взаимодействия");
	Сообщение = МодульВзаимодействия.ПараметрыПисьма();
	Сообщение.Тема  = ПараметрыПисьма.Тема;
	Сообщение.Текст = ПараметрыПисьма.Тело;
	Сообщение.Важность = ПараметрыПисьма.Важность;
	Сообщение.ДополнительныеПараметры.УведомитьОДоставке = ПараметрыПисьма.УведомитьОДоставке;  
	Сообщение.ДополнительныеПараметры.УведомитьОПрочтении = ПараметрыПисьма.УведомитьОПрочтении;

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

		Для Каждого Получатель Из ПараметрыДоставки.Получатели Цикл
			ПредставлениеПолучателя = Строка(Получатель.Ключ);
			АдресаПолучателя = ОбщегоНазначенияКлиентСервер.РазобратьСтрокуСПочтовымиАдресами(Получатель.Значение);
			Для Каждого Кому Из АдресаПолучателя Цикл
				НоваяСтрока = Сообщение[ИмяТаблицыПолучателей].Добавить();
				НоваяСтрока.Адрес         = Кому.Адрес;
				НоваяСтрока.Представление = ПредставлениеПолучателя + " (" + Кому.Адрес + ")";
				НоваяСтрока.ИсточникКонтактнойИнформации = Получатель.Ключ;
			КонецЦикла;
		КонецЦикла;
	КонецЕсли;
	
	Если ЗначениеЗаполнено(ПараметрыПисьма.АдресОтвета) Тогда
		НоваяСтрока = Сообщение.ПолучателиОтвета.Добавить();
		НоваяСтрока.Адрес         = ПараметрыПисьма.АдресОтвета;
		НоваяСтрока.Представление = ПараметрыПисьма.АдресОтвета;
	КонецЕсли;
		
	Для Каждого Картинка Из ПараметрыДоставки.Картинки Цикл
		СтрокаВложение = Сообщение.Вложения.Добавить();
		ФайлКартинка = Картинка.Значение.ПолучитьДвоичныеДанные();
		СтрокаВложение.АдресВоВременномХранилище = ПоместитьВоВременноеХранилище(ФайлКартинка);
		СтрокаВложение.Представление = Картинка.Ключ;
		СтрокаВложение.Идентификатор = Картинка.Ключ;
	КонецЦикла;

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

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

// Объединяет массивы, возвращая результат объединения.
Функция ОбъединитьМассивы(Массив1, Массив2)
	Массив = Новый Массив;
	Для Каждого ЭлементМассива Из Массив1 Цикл
		Массив.Добавить(ЭлементМассива);
	КонецЦикла;
	Для Каждого ЭлементМассива Из Массив2 Цикл
		Массив.Добавить(ЭлементМассива);
	КонецЦикла;
	Возврат Массив;
КонецФункции

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

// Параметры сохранения табличного документа в формат.
//
// Параметры:
//   Формат - ПеречислениеСсылка.ФорматыСохраненияОтчетов - формат, для которого необходимо получить параметры.
//
// Возвращаемое значение:
//   Структура - результат - Структура - Параметры записи:
//       * Расширение - Строка - расширение, с которым можно сохранить файл.
//       * ТипФайла - ТипФайлаТабличногоДокумента - формат сохранения табличного документа.
//           Используется для определения параметра <ТипФайлаТаблицы> метода "ТабличныйДокумент.Записать".
//
Функция ПараметрыЗаписиТабличногоДокументаВФормат(Формат) Экспорт
	Результат = Новый Структура("Расширение, ТипФайла");
	Если Формат = Перечисления.ФорматыСохраненияОтчетов.XLSX Тогда
		Результат.Расширение = ".xlsx";
		Результат.ТипФайла = ТипФайлаТабличногоДокумента.XLSX;
		
	ИначеЕсли Формат = Перечисления.ФорматыСохраненияОтчетов.XLS Тогда
		Результат.Расширение = ".xls";
		Результат.ТипФайла = ТипФайлаТабличногоДокумента.XLS;
		
	ИначеЕсли Формат = Перечисления.ФорматыСохраненияОтчетов.ODS Тогда
		Результат.Расширение = ".ods";
		Результат.ТипФайла = ТипФайлаТабличногоДокумента.ODS;
		
	ИначеЕсли Формат = Перечисления.ФорматыСохраненияОтчетов.MXL Тогда
		Результат.Расширение = ".mxl";
		Результат.ТипФайла = ТипФайлаТабличногоДокумента.MXL;
		
	ИначеЕсли Формат = Перечисления.ФорматыСохраненияОтчетов.PDF Тогда
		Результат.Расширение = ".pdf";
		Результат.ТипФайла = СтандартныеПодсистемыСервер.ТипФайлаТабличногоДокументаPDF();
		
	ИначеЕсли Формат = Перечисления.ФорматыСохраненияОтчетов.HTML Тогда
		Результат.Расширение = ".html";
		Результат.ТипФайла = ТипФайлаТабличногоДокумента.HTML5;
		
	ИначеЕсли Формат = Перечисления.ФорматыСохраненияОтчетов.HTML4 Тогда
		Результат.Расширение = ".html";
		Результат.ТипФайла = ТипФайлаТабличногоДокумента.HTML4;
		
	ИначеЕсли Формат = Перечисления.ФорматыСохраненияОтчетов.DOCX Тогда
		Результат.Расширение = ".docx";
		Результат.ТипФайла = ТипФайлаТабличногоДокумента.DOCX;
		
	ИначеЕсли Формат = Перечисления.ФорматыСохраненияОтчетов.TXT Тогда
		Результат.Расширение = ".txt";
		Результат.ТипФайла = ТипФайлаТабличногоДокумента.TXT;
	
	ИначеЕсли Формат = Перечисления.ФорматыСохраненияОтчетов.ANSITXT Тогда
		Результат.Расширение = ".txt";
		Результат.ТипФайла = ТипФайлаТабличногоДокумента.ANSITXT;
		
	Иначе 
		// "Рыба" для всех форматов, добавленных при внедрении, обработчик сохранения
		// которых должен находиться в переопределяемом модуле.
		Результат.Расширение = Неопределено;
		Результат.ТипФайла = Неопределено;
		
	КонецЕсли;
	
	Возврат Результат;
КонецФункции

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

// Преобразует недопустимые символы файлов в похожие допустимые.
//   Работает только с именем файла, путь не поддерживается.
//
// Параметры:
//   ИсходноеИмяФайла - Строка - имя файла, из которого следует удалить недопустимые символы.
//
// Возвращаемое значение:
//   Строка - результат - Строка - Результат преобразования.
//
Функция ПривестиИмяФайла(ИсходноеИмяФайла, ТранслитерироватьИменаФайлов)
	
	Результат = Лев(СокрЛП(ИсходноеИмяФайла), 255);
	
	СоответствиеЗамен = Новый Соответствие;
	
	// Стандартные не поддерживаемые символы.
	СоответствиеЗамен.Вставить("""", "'");
	СоответствиеЗамен.Вставить("/", "_");
	СоответствиеЗамен.Вставить("\", "_");
	СоответствиеЗамен.Вставить(":", "_");
	СоответствиеЗамен.Вставить(";", "_");
	СоответствиеЗамен.Вставить("|", "_");
	СоответствиеЗамен.Вставить("=", "_");
	СоответствиеЗамен.Вставить("?", "_");
	СоответствиеЗамен.Вставить("*", "_");
	СоответствиеЗамен.Вставить("<", "_");
	СоответствиеЗамен.Вставить(">", "_");
	
	// Символы, не поддерживаемые устаревшими ОС.
	СоответствиеЗамен.Вставить("[", "");
	СоответствиеЗамен.Вставить("]", "");
	СоответствиеЗамен.Вставить(",", "");
	СоответствиеЗамен.Вставить("{", "");
	СоответствиеЗамен.Вставить("}", "");
	
	Для Каждого КлючИЗначение Из СоответствиеЗамен Цикл
		Результат = СтрЗаменить(Результат, КлючИЗначение.Ключ, КлючИЗначение.Значение);
	КонецЦикла;
	
	Если ТранслитерироватьИменаФайлов Тогда
		Результат = СтроковыеФункции.СтрокаЛатиницей(Результат);
	КонецЕсли;
	
	Возврат Результат;
КонецФункции

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

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

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

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

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

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

// Формирует текст о успешной отправки отчетов для записи в РегистрСведений.ИсторияРассылкиОтчетов.
Функция ТекстУспешнойОтправкиОтчетов(ПараметрыДоставки, СтрокаПолучатель, ПредставлениеПолучателя, ПредставлениеОтправителя, ДополнительныеСведения = "")

	СформированныеОтчеты = Новый Массив;

	Если ПараметрыДоставки.ИспользоватьЭлектроннуюПочту И ПараметрыДоставки.ЗаполнитьСформированныеОтчетыВШаблонеСообщения Тогда

		Для Каждого СтрокаОтчет Из ПараметрыДоставки.СтрокаОбщихОтчетов.Строки Цикл
			СформированныеОтчеты.Добавить(СтрокаОтчет.Настройки.ПредставлениеВПисьме);
		КонецЦикла;

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

	Если СформированныеОтчеты.Количество() = 1 Тогда 
		ТекстСообщения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = '- ""%1"" отправлен от %2. %3'"), СформированныеОтчеты[0],
			ПредставлениеОтправителя, ДополнительныеСведения);	
	Иначе

		ОтчетыСтрокой = "";

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

	КонецЕсли;

	Если ПараметрыДоставки.Архивировать Тогда
		УпакованоВАрхив = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Упаковано в архив ""%1""'"), ПараметрыДоставки.ИмяАрхива);
		ТекстСообщения = ТекстСообщения + Символы.ПС + УпакованоВАрхив;	
	КонецЕсли;

	Возврат ТекстСообщения;
	
КонецФункции

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

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

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

// Возвращает список значений перечисления "ФорматыСохраненияОтчетов".
//
// Возвращаемое значение: 
//   СписокЗначений - список форматов, с пометками на системных форматах по умолчанию, где:
//     * Значение      - ПеречислениеСсылка.ФорматыСохраненияОтчетов - ссылка на описываемый формат.
//     * Представление - Строка - пользовательское представление описываемого формата.
//     * Пометка       - Булево - признак использования как формата по умолчанию.
//     * Картинка      - Картинка - картинка формата.
//
Функция СписокФорматов() Экспорт
	СписокФорматов = Новый СписокЗначений;
	
	УстановитьПараметрыФормата(СписокФорматов, "HTML", БиблиотекаКартинок.ФорматHTML, Истина);
	УстановитьПараметрыФормата(СписокФорматов, "PDF"  , БиблиотекаКартинок.ФорматPDF);
	УстановитьПараметрыФормата(СписокФорматов, "XLSX" , БиблиотекаКартинок.ФорматExcel2007);
	УстановитьПараметрыФормата(СписокФорматов, "XLS"  , БиблиотекаКартинок.ФорматExcel);
	УстановитьПараметрыФормата(СписокФорматов, "ODS"  , БиблиотекаКартинок.ФорматOpenOfficeCalc);
	УстановитьПараметрыФормата(СписокФорматов, "MXL"  , БиблиотекаКартинок.ФорматMXL);
	УстановитьПараметрыФормата(СписокФорматов, "DOCX" , БиблиотекаКартинок.ФорматWord2007);
	УстановитьПараметрыФормата(СписокФорматов, "TXT"    , БиблиотекаКартинок.ФорматTXT);
	УстановитьПараметрыФормата(СписокФорматов, "ANSITXT", БиблиотекаКартинок.ФорматTXT);
	
	РассылкаОтчетовПереопределяемый.ПереопределитьПараметрыФорматов(СписокФорматов);
	
	// Оставшиеся форматы
	Для каждого ФорматСсылка Из Перечисления.ФорматыСохраненияОтчетов Цикл
		Если ФорматСсылка = Перечисления.ФорматыСохраненияОтчетов.HTML4 Тогда // Пропустить HTML4
			Продолжить;
		КонецЕсли;
		УстановитьПараметрыФормата(СписокФорматов, ФорматСсылка);
	КонецЦикла;
	
	Возврат СписокФорматов;
КонецФункции

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

// Получает заголовок системы, а если он не задан - синоним метаданных конфигурации.
Функция ИмяЭтойИнформационнойБазы() Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	Результат = Константы.ЗаголовокСистемы.Получить();
	Возврат ?(ПустаяСтрока(Результат), Метаданные.Синоним, Результат);
	
КонецФункции

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

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

Функция ПредставлениеФормата(Формат) Экспорт
	
	Если Формат = Перечисления.ФорматыСохраненияОтчетов.PDF Тогда
		Возврат СтандартныеПодсистемыСервер.ПредставлениеТипаФайлаТабличногоДокументаPDF();
	КонецЕсли;
	
	Возврат Строка(Формат);
	
КонецФункции

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

Функция ПоляИсторииРассылкиОтчетов(РассылкаОтчетов, Получатель, ЗапускРассылки)

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

	Возврат ПоляИстории;

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

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

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

	СпособПолучения = "";

	Если ПараметрыДоставки.ИспользоватьСетевойКаталог Тогда
		СетевойКаталогСервера = ПараметрыДоставки.СетевойКаталогWindows;

		Если ОбщегоНазначения.ЭтоLinuxСервер() Тогда
			СетевойКаталогСервера = ПараметрыДоставки.СетевойКаталогLinux;
		КонецЕсли;

		СпособПолучения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр(
			"ru = 'Публиковать в сетевой каталог: ""%1"".'"), СетевойКаталогСервера);
	КонецЕсли;

	Если ПараметрыДоставки.ИспользоватьFTPРесурс Тогда
		РесурсFTP = "ftp://" + ПараметрыДоставки.Сервер + ":" + Формат(ПараметрыДоставки.Порт, "ЧН=0; ЧГ=0")
			+ ПараметрыДоставки.Каталог;
		СпособПолучения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр(
			"ru = 'Публиковать на FTP ресурс: ""%1"".'"), РесурсFTP);
	КонецЕсли;

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

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

	КонецЕсли;

	Возврат СпособПолучения;

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

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

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

	Запрос.УстановитьПараметр("НачальнаяДатаХраненияИстории", НачальнаяДатаХраненияИстории);

	НачатьТранзакцию();
	Попытка

	БлокировкаДанных = Новый БлокировкаДанных;
	ЭлементБлокировкиДанных = БлокировкаДанных.Добавить("РегистрСведений.ИсторияРассылкиОтчетов");
	ЭлементБлокировкиДанных.Режим = РежимБлокировкиДанных.Разделяемый;
	БлокировкаДанных.Заблокировать();

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

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

// Устанавливает значение количества месяцев хранения истории рассылки по умолчанию
//
Процедура УстановитьКоличествоМесяцевХраненияИсторииРассылкиОтчетов() Экспорт
	
	Константы.КоличествоМесяцевХраненияИсторииРассылкиОтчетов.Установить(3);
	
КонецПроцедуры

Функция ДоступноШифрованиеВложений() Экспорт

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

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

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

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

Функция ПолучитьПериодИзПользовательскихНастроек(ПользовательскиеНастройкиКД) Экспорт
	Если ТипЗнч(ПользовательскиеНастройкиКД) <> Тип("ПользовательскиеНастройкиКомпоновкиДанных") Тогда
		Возврат Неопределено;
	КонецЕсли;

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

// Параметры:
//  Форма - ФормаКлиентскогоПриложения
//
Процедура ДобавитьКомандыДобавленияДополнительныхПараметровТекста(Форма) Экспорт

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

	ДополнительныеПараметрыТекстаПисьма = Новый Структура;

	ТипПолучателейРассылки = ?(Форма.ВидРассылки = "Личная", Неопределено, Форма.ТипПолучателейРассылки);
	РассылкаОтчетовПереопределяемый.ПриОпределенииПараметровТекстаПисьма(Форма.ВидРассылки,
		ТипПолучателейРассылки, ДополнительныеПараметрыТекстаПисьма);

	Если ДополнительныеПараметрыТекстаПисьма.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;

	Для Каждого ПараметрТекста Из ДополнительныеПараметрыТекстаПисьма Цикл

		ИмяКоманды        = "ДобавитьШаблон" + СтрЗаменить(ТРег(ПараметрТекста.Ключ), Символы.НПП, "");
		Команда           = Форма.Команды.Добавить(ИмяКоманды);
		Команда.Заголовок = ПараметрТекста.Значение;
		Команда.Подсказка = ПараметрТекста.Значение;
		Команда.Действие  = "Подключаемый_ДобавитьДополнительныйПараметрТекстаПисьма";
		
		ГруппаПараметры = Форма.Элементы.ТекстПисьмаДобавитьПараметрШаблона;
		ИмяКнопкиТекст = "ДобавитьШаблон" + ИмяКоманды;
		Кнопка = Форма.Элементы.Добавить(ИмяКнопкиТекст, Тип("КнопкаФормы"), ГруппаПараметры);
		Кнопка.Заголовок  = ПараметрТекста.Значение;
		Кнопка.ИмяКоманды = ИмяКоманды;
		Форма.Элементы.Переместить(Кнопка, ГруппаПараметры, Форма.Элементы.ДобавитьШаблонПоУмолчанию);
		
		ГруппаПараметры = Форма.Элементы.ТекстПисьмаФорматированныйДокументДобавитьПараметрШаблона;
		ИмяКнопкиHTML = "ТекстПисьмаФорматированныйДокументДобавитьШаблон" + ИмяКоманды;
		Кнопка = Форма.Элементы.Добавить(ИмяКнопкиHTML, Тип("КнопкаФормы"), ГруппаПараметры);
		Кнопка.Заголовок  = ПараметрТекста.Значение;
		Кнопка.ИмяКоманды = ИмяКоманды;
		
		Форма.Элементы.Переместить(Кнопка, ГруппаПараметры, Форма.Элементы.ТекстПисьмаФорматированныйДокументДобавитьШаблонПоУмолчанию);

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

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

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

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

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

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

	ЗаписьЖурнала(ПараметрыЖурнала,
		УровеньЖурналаРегистрации.Примечание,
		НСтр("ru = 'Повторная доставка на электронную почту.'"));

	ВыполнитьРассылку(ТаблицаОтчетов, ПараметрыДоставки, Рассылка, ПараметрыЖурнала);
	
КонецПроцедуры

Функция ВыполнитьФормированиеОтчетов(Отчеты, ПараметрыДоставки, НаименованиеРассылки, ПараметрыЖурнала) 

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

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

Функция ВыполнитьФормированиеОтчетовВОдномПотоке(Отчеты, ПараметрыДоставки, НаименованиеРассылки, ПараметрыЖурнала)

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

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

	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	РассылкиОтчетов.Ссылка КАК Ссылка
	|ИЗ
	|	Справочник.РассылкиОтчетов КАК РассылкиОтчетов
	|ГДЕ
	|	НЕ РассылкиОтчетов.ЭтоГруппа";

	РезультатЗапроса = Запрос.Выполнить();
	Если РезультатЗапроса.Пустой() Тогда
		Возврат;
	КонецЕсли;

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

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

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

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

	СетевойКаталогСервера = ПараметрыДоставки.СетевойКаталогWindows;

	Если ОбщегоНазначения.ЭтоLinuxСервер() Тогда
		СетевойКаталогСервера = ПараметрыДоставки.СетевойКаталогLinux;
	КонецЕсли;

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

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

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

			РегистрыСведений.ИсторияРассылкиОтчетов.ЗафиксироватьРезультатВыполненияРассылкиПолучателю(ПоляИстории);
		КонецЦикла;
	КонецЕсли;

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

Процедура ВыполнитьДоставкуНаFTPРесурс(ПараметрыЖурнала, ПараметрыДоставки, Вложения, Результат)

	РежимТестирования = ОбщегоНазначенияКлиентСервер.СвойствоСтруктуры(ПараметрыДоставки, "РежимТестирования", Ложь);

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

		Результат = Истина;
		ПараметрыДоставки.ВыполненаНаFTP = Истина;

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

			РегистрыСведений.ИсторияРассылкиОтчетов.ЗафиксироватьРезультатВыполненияРассылкиПолучателю(ПоляИстории);
		КонецЦикла;
	КонецЕсли;

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

Процедура ВыполнитьДоставкуВПапку(ПараметрыЖурнала, ПараметрыДоставки, Вложения, Результат)

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

	ВсеПолучатели = СформироватьМассивПолучателейРассылки(ПараметрыЖурнала.Данные, ПараметрыЖурнала);

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

	Исключение
		ДоставкаВыполнена = Ложь;
		ТекстСообщения = ШаблонСообщенияОбОшибке();
		ЗаписьЖурнала(ПараметрыЖурнала,, ТекстСообщения, ИнформацияОбОшибке());
	КонецПопытки;

	Если ПолучитьФункциональнуюОпцию("ХранитьИсториюРассылкиОтчетов") И ТипЗнч(ПараметрыЖурнала.Данные) = Тип(
		"СправочникСсылка.РассылкиОтчетов") Тогда

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

			РегистрыСведений.ИсторияРассылкиОтчетов.ЗафиксироватьРезультатВыполненияРассылкиПолучателю(
				ПоляИстории);
		КонецЦикла;
	КонецЕсли;

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

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

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

					РегистрыСведений.ИсторияРассылкиОтчетов.ЗафиксироватьРезультатВыполненияРассылкиПолучателю(
						ПоляИстории);
				КонецЦикла;

			КонецЦикла;
		КонецЕсли;
	КонецПопытки;
	
	Если ОтчетыОтправлены Тогда
		Если Не ПараметрыДоставки.ТолькоУведомить Тогда
			Результат = Истина;
		КонецЕсли;
		ПараметрыДоставки.ВыполненаПоЭлектроннойПочте = Результат;
    КонецЕсли;
	
КонецПроцедуры

Функция ШаблонСообщенияОбОшибке()
	Возврат НСтр("ru = 'Отчеты не доставлены'");
КонецФункции

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

#Область ФормированиеОтчетовВНесколькоПотоков

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

			ПараметрыМетода.Вставить(НомерПорции, МассивПараметров);

			Порция = Новый Массив;
			НомерПорции = НомерПорции + 1;
		КонецЕсли;
	КонецЦикла;

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

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

	Результат = Новый Структура;
	Результат.Вставить("ДеревоОтчетов", ДеревоОтчетов);
	Результат.Вставить("ОтчетыДляТекстаПисьма", ПараметрыДоставки.ОтчетыДляТекстаПисьма);
	
	Возврат Результат;
	
КонецФункции
// АПК:299-вкл, АПК:581-вкл

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

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

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

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

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