Вводные:
1. Нужна рассылка уведомлений клиентам о заказах
2. Конфигурация УТ 11.4
3. Отсутствие умения верстать HTML письма.
4. Сервис отправки выбран потому что у него есть:
4.1 нормальная документация на HTTP API https://sendpulse.com/ru/integrations/api/smtp
4.2 адекватная тех поддержка
4.3 бесплатный пакет (ограничение отправка со списка email, отправка с домена по платным подпискам)
Реализация:
Выбрал вариант реализации в виде расширения в котором:
1. Добавил справочник с настройками подсистемы, чтобы в коде настройки не фиксировать, для работы с сервисом беру первую не помеченную на удаление запись.
1.1 Настройка доступа на стороне SendPulse
"Необходимые для получения ключа параметры можно найти в настройках аккаунта, во вкладке API."
2. Добавил перечисление ТипыОтправлений и регистр сведений СоответствиеМакетов, для фиксации настроек компоновки для каждого типа отправлений, макеты формируются на стороне сервиса и в них прописываются нужные параметры.
1.2 Настройка макетов на стороне SendPulse
Макеты создаются по документации https://sendpulse.com/ru/knowledge-base/email-service/email-create/create-own-template
Получение ID макета и использование переменных.
ID макета можно получить из адресной строки при редактировании сохраненного макета.
Переменные обозначаются "{{}}".
3. Для хранения очередью добавил перечисление СостояниеОтправлений и Регистр сведений ОчередьОтправкиУведомлений
Формирование очереди:
Доработал процедуру "При записи" в документе "Заказ клиента" и РС "СостоянияЗаказовКлиентов"
Процедура в РС СостоянияЗаказовКлиентов
&После("ПриЗаписи")
Процедура spe_ПриЗаписи(Отказ, Замещение)
Для каждого Запись Из ЭтотОбъект Цикл
Если Запись.ПроцентОплаты = 100 Тогда
РегистрыСведений.spe_ОчередьОтправкиУведомлений.СоздатьЗаписьУведомления(Запись.Заказ, Перечисления.spe_ТипыОтправлений.ОплатаПоступилаПолностью);
ИначеЕсли Запись.СуммаОплаты > 0 Тогда
РегистрыСведений.spe_ОчередьОтправкиУведомлений.СоздатьЗаписьУведомления(Запись.Заказ, Перечисления.spe_ТипыОтправлений.ОплатаПоступила);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Реализация обработки изменения заказа зависит от специфики работы компании.
Отправка очереди:
Запросом выбираю все записи с состоянием "создано" и обрабатываю:
Процедура ОтправитьСообщения()
РезультатЗапроса = ПолучитьРезультатЗапросаОчереди();
Если РезультатЗапроса.Пустой() Тогда
Возврат
Иначе;
ПройтиАутентификацию();
КонецЕсли;
Если Отказ Тогда Возврат; КонецЕсли;
Выборка = РезультатЗапроса.Выбрать();
ОтправитьИзОчереди(Выборка);
КонецПроцедуры
Аутентификация на сервере:
Для отправки рабочих сообщений используется Bearer токен. Токен храню в переменной модуля.
Процедура ПройтиАутентификацию()
СоответствиеДанные = Новый Соответствие;
СоответствиеДанные.Вставить("grant_type", "client_credentials");
СоответствиеДанные.Вставить("client_id", СтруктураНастроек.client_id);
СоответствиеДанные.Вставить("client_secret", СтруктураНастроек.client_secret);
СтрокаДанные = В_JSON(СоответствиеДанные);
HTTPОтвет = POST("/oauth/access_token", СтрокаДанные);
Если HTTPОтвет.КодСостояния = 200 Тогда
СтрокаОтвет = HTTPОтвет.ПолучитьТелоКакСтроку();
СоответствиеОтвет = ИЗ_JSON(СтрокаОтвет);
Токен = "Bearer " + СоответствиеОтвет.Получить("access_token");
Иначе
Отказ = Истина;
КонецЕсли;
КонецПроцедуры
Формирование сообщения:
Документация по структуре данных https://sendpulse.com/ru/integrations/api/smtp
Процедура ОтправитьИзОчереди(Выборка)
Пока Выборка.Следующий() Цикл
Отказ = Ложь;
Если Выборка.ИДМакета = 0 Тогда
ЗаписатьОчередьОтправкиУведомлений(Выборка, Перечисления.spe_СостоянияОтправлений.Ошибка, "ИДМакета не заполнена");
Продолжить;
КонецЕсли;
Если НачалоДня(Выборка.ДатаДобавления) < НачалоДня(ТекущаяДата() - СтруктураНастроек.КоличествоДнейАктуальностиПисьма * 24*60*60) Тогда
ЗаписатьОчередьОтправкиУведомлений(Выборка, Перечисления.spe_СостоянияОтправлений.Ошибка, "Письмо не актуально");
Продолжить;
КонецЕсли;
СоответствиеПисьмо = Новый Соответствие;
СоответствиеПисьмо.Вставить("subject", ПолучитьТемуПисьма(Выборка));
СоответствиеПисьмо.Вставить("from", ПолучитьСоответствиеОтправителя(Выборка));
СоответствиеПисьмо.Вставить("to", ПолучитьМассивПолучателей(Выборка, СоответствиеПисьмо));
СоответствиеПисьмо.Вставить("template", ПолучитьСоответствиеМакет(Выборка));
СоответствиеПисьмо.Вставить("attachments_binary", ПолучитьМассивВложений(Выборка));
СоответствиеДанные = Новый Соответствие;
СоответствиеДанные.Вставить("email", СоответствиеПисьмо);
Если Отказ Тогда Продолжить КонецЕсли;
Попытка
ОтправитьДанные(СоответствиеДанные, Выборка);
Исключение
ЗаписатьОчередьОтправкиУведомлений(Выборка, Перечисления.spe_СостоянияОтправлений.Ошибка, ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
КонецПроцедуры
Функция ПолучитьСоответствиеМакет(Выборка)
Если Отказ Тогда Возврат "" КонецЕсли;
ТипОтправления = Выборка.ТипОтправления;
ЗаказКлиента = Выборка.ЗаказКлиента;
СоответствиеМакет = Новый Соответствие;
СоответствиеМакет.Вставить("id", Выборка.ИДМакета);
СоответствиеМакет.Вставить("variables", ПолучитьПараметрыМакета(Выборка));
Возврат СоответствиеМакет;
КонецФункции
В общем формирую соответствие, которое сериализую в JSON и отправляю на сервер. Заполнение отправителя, получателя зависит от конфигурации и специфики работы компании.
Вложения реализовываются через Base64 строку.
Код реализации вложения для УТ 11.4
Функция ПолучитьМассивВложений(Выборка)
Если Отказ Тогда Возврат "" КонецЕсли;
СоответствиеВложения = Новый Соответствие;
Если Выборка.ПечатнаяФормаСчет Тогда
МассивОбъектов = Новый Массив;
МассивОбъектов.Добавить(Выборка.ЗаказКлиента);
ДополнительныеПараметры = Новый Структура;
ДополнительныеПараметры.Вставить("ДополнитьКомплектВнешнимиПечатнымиФормами", Ложь);
ПечатныеФормы = УправлениеПечатью.СформироватьПечатныеФормы("Обработка.ПечатьСчетовНаОплату", "СчетНаОплату",
МассивОбъектов, ДополнительныеПараметры, Неопределено);
ТабДок = ПечатныеФормы.КоллекцияПечатныхФорм[0].ТабличныйДокумент;
ВремФайл = ПолучитьИмяВременногоФайла("pdf");
ТабДок.Записать(ВремФайл,ТипФайлаТабличногоДокумента.PDF);
ДД = Новый ДвоичныеДанные(ВремФайл);
Строка64 = Base64Строка(ДД);
СоответствиеВложения.Вставить("Счет на оплату № " + Выборка.ЗаказКлиентаНомер + " от " + Формат(Выборка.ЗаказКлиентаДата,"ДФ=dd.MM.yyyy") + ".pdf", Строка64);
КонецЕсли;
Возврат СоответствиеВложения;
КонецФункции
Отправка данных:
Сериализую соответствие данных и отправляю POST запросом, проверяю на коды ошибок и по результату делаю запись в очередь отправки.
Код реализации отправки данных
Функция ОтправитьДанные(СоответствиеДанные, Выборка)
СтрокаДанные = В_JSON(СоответствиеДанные);
HTTPОтвет = POST("/smtp/emails", СтрокаДанные);
Если HTTPОтвет.КодСостояния = 200 Тогда
ЗаписатьОчередьОтправкиУведомлений(Выборка, Перечисления.spe_СостоянияОтправлений.Отправлено);
Иначе
ЗаписатьОчередьОтправкиУведомлений(Выборка, Перечисления.spe_СостоянияОтправлений.Ошибка, HTTPОтвет.ПолучитьТелоКакСтроку());
КонецЕсли;
КонецФункции
Функция POST(АдресРесурса, СтрокаДанные)
HTTPСтруктура = ПолучитьHTTPСтруктура(АдресРесурса);
HTTPЗапрос = HTTPСтруктура.HTTPЗапрос;
HTTPСоединение = HTTPСтруктура.HTTPСоединение;
HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаДанные);
Попытка
HTTPОтвет = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
Исключение
СообщитьОбОшибке(ИмяОбмена, ОписаниеОшибки());
Отказ = Истина;
HTTPОтвет = Новый Структура;
HTTPОтвет.Вставить("КодСостояния", 500);
КонецПопытки;
ПроверитьНаКодыОшибок(HTTPОтвет, "POST " + АдресРесурса);
Возврат HTTPОтвет;
КонецФункции
Функция ПолучитьHTTPСтруктура(АдресРесурса)
ssl4 = Новый ЗащищенноеСоединениеOpenSSL(Неопределено, Неопределено);
HTTPСоединение = Новый HTTPСоединение("api.sendpulse.com",,,,,,ssl4);
Заголовки = Новый Соответствие;
Заголовки.Вставить("Content-type", "application/json");
Заголовки.Вставить("Authorization", Токен);
HTTPЗапрос = Новый HTTPЗапрос(АдресРесурса, Заголовки);
HTTPСтруктура = Новый Структура;
HTTPСтруктура.Вставить("HTTPСоединение", HTTPСоединение);
HTTPСтруктура.Вставить("HTTPЗапрос", HTTPЗапрос);
Возврат HTTPСтруктура;
КонецФункции
Процедура ПроверитьНаКодыОшибок(HTTPОтвет, ОписаниеЗапроса)
КодСостояния = HTTPОтвет.КодСостояния;
Если КодСостояния = 301
ИЛИ КодСостояния = 302
ИЛИ КодСостояния = 501
ИЛИ КодСостояния = 401
ИЛИ КодСостояния = 500
Тогда
СообщитьОбОшибке(ИмяОбмена, ОписаниеЗапроса + Символы.ПС + "КодСостояния: " + КодСостояния);
Отказ = Истина;
ИначеЕсли КодСостояния = 400 Тогда
Если СтруктураНастроек.Тест Тогда
СообщитьОбОшибке(ИмяОбмена, ОписаниеЗапроса + Символы.ПС + "КодСостояния: " + КодСостояния + Символы.ПС + HTTPОтвет.ПолучитьТелоКакСтроку());
Иначе
СообщитьОбОшибке(ИмяОбмена, ОписаниеЗапроса + Символы.ПС + "КодСостояния: " + КодСостояния);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Процедура ОтправитьИзОчереди(Выборка)
Пока Выборка.Следующий() Цикл
Отказ = Ложь;
Если Выборка.ИДМакета = 0 Тогда
ЗаписатьОчередьОтправкиУведомлений(Выборка, Перечисления.spe_СостоянияОтправлений.Ошибка, "ИДМакета не заполнена");
Продолжить;
КонецЕсли;
Если НачалоДня(Выборка.ДатаДобавления) < НачалоДня(ТекущаяДата() - СтруктураНастроек.КоличествоДнейАктуальностиПисьма * 24*60*60) Тогда
ЗаписатьОчередьОтправкиУведомлений(Выборка, Перечисления.spe_СостоянияОтправлений.Ошибка, "Письмо не актуально");
Продолжить;
КонецЕсли;
СоответствиеПисьмо = Новый Соответствие;
СоответствиеПисьмо.Вставить("subject", ПолучитьТемуПисьма(Выборка));
СоответствиеПисьмо.Вставить("from", ПолучитьСоответствиеОтправителя(Выборка));
СоответствиеПисьмо.Вставить("to", ПолучитьМассивПолучателей(Выборка, СоответствиеПисьмо));
СоответствиеПисьмо.Вставить("template", ПолучитьСоответствиеМакет(Выборка));
СоответствиеПисьмо.Вставить("attachments_binary", ПолучитьМассивВложений(Выборка));
СоответствиеДанные = Новый Соответствие;
СоответствиеДанные.Вставить("email", СоответствиеПисьмо);
Если Отказ Тогда Продолжить КонецЕсли;
Попытка
ОтправитьДанные(СоответствиеДанные, Выборка);
Исключение
ЗаписатьОчередьОтправкиУведомлений(Выборка, Перечисления.spe_СостоянияОтправлений.Ошибка, ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
КонецПроцедуры
Протухшие записи:
Бывают ситуации что очередь не отправляется по техническим причинам, а отправлять неактуальные данные не нужно.
Если дата создания записи очереди меньше чем ТекущаяДата() — КоличествоДнейАктуальностиПисьма то помечаю её как ошибочную.
Отладка:
Для отладки использую параметр настройки "Тест" при которой письма отправляются отправителю и выводятся сообщения о ошибках HTTP запросов.
Обработка ошибок:
При ошибках формирования письма и отправки обновляю запись очереди сообщений устанавливая состояние "Ошибка" и записываю в реквизит "СообщениеОбОшибке"
Подрезка очереди:
Использую параметр настроек "КоличествоДнейИстории" и Удаляю все записи с датой создания меньше ТекущаядДата() — КоличествоДнейИстории
Процедура ПодрезатьОчередь()
КонецПериода = НачалоДня(ТекущаяДата() - СтруктураНастроек.КоличествоДнейИстории * 24*60*60);
НаборЗаписей = РегистрыСведений.spe_ОчередьОтправкиУведомлений.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.ДатаДобавления.Использование = Истина;
НаборЗаписей.Отбор.ДатаДобавления.ЗначениеС = Дата(1,1,1);
НаборЗаписей.Отбор.ДатаДобавления.ЗначениеПо = КонецПериода;
НаборЗаписей.Прочитать();
НаборЗаписей.Очистить();
НаборЗаписей.Записать(Истина);
КонецПроцедуры
Автоматический запуск:
Реализовано в виде внешней обработки (как формировать регламентные задания кодом не нашел, с обработкой реализуется просто, работает стабильно).
Процедура ВыполнитьКоманду(ИмяКоманды, ПараметрыВыполнения) Экспорт
Если ИмяКоманды = "ОтправитьСообщения" Тогда
обОбработка = Обработки.spe_ОтправкаСообщенийСендПульс.Создать();
обОбработка.ВыполнитьКоманду("ОтправитьСообщения", Неопределено);
ИначеЕсли ИмяКоманды = "ПодрезатьОчередь" Тогда
обОбработка = Обработки.spe_ОтправкаСообщенийСендПульс.Создать();
обОбработка.ВыполнитьКоманду("ПодрезатьОчередь", Неопределено);
КонецЕсли;
КонецПроцедуры
Итог:
1. Работающие уведомления по email, которых хватает для базовой работы отдела продаж.
2. Клиент может изменять макеты по своему усмотрению.
3. Добавление новых типов сообщений и формирования параметров под них относительно просто.
4. История отправки хранится в личном кабинете сервиса, разбор ситуации с отсутствием доставки решаем.
В реализации использовались наработки из //infostart.ru/public/717412/
Благодарю за внимание.
Замер скорости отправки через http и через ИнтернетПочта делали ?
(1) Замеры по скорости не делал. Для текущей инсталляции хватает. Если без вложений запрос маленький и обрабатывается относительно быстро.
Решал вопрос про перенос верстки на сторонний сервис, дополнительно и аналитику получаем по просмотрам сообщений и переходам.
ИнтернетПочта по SMTP отправляет, с ним вариант воспользоваться сервисом шаблонов с него взять верстку скомпоновать локально и отправить по SMTP, но нагрузка на сервер 1С и трафик с него будет больше.
Здравствуйте
Отличный разбор.
можно глянуть код функции
спасибо
(3) Добавил в статью раздел «Отправка данных»
(4) Благодарю