Как в блоке Исключение вывести наименование контрагента при ошибке отправки почты в 1С?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

При массовой рассылке электронных писем или пакетной обработке справочников разработчики часто сталкиваются с ситуацией, когда на одном из элементов возникает ошибка. Если использовать блок Попытка ... Исключение некорректно, можно потерять информацию о том, на какой именно записи произошел сбой. Разберем типичные ошибки при обращении к данным в блоке перехвата исключений и построим надежный алгоритм обработки с подробным логированием.

Анализ типовой ошибки обращения к метаданным

Рассмотрим частую ошибку начинающих разработчиков: попытку обратиться к менеджеру справочника внутри блока Исключение вида Справочники.Контрагенты.... Менеджер справочника предоставляет методы для работы со всем списком объектов (например, НайтиПоКоду, СоздатьЭлемент), но сам по себе не хранит контекст текущей итерации цикла.

Для вывода наименования контрагента, вызвавшего ошибку, необходимо использовать переменную, в которой сохранена ссылка или выборка текущей итерации цикла. Посмотрим на пример некорректного и корректного подходов.

Некорректный вариант:


Попытка
    Почта.Послать(Сообщение, ОбработкаТекстаИнтернетПочтовогоСообщения.НеОбрабатывать, ПротоколИнтернетПочты.SMTP);
Исключение
    // Ошибка: менеджер справочника не содержит данных конкретного элемента
    Сообщить(Справочники.Контрагенты.Наименование); 
КонецПопытки;

Корректный вариант:


Попытка
    Почта.Послать(Сообщение, ОбработкаТекстаИнтернетПочтовогоСообщения.НеОбрабатывать, ПротоколИнтернетПочты.SMTP);
Исключение
    // Обращаемся к переменной текущей итерации цикла
    Сообщить("Ошибка при отправке контрагенту: " + Строка(ТекущийКонтрагент) + ". Причина: " + ОписаниеОшибки());
КонецПопытки;

Пошаговый алгоритм обхода контрагентов и отправки почты

Разберем по шагам, как правильно организовать выборку данных, отправку писем и обработку исключений.

  1. Подготовка данных запросом. Не следует выполнять обращения через точку к реквизитам объектов в цикле, так как каждое такое обращение генерирует отдельный запрос к базе данных. Заранее выбираем все нужные поля: ссылку, наименование и адрес электронной почты.
  2. Инициализация почтового клиента. Подключение к SMTP-серверу с помощью метода ИнтернетПочта.Подключиться() должно происходить один раз до входа в цикл, чтобы избежать блокировок со стороны почтового сервера.
  3. Проверка корректности адреса перед отправкой. Предварительная валидация формата почтового ящика позволяет отсечь заведомо некорректные записи еще до отправки сетевого запроса.
  4. Обработка исключения с выводом контекста. В секции Исключение выводим наименование контрагента и текст системной ошибки через функцию ОписаниеОшибки() или ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()).

Пример правильной реализации пакетной отправки

Проанализируем готовый пример кода с оптимизированной выборкой и безопасной отправкой сообщений:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Контрагенты.Ссылка КАК Контрагент,
    |	Контрагенты.Наименование КАК Наименование,
    |	Контрагенты.ЭлектроннаяПочта КАК АдресПочты
    |ИЗ
    |	Справочник.Контрагенты КАК Контрагенты
    |ГДЕ
    |	Контрагенты.ЭлектроннаяПочта <> """"";

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

Почта = Новый ИнтернетПочта;
Профиль = Новый ИнтернетПочтовыйПрофиль;
// Заполнение параметров профиля (Сервер, Порт, Пользователь, Пароль)
// ...

Попытка
    Почта.Подключиться(Профиль, ПротоколИнтернетПочты.SMTP);
Исключение
    Сообщить("Не удалось подключиться к SMTP-серверу: " + ОписаниеОшибки());
    Возврат;
КонецПопытки;

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

Почта.Отключиться();

Возможные причины сбоев при отправке почты

Выясним основные причины, из-за которых метод Послать() может вызывать исключение при обработке конкретных контрагентов:

  1. Невидимые и спецсимволы в строке адреса. Если адрес был скопирован из внешних таблиц или документов, он может содержать пробелы, символы табуляции или перевода строки (Символы.ПС, Символы.ВК). Всегда используйте функцию СокрЛП() и очистку от непечатаемых символов.
  2. Кириллица в адресе или домене. Использование доменов в зоне .рф без предварительного преобразования в формат Punycode либо русские буквы в имени ящика приводят к отказу SMTP-сервера при выполнении команды RCPT TO.
  3. Несколько адресов в одном реквизите. Если в строке записано несколько email через запятую или точку с запятой, прямая передача всей строки в метод Получатели.Добавить() вызовет ошибку. Адреса необходимо предварительно разбивать на отдельные элементы.
  4. Ограничения и лимиты почтового сервера. При слишком частой отправке сообщений в цикле сервер может разорвать соединение по таймауту или заблокировать сессию из-за превышения лимита писем в минуту. В таких случаях в блоке Исключение можно предусмотреть повторную попытку вызова Подключиться().
← На главную