Загрузка данных


Привет! Ссылку не открывал, она не нужна: задача понятная, напишем свою обработку. Ниже полный код и порядок сборки.

Как это будет работать

Это дополнительная внешняя обработка для подсистемы «Дополнительные отчеты и обработки» (она есть в УНФ). В ней три части:

Форма настроек: тип цен, группа номенклатуры, учетная запись почты, получатели, тема, формат (xlsx/pdf).
Формирование прайса запросом к регистру цен и отправка письмом через стандартную почту БСП.
Серверные команды «Прайс 1/2/3». Для таких команд в карточке доп. обработки можно настроить расписание, то есть регламентное задание. Так получаются до трех разных рассылок с разными настройками.
Шаг 1. Создайте обработку в конфигураторе

Файл → Новый → Внешняя обработка. Имя: ПрайсЛистыПоРасписанию.

Реквизиты обработки:

Имя	Тип
НомерПрофиля	Число (1 знак)
ТипЦен	СправочникСсылка.ТипыЦен
Группа	СправочникСсылка.Номенклатура
УчетнаяЗапись	СправочникСсылка.УчетныеЗаписиЭлектроннойПочты
Получатели	Строка (500)
Тема	Строка (200)
ТекстПисьма	Строка (0, неограниченная)
Формат	Строка (4)

Добавьте форму Форма и сделайте ее основной.

Шаг 2. Модуль объекта

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

Функция СведенияОВнешнейОбработке() Экспорт

	ПараметрыРегистрации = ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке("2.2.2");
	ПараметрыРегистрации.Вид = ДополнительныеОтчетыИОбработкиКлиентСервер.ВидОбработкиДополнительнаяОбработка();
	ПараметрыРегистрации.Версия = "1.0";
	ПараметрыРегистрации.БезопасныйРежим = Ложь;
	ПараметрыРегистрации.Информация = "Отправка прайс-листов по e-mail по расписанию";

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

	// Команды для расписания (три независимых профиля)
	Для Номер = 1 По 3 Цикл
		Команда = ПараметрыРегистрации.Команды.Добавить();
		Команда.Представление = "Отправить прайс-лист (профиль " + Номер + ")";
		Команда.Идентификатор = "Прайс" + Номер;
		Команда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыВызовСерверногоМетода();
		Команда.ПоказыватьОповещение = Истина;
	КонецЦикла;

	Возврат ПараметрыРегистрации;

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

// Вызывается БСП при выполнении серверной команды (в т.ч. из регламентного задания).
Процедура ВыполнитьКоманду(ИдентификаторКоманды, ПараметрыВыполнения = Неопределено) Экспорт

	Номер = Число(СтрЗаменить(ИдентификаторКоманды, "Прайс", ""));
	ОтправитьПрайс(ПолучитьНастройки(Номер));

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

Функция ПолучитьНастройки(НомерПрофиля) Экспорт

	Результат = Новый Структура;
	Результат.Вставить("ТипЦен", Справочники.ТипыЦен.ПустаяСсылка());
	Результат.Вставить("Группа", Справочники.Номенклатура.ПустаяСсылка());
	Результат.Вставить("УчетнаяЗапись", Справочники.УчетныеЗаписиЭлектроннойПочты.ПустаяСсылка());
	Результат.Вставить("Получатели", "");
	Результат.Вставить("Тема", "Прайс-лист от [Дата]");
	Результат.Вставить("ТекстПисьма", "Добрый день! Актуальный прайс-лист во вложении.");
	Результат.Вставить("Формат", "xlsx");

	Сохраненные = ХранилищеОбщихНастроек.Загрузить("ПрайсЛистыПоРасписанию",
		"Профиль" + НомерПрофиля, , ИмяПользователяХранилища());

	Если ТипЗнч(Сохраненные) = Тип("Структура") Тогда
		ЗаполнитьЗначенияСвойств(Результат, Сохраненные);
	КонецЕсли;

	Возврат Результат;

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

Процедура СохранитьНастройки(НомерПрофиля, Настройки) Экспорт

	ХранилищеОбщихНастроек.Сохранить("ПрайсЛистыПоРасписанию",
		"Профиль" + НомерПрофиля, Настройки, , ИмяПользователяХранилища());

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

Процедура ОтправитьПрайс(Настройки) Экспорт

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

	Попытка
		ТабДок = СформироватьПрайс(Настройки);

		Если Настройки.Формат = "pdf" Тогда
			ТипФайла = ТипФайлаТабличногоДокумента.PDF;
			Расширение = "pdf";
		Иначе
			ТипФайла = ТипФайлаТабличногоДокумента.XLSX;
			Расширение = "xlsx";
		КонецЕсли;

		ДатаСтрока = Формат(ТекущаяДата(), "ДФ=dd.MM.yyyy");

		ВремФайл = ПолучитьИмяВременногоФайла(Расширение);
		ТабДок.Записать(ВремФайл, ТипФайла);
		Данные = Новый ДвоичныеДанные(ВремФайл);
		УдалитьФайлы(ВремФайл);
		Адрес = ПоместитьВоВременноеХранилище(Данные, Новый УникальныйИдентификатор);

		Вложение = Новый Структура;
		Вложение.Вставить("Представление", "Прайс-лист " + ДатаСтрока + "." + Расширение);
		Вложение.Вставить("АдресВоВременномХранилище", Адрес);
		Вложения = Новый Массив;
		Вложения.Добавить(Вложение);

		ПараметрыПисьма = Новый Структура;
		ПараметрыПисьма.Вставить("Кому", Настройки.Получатели);
		ПараметрыПисьма.Вставить("Тема", СтрЗаменить(Настройки.Тема, "[Дата]", ДатаСтрока));
		ПараметрыПисьма.Вставить("Тело", Настройки.ТекстПисьма);
		ПараметрыПисьма.Вставить("Вложения", Вложения);

		РаботаСПочтовымиСообщениями.ОтправитьПочтовоеСообщение(Настройки.УчетнаяЗапись, ПараметрыПисьма);

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

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

Функция СформироватьПрайс(Настройки) Экспорт

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

	Если ЗначениеЗаполнено(Настройки.Группа) Тогда
		Запрос.Текст = Запрос.Текст + "
		|	И Номенклатура.Ссылка В ИЕРАРХИИ(&Группа)";
		Запрос.УстановитьПараметр("Группа", Настройки.Группа);
	КонецЕсли;

	Запрос.Текст = Запрос.Текст + "
	|УПОРЯДОЧИТЬ ПО
	|	Группа,
	|	Наименование";

	Запрос.УстановитьПараметр("Дата", ТекущаяДата());
	Запрос.УстановитьПараметр("ТипЦен", Настройки.ТипЦен);

	Выборка = Запрос.Выполнить().Выбрать();

	ТабДок = Новый ТабличныйДокумент;
	ТабДок.ОриентацияСтраницы = ОриентацияСтраницы.Портрет;
	ТабДок.ПолеСлева = 10;
	ТабДок.ПолеСправа = 10;
	ТабДок.АвтоМасштаб = Истина;

	Строка = 1;
	ТабДок.Область(Строка, 1).Текст = "Прайс-лист на " + Формат(ТекущаяДата(), "ДФ=dd.MM.yyyy")
		+ " (" + Настройки.ТипЦен + ")";
	ТабДок.Область(Строка, 1).Шрифт = Новый Шрифт(, 12, Истина);
	Строка = Строка + 2;

	Заголовки = Новый Массив;
	Заголовки.Добавить("Артикул");
	Заголовки.Добавить("Наименование");
	Заголовки.Добавить("Характеристика");
	Заголовки.Добавить("Ед.");
	Заголовки.Добавить("Цена");

	Для Колонка = 1 По Заголовки.Количество() Цикл
		Ячейка = ТабДок.Область(Строка, Колонка);
		Ячейка.Текст = Заголовки[Колонка - 1];
		Ячейка.Шрифт = Новый Шрифт(, , Истина);
		Ячейка.ЦветФона = Новый Цвет(220, 220, 220);
		Ячейка.Обвести(Линия, Линия, Линия, Линия);
	КонецЦикла;
	ТабДок.ФиксацияСверху = Строка;
	Строка = Строка + 1;

	ТекущаяГруппа = Неопределено;

	Пока Выборка.Следующий() Цикл

		Если Выборка.Группа <> ТекущаяГруппа Тогда
			ТекущаяГруппа = Выборка.Группа;
			Область = ТабДок.Область(Строка, 1, Строка, 5);
			Область.Объединить();
			Область.Текст = ?(ПустаяСтрока(ТекущаяГруппа), "Без группы", ТекущаяГруппа);
			Область.Шрифт = Новый Шрифт(, , Истина);
			Область.ЦветФона = Новый Цвет(240, 240, 240);
			Строка = Строка + 1;
		КонецЕсли;

		ТабДок.Область(Строка, 1).Текст = Выборка.Артикул;
		ТабДок.Область(Строка, 2).Текст = Выборка.Наименование;
		ТабДок.Область(Строка, 3).Текст = Выборка.Характеристика;
		ТабДок.Область(Строка, 4).Текст = Выборка.Единица;
		ТабДок.Область(Строка, 5).Текст = Формат(Выборка.Цена, "ЧДЦ=2; ЧРГ=' '");
		ТабДок.Область(Строка, 5).ГоризонтальноеПоложение = ГоризонтальноеПоложение.Право;
		Строка = Строка + 1;

	КонецЦикла;

	ТабДок.Область(, 1, , 1).ШиринаКолонки = 15;
	ТабДок.Область(, 2, , 2).ШиринаКолонки = 55;
	ТабДок.Область(, 3, , 3).ШиринаКолонки = 20;
	ТабДок.Область(, 4, , 4).ШиринаКолонки = 6;
	ТабДок.Область(, 5, , 5).ШиринаКолонки = 14;

	Возврат ТабДок;

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

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

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

Функция ИмяПользователяХранилища()
	// Фиксированное имя, чтобы настройки читались и из регламентного задания
	Возврат "ПрайсЛистыПоРасписанию_Общие";
КонецФункции

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

В СформироватьПрайс переменная Линия не объявлена. Добавьте в начало функции:

bsl
Линия = Новый Линия(ТипЛинииЯчейкиТабличногоДокумента.Сплошная, 1);
Шаг 3. Форма

В форме разместите поля: Объект.НомерПрофиля (поле выбора со списком 1, 2, 3), Объект.ТипЦен, Объект.Группа, Объект.УчетнаяЗапись, Объект.Получатели, Объект.Тема, Объект.ТекстПисьма (многострочное), Объект.Формат (список «xlsx» и «pdf»). Добавьте две команды: Сохранить и ОтправитьСейчас. Для поля НомерПрофиля создайте обработчик ПриИзменении.

Модуль формы:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
	Объект.НомерПрофиля = 1;
	ЗагрузитьНастройкиНаСервере();
КонецПроцедуры

&НаКлиенте
Процедура НомерПрофиляПриИзменении(Элемент)
	ЗагрузитьНастройкиНаСервере();
КонецПроцедуры

&НаКлиенте
Процедура Сохранить(Команда)
	СохранитьНаСервере();
	ПоказатьОповещениеПользователя("Настройки сохранены");
КонецПроцедуры

&НаКлиенте
Процедура ОтправитьСейчас(Команда)
	СохранитьНаСервере();
	ОтправитьНаСервере();
	ПоказатьОповещениеПользователя("Прайс-лист отправлен");
КонецПроцедуры

&НаСервере
Функция НастройкиИзФормы()
	Результат = Новый Структура("ТипЦен, Группа, УчетнаяЗапись, Получатели, Тема, ТекстПисьма, Формат");
	ЗаполнитьЗначенияСвойств(Результат, Объект);
	Возврат Результат;
КонецФункции

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

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

&НаСервере
Процедура ОтправитьНаСервере()
	РеквизитФормыВЗначение("Объект").ОтправитьПрайс(НастройкиИзФормы());
КонецПроцедуры

Шаг 4. Подключение в УНФ
Сохраните обработку в файл .epf.
В УНФ: Администрирование → Печатные формы, отчеты и обработки → Дополнительные отчеты и обработки. Если пункта нет, включите флажок «Дополнительные отчеты и обработки».
Нажмите «Создать», выберите файл, подтвердите работу в небезопасном режиме (нужны права администратора).
В карточке на вкладке команд для «Отправить прайс-лист (профиль N)» нажмите Расписание и задайте время рассылки. Нужно, чтобы был выбран пользователь, от имени которого задание будет работать, и чтобы у него были права на почту.
Откройте «Настройка рассылки прайс-листов», заполните параметры и нажмите «Отправить сейчас» для теста.
Что может не совпасть с вашей конфигурацией

Я писал по памяти, а имена в разных релизах УНФ иногда отличаются. Проверьте в конфигураторе:

Справочник.ТипыЦен;
РегистрСведений.ЦеныНоменклатуры с измерениями ТипЦен, Номенклатура, Характеристика и ресурсом Цена;
Справочник.УчетныеЗаписиЭлектроннойПочты;
реквизиты номенклатуры Артикул и ЕдиницаИзмерения.

Если что-то отличается, исправьте имя в запросе. При ошибке пришлите мне текст, подправлю код.

В регистре цен в УНФ может быть флаг актуальности цены. Если он есть, добавьте в условие ГДЕ отбор по нему. Валюту можно взять из типа цен, добавив в запрос соответствующее поле.

Что можно добавить дальше: фильтр по остаткам на складе, отправка нескольким получателям с разными типами цен, архивирование вложения, прикрепление картинок. Скажите, что из этого нужно.

Всё делается в Конфигураторе вашей базы УНФ. Саму конфигурацию вы не меняете: обработка получается отдельным файлом .epf, поэтому УНФ она не сломает. Если хочется подстраховаться, перед началом скопируйте базу.

Часть 1. Открываем Конфигуратор
Запустите 1С, выберите вашу базу УНФ и нажмите кнопку «Конфигуратор», не «1С:Предприятие».
Обработку нужно создавать именно здесь. Тогда Конфигуратор знает справочники вашей УНФ и не ругается на Справочники.ТипыЦен и подобное.
Часть 2. Создаём обработку
Меню Файл → Новый… → Внешняя обработка.
Слева появится дерево с названием «Обработка1». Выделите его и нажмите Alt+Enter, откроется окно свойств.
В поле Имя впишите ПрайсЛистыПоРасписанию.
Часть 3. Добавляем реквизиты
В дереве слева раскройте обработку, щёлкните правой кнопкой по «Реквизиты» → «Добавить».
В свойствах впишите имя и выберите тип в поле «Тип» (кнопка с тремя точками). Всего нужно добавить 8 реквизитов:
Имя	Тип	Как выбрать
НомерПрофиля	Число	длина 1, точность 0
ТипЦен	СправочникСсылка.ТипыЦен	раскройте «СправочникСсылка», поставьте галочку
Группа	СправочникСсылка.Номенклатура	так же
УчетнаяЗапись	СправочникСсылка.УчетныеЗаписиЭлектроннойПочты	так же
Получатели	Строка	длина 500
Тема	Строка	длина 200
ТекстПисьма	Строка	длина 0 (без ограничения)
Формат	Строка	длина 4

Если какого-то справочника нет в списке типов, назовите мне его. Скорее всего, он называется чуть иначе в вашем релизе, и я поправлю код.

Часть 4. Вставляем код обработки
Правой кнопкой по самой обработке в дереве → «Открыть модуль объекта».
Вставьте весь код из блока «Шаг 2. Модуль объекта» моего прошлого сообщения.
Найдите строку Функция СформироватьПрайс(Настройки) Экспорт и сразу под ней добавьте строку:
bsl
Линия = Новый Линия(ТипЛинииЯчейкиТабличногоДокумента.Сплошная, 1);
Нажмите Ctrl+F7, это проверка синтаксиса. Если выдаст ошибку, скопируйте её текст и пришлите мне.
Часть 5. Создаём форму
В дереве правой кнопкой по «Формы» → «Добавить».
В мастере оставьте всё по умолчанию. Имя должно быть именно Форма. Нажмите «Готово».
Откроется редактор формы. Слева сверху окно «Элементы», снизу окно «Реквизиты». Раскройте в нём «Объект», там будут ваши 8 реквизитов.
Перетащите мышкой из «Реквизиты» в «Элементы» эти 8 штук: НомерПрофиля, ТипЦен, Группа, УчетнаяЗапись, Получатели, Тема, ТекстПисьма, Формат.
Настройте поля. Щёлкните по полю в «Элементы», свойства откроются справа или по Alt+Enter:
НомерПрофиля: найдите «Список выбора», добавьте значения 1, 2, 3 и включите «Режим выбора из списка».
Формат: то же самое, значения xlsx и pdf.
ТекстПисьма: включите «Многострочный режим».
Событие для НомерПрофиля: в его свойствах откройте вкладку «События», у «ПриИзменении» нажмите значок лупы и выберите «Создать на клиенте». Откроется модуль формы. Пока просто закройте его.
Команды: вкладка «Команды» в редакторе формы, нажмите «Добавить» (зелёный плюс) и создайте две команды:
имя Сохранить, заголовок «Сохранить настройки»;
имя ОтправитьСейчас, заголовок «Отправить сейчас».
У каждой нажмите на лупу у поля «Действие» → «Создать на клиенте». Затем перетащите обе команды в окно «Элементы», чтобы появились кнопки.
Откройте модуль формы: правой кнопкой по форме в дереве → «Открыть модуль» (или вкладка «Модуль» внизу редактора). Удалите всё, что там есть, и вставьте код из «Шаг 3. Модуль формы» из прошлого сообщения.
Снова Ctrl+F7.
Часть 6. Сохраняем файл

Файл → Сохранить, выберите папку и имя, например ПрайсЛисты.epf.

Часть 7. Подключаем к УНФ
Закройте Конфигуратор и запустите базу в режиме «1С:Предприятие» под администратором.
Перейдите в Администрирование → Печатные формы, отчёты и обработки → Дополнительные отчёты и обработки. Если не найдёте, ищите по слову «Дополнительные» в поиске по меню. Если раздел скрыт, включите там галочку «Дополнительные отчёты и обработки».
Нажмите «Создать», выберите ваш .epf и нажмите «Выполнить». На вопрос о небезопасном режиме ответьте «Продолжить».
В карточке обработки сохраните её.
Часть 8. Настраиваем и тестируем
Почта должна быть настроена заранее. В УНФ в разделе настроек электронной почты должна существовать рабочая учётная запись (с адресом, SMTP-сервером и паролем). Если её нет, сначала создайте.
В карточке обработки откройте команду «Настройка рассылки прайс-листов». Выберите профиль 1, тип цен, учётную запись, впишите получателей (несколько адресов через ;) и нажмите «Сохранить настройки».
Нажмите «Отправить сейчас». Письмо с прайсом должно прийти получателю. Это тест.
Когда тест прошёл, вернитесь в карточку обработки, на вкладку команд. Выберите строку «Отправить прайс-лист (профиль 1)» и нажмите кнопку «Расписание» над таблицей. Задайте время, например «по будням в 8:00», и сохраните.
Важное про расписание
Если у вас клиент-серверная база (SQL или PostgreSQL), регламентные задания работают сами.
Если у вас файловая база (просто папка с файлом 1Cv8.1CD), задания выполняются только пока на одном из компьютеров открыта 1С с включённой обработкой регламентных заданий. Если так, скажите мне, и я объясню, как это настроить.
Если что-то не работает
Что видите	Что делать
Ошибка в запросе, «поле не найдено»	Пришлите текст ошибки, поправлю имена
Письмо не уходит	Проверьте, что в УНФ тестовое письмо через эту учётную запись отправляется
Нет кнопки «Расписание»	Проверьте, что у команды тип «Вызов серверного метода» и обработка включена
Пустой прайс	Проверьте выбранный тип цен и что цены у номенклатуры заполнены

Если застрянете на любом шаге, напишите номер шага и что именно на экране, помогу.