При организации автоматических рассылок из среды 1С:Предприятие разработчики часто сталкиваются с неприятной ситуацией. Нам необходимо отправить одно и то же письмо группе адресатов (например, 20 сотрудникам или клиентам). Мы подготавливаем одно почтовое сообщение, заполняем список получателей и вызываем метод Послать() объекта ИнтернетПочта. Однако, если среди этих 20 адресов окажется хотя бы один несуществующий, удаленный или временно заблокированный почтовый ящик, весь процесс отправки прерывается аварийно с ошибкой «SMTP error code 550, Mailbox unavailable». В итоге письмо не доходит ни до одного из получателей, даже если остальные 19 адресов абсолютно корректны.
В этой статье мы подробно проанализируем причины такого поведения системы, рассмотрим ограничения старых версий платформы 1С, изучим штатные инструменты современных релизов, а также разберем несколько практических способов обхода этой проблемы.
Для того чтобы понять, почему возникает эта ошибка, нам необходимо обратиться к принципам работы сетевого протокола SMTP. Когда 1С отправляет письмо группе адресатов, взаимодействие с почтовым сервером происходит в рамках одной SMTP-сессии. Платформа последовательно передает серверу команды RCPT TO для каждого получателя из списка.
Почтовый сервер получателя (или ваш исходящий SMTP-сервер) во время этой сессии проверяет статус каждого адреса. Если сервер обнаруживает заблокированный или несуществующий почтовый ящик, он мгновенно возвращает стандартный код ответа об ошибке: 550 Mailbox unavailable (или User unknown).
В устаревших версиях платформы 1С:Предприятие (включая ветку 8.2 и ранние релизы 8.3) заложена жесткая логика: любая ошибка SMTP-сервера в процессе отправки генерирует исключение (ошибку времени выполнения) в коде 1С. Из-за этого метод Послать() прерывает работу, сессия закрывается, и отправка письма блокируется для всей группы получателей.
Давайте рассмотрим детально все возможные варианты решения этой проблемы — от низкоуровневых изменений в кодовой базе до административных настроек почтовой инфраструктуры.
Фирма «1С» со временем переработала этот механизм. Начиная с версии платформы 1С:Предприятие 8.3.9, поведение объекта ИнтернетПочта кардинально изменилось. Теперь частичная недоступность адресатов больше не вызывает аварийного завершения работы метода Послать().
Проанализируем новые особенности работы платформы:
Послать() возвращает объект типа Соответствие. Если все письма ушли успешно, возвращается пустое соответствие. Если возникли ошибки, то ключом в возвращаемом соответствии будет адрес недоступного получателя, а значением — строка с текстом ошибки (например, «SMTP error code 550»).Важное условие: Данное поведение работает только в том случае, если у вашей конфигурации Режим совместимости установлен в значение 8.3.9 или выше. Если оставить режим совместимости со старыми версиями (например, 8.3.8), платформа продолжит вызывать исключение.
Рассмотрим пример кода, реализующий обработку результатов отправки по новым правилам:
Профиль = Новый ИнтернетПочтовыйПрофиль;
// Заполняем параметры подключения к SMTP-серверу...
Профиль.АдресСервераSMTP = "smtp.yandex.ru";
Профиль.ПортSMTP = 465;
Профиль.ПользовательSMTP = "robot@domain.ru";
Профиль.ПарольSMTP = "MyPassword";
Профиль.ИспользоватьSSLSMTP = Истина;
Почта = Новый ИнтернетПочта;
Письмо = Новый ИнтернетПочтовоеСообщение;
Письмо.Отправитель.Адрес = "robot@domain.ru";
// Добавляем список получателей, включая заведомо некорректный
Письмо.Получатели.Добавить("valid_user1@domain.ru");
Письмо.Получатели.Добавить("blocked_user@domain.ru"); // Вызовет ошибку 550
Письмо.Получатели.Добавить("valid_user2@domain.ru");
Письмо.Тема = "Тестовая рассылка";
Письмо.Тексты.Добавить("Текст важного уведомления для всей группы.");
Попытка
Почта.Подключиться(Профиль);
// На платформе 8.3.9+ метод возвращает Соответствие проблемных адресов
ОшибкиОтправки = Почта.Послать(Письмо);
Почта.Отключиться();
Если ТипЗнч(ОшибкиОтправки) = Тип("Соответствие") И ОшибкиОтправки.Количество() > 0 Тогда
Для Каждого Ошибка Из ОшибкиОтправки Цикл
ЗаписьЖурналаРегистрации("ОтправкаПочты",
УровеньЖурналаРегистрации.Предупреждение,
,
,
"Не удалось отправить письмо на адрес: " + Ошибка.Ключ + ". Причина: " + Ошибка.Значение);
КонецЦикла;
Сообщить("Письмо было отправлено частично. Детали записаны в журнал регистрации.");
Иначе
Сообщить("Письмо успешно отправлено всем получателям!");
КонецЕсли;
Исключение
// Сюда мы попадем только при глобальных ошибках авторизации или связи с сервером
Предупреждение("Критическая ошибка при работе с почтой: " + ОписаниеОшибки());
КонецПопытки;
Если вы разрабатываете решение на базе современных типовых конфигураций 1С (таких как ERP, УТ, КА, БП), в вашем распоряжении находится Библиотека стандартных подсистем (БСП). Разработчики БСП уже предусмотрели обертку над платформенными механизмами отправки.
Вместо прямого использования объекта ИнтернетПочта рекомендуется вызывать программный интерфейс общего модуля РаботаСПочтовымиСообщениями. Функция РаботаСПочтовымиСообщениями.ОтправитьПочтовоеСообщение() самостоятельно анализирует платформенный ответ, фиксирует ошибочных адресатов, исключает их из последующих итераций и корректно логирует информацию в системных регистрах, не прерывая выполнение основного бизнес-процесса.
Если вы вынуждены работать на старых версиях платформы (например, 1С 8.2), где обновить платформу или отключить режим совместимости невозможно, самым надежным программным решением в коде 1С является отказ от отправки одного письма всему списку получателей одновременно. Вместо этого мы организуем цикл и отправляем персональное письмо каждому адресату.
Давайте разберем по шагам, как правильно реализовать этот алгоритм, чтобы не перегружать почтовое соединение частыми циклами авторизации:
Подключиться().ИнтернетПочтовоеСообщение, добавляем в него ровно одного получателя.Послать() внутрь блока Попытка ... Исключение. В случае неудачи фиксируем ошибку для конкретного адресата и продолжаем выполнение цикла.Отключиться().Посмотрим на пример кода:
Профиль = ПолучитьПрофильПодключения(); // Функция инициализации почтового профиля
Почта = Новый ИнтернетПочта;
МассивАдресатов = Новый Массив;
МассивАдресатов.Добавить("user1@domain.ru");
МассивАдресатов.Добавить("blocked@domain.ru");
МассивАдресатов.Добавить("user2@domain.ru");
Попытка
Почта.Подключиться(Профиль);
Исключение
Сообщить("Не удалось установить соединение с сервером: " + ОписаниеОшибки());
Возврат;
КонецПопытки;
Для Каждого Адрес Из МассивАдресатов Цикл
Письмо = Новый ИнтернетПочтовоеСообщение;
Письмо.Отправитель.Адрес = "robot@domain.ru";
Письмо.Получатели.Добавить(Адрес);
Письмо.Тема = "Индивидуальное уведомление";
Письмо.Тексты.Добавить("Уважаемый пользователь, это сообщение сформировано автоматически.");
Попытка
Почта.Послать(Письмо);
Сообщить("Успешно отправлено на адрес: " + Адрес);
Исключение
// Ловим ошибку 550 конкретного адресата, не прерывая рассылку остальным
Сообщить("Ошибка отправки на адрес " + Адрес + ": " + ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
Почта.Отключиться();
Преимущества: Полная независимость от версии платформы 1С и настроек сторонних почтовых серверов. Доставка гарантирована всем существующим адресатам.
Недостатки: Возрастает нагрузка на почтовый сервер и трафик, так как для каждого получателя отправляется отдельный экземпляр письма, вместо передачи одного письма с перечислением адресов.
Если вам критически важно отправлять именно одно письмо, а не плодить дубликаты для каждого получателя, перенесите логику группировки адресатов на сторону почтового сервера. Для этого используется механизм Почтовых групп (списков рассылки).
Рассмотрим этот подход подробнее:
all_managers@domain.ru.all_managers@domain.ru.Для крупных инфраструктурных решений оптимальным выходом будет администрирование промежуточного SMTP-сервера. Вы можете развернуть локальный почтовый релей (например, IIS SMTP, Postfix, Exim) на том же сервере, где функционирует сервер приложений 1С.
В таком сценарии 1С отправляет почту на локальный адрес localhost. Локальный SMTP-сервер настроен так, чтобы моментально принимать абсолютно любые письма от 1С (всегда отвечая кодом 250 OK). Метод Послать() в 1С будет отрабатывать за доли секунды и всегда без ошибок. После приема писем локальный релей самостоятельно занимается их доставкой внешним адресатам в асинхронном режиме. В случае недоступности какого-либо ящика релей сформирует стандартный технический отчет о недоставке (NDR — Non-Delivery Report) и отправит его на ящик отправителя.
В операционных системах Windows для отправки почты можно использовать системную библиотеку CDO (Collaboration Data Objects). Преимущество этого COM-объекта заключается в более гибком взаимодействии со службами SMTP по сравнению со старым объектом ИнтернетПочта в 1С 8.2.
Проанализируем пример кода с использованием CDO.Message:
// Создаем конфигурацию подключения
CDO_Config = Новый COMОбъект("CDO.Configuration");
Fields = CDO_Config.Fields;
// Задаем параметры SMTP-сервера
Fields.Item("http://schemas.microsoft.com/cdo/configuration/sendusing").Value = 2; // Порт отправки
Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserver").Value = "smtp.yourserver.ru";
Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport").Value = 25;
Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate").Value = 1; // Авторизация
Fields.Item("http://schemas.microsoft.com/cdo/configuration/sendusername").Value = "user@yourserver.ru";
Fields.Item("http://schemas.microsoft.com/cdo/configuration/sendpassword").Value = "password";
Fields.Update();
// Создаем сообщение
CDO_Message = Новый COMОбъект("CDO.Message");
CDO_Message.Configuration = CDO_Config;
CDO_Message.From = "user@yourserver.ru";
// Задаем список получателей через запятую или точку с запятой
CDO_Message.To = "active_user1@domain.ru; blocked_user@domain.ru; active_user2@domain.ru";
CDO_Message.Subject = "Проверка отправки через CDO";
CDO_Message.TextBody = "Текст сообщения, отправленный в обход стандартной ИнтернетПочты.";
Попытка
CDO_Message.Send();
Сообщить("Запрос на отправку успешно передан службе CDO!");
Исключение
Сообщить("Ошибка отправки через CDO: " + ОписаниеОшибки());
КонецПопытки;
Этот способ позволяет обойти жесткие ограничения старых платформ 1С на обработку сетевых исключений SMTP-протокола, перекладывая управление сессией на системную библиотеку Windows.
Мы подробно разобрали природу возникновения ошибки 550 Mailbox unavailable при рассылках из 1С. Подводя итог, дадим несколько практических рекомендаций по выбору оптимального решения:
ИнтернетПочта.Послать() и обрабатывайте возвращаемое им Соответствие для выявления неактивных адресов.Попытка ... Исключение.