Как реализовать автоматическую отправку таблицы с заказами клиентов из 1С по электронной почте?

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

Разработка механизмов автоматического оповещения — одна из самых частых задач, с которыми сталкиваются начинающие программисты 1С. Однако лаконичные формулировки технических заданий нередко вызывают ступор. Разработчики задаются вопросами: нужно ли выводить таблицу на форму внешней обработки? Как правильно применить отборы? Каким образом сформировать и отправить саму таблицу?

В этой статье мы подробно разберем классическую задачу: создание внешней обработки для автоматической отправки по электронной почте таблицы с данными оформленных документов «Заказ клиента», удовлетворяющих заданным условиям отбора. Мы декомпозируем требования, спроектируем правильную архитектуру взаимодействия клиента и сервера, а также напишем весь необходимый код на языке 1С.

Декомпозиция задачи и анализ контекста

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

  1. «При нажатии на кнопку "Отправить"...» — требуется интерактивная кнопка на управляемой форме нашей внешней обработки, которая запускает основной алгоритм.
  2. «...из 1С отправляется письмо...» — отправка должна происходить в фоновом режиме на стороне сервера с использованием системных механизмов платформы.
  3. «...содержащее таблицу с данными...» — письмо должно быть сформировано в формате HTML, где сама таблица генерируется динамически в теле сообщения. Никаких «ручных» вбиваний данных или статичных таблиц на форме обработки делать не нужно.
  4. «...оформленных документов "Заказ клиента", удовлетворяющих условиям отбора» — программа должна обратиться к базе данных с помощью запроса, выбрать проведенные (оформленные) заказы, отфильтровать их по параметрам (например, по периоду дат и пороговой сумме) и вывести результат в письмо.

Выяснив суть, перейдем к пошаговому проектированию нашей обработки.

Шаг 1. Проектирование формы и настройка автосохранения параметров

Для реализации отборов нам понадобятся параметры на форме внешней обработки. Создадим форму и добавим на нее два ключевых реквизита:

Разместим эти реквизиты на форме. Чтобы пользователю не приходилось при каждом запуске обработки вводить параметры заново, настроим их автоматическое запоминание. Для этого в свойствах формы установим флаг «Автоматическое сохранение данных в настройках», а в свойствах самих реквизитов ПериодОтбора и МинимальнаяСумма взведем флаг «Сохранение».

Шаг 2. Валидация заполнения полей на клиенте

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

Посмотрим на пример реализации процедуры валидации на клиенте:


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

Шаг 3. Выборка данных на сервере с помощью запроса

Переходим к серверной части разработки. Нам необходимо написать оптимальный запрос к таблице документов Документ.ЗаказКлиента. Мы выберем только оформленные (проведенные) заказы за указанный период, сумма которых больше или равна установленному порогу.

Проанализируем структуру запроса и получение необходимых полей (номер, дата, клиент, сумма, валюта, менеджер):


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

Шаг 4. Динамическое формирование HTML-письма

Чтобы письмо выглядело презентабельно и открывалось в любых почтовых клиентах (Outlook, Thunderbird, веб-интерфейсы Яндекс.Почты или Mail.ru), мы сформируем тело письма в формате HTML. Использование инлайновых стилей (inline CSS) гарантирует правильное отображение сетки таблицы.

Разберем по шагам процесс генерации HTML-кода, включая вывод итоговой строки с суммой прописью:


&НаСервереБезКонтекста
Функция СформироватьHTMLТелоПисьма(ТаблицаДанных, ДатаНачала, ДатаОкончания)
    
    // Форматируем даты для заголовка
    СтрДатаНачала = Формат(ДатаНачала, "ДФ=dd.MM.yyyy");
    СтрДатаОкончания = Формат(ДатаОкончания, "ДФ=dd.MM.yyyy");
    
    // Начало HTML-документа и стили
    HTML = "<html><head><style>
           |body { font-family: Arial, sans-serif; font-size: 14px; color: #333333; }
           |table { border-collapse: collapse; width: 100%; margin-top: 15px; }
           |th { background-color: #f2f2f2; padding: 8px; border: 1px solid #dddddd; text-align: left; }
           |td { padding: 8px; border: 1px solid #dddddd; }
           |tr:nth-child(even) { background-color: #fafafa; }
           |.summary { font-weight: bold; margin-top: 15px; background-color: #eaf2ff; padding: 10px; border: 1px solid #b3d4ff; }
           |</style></head><body>";
           
    HTML = HTML + "<h2>Уведомление о крупных заказах</h2>";
    HTML = HTML + "<p>Приветствуем вас! Ниже представлена сводная таблица по оформленным заказам клиентов за период с <strong>" + СтрДатаНачала + "</strong> по <strong>" + СтрДатаОкончания + "</strong>.</p>";
    
    // Формируем шапку таблицы
    HTML = HTML + "<table>";
    HTML = HTML + "<tr>
                 |<th>№</th>
                 |<th>Дата</th>
                 |<th>Клиент</th>
                 |<th>Сумма</th>
                 |<th>Менеджер</th>
                 |</tr>";
                 
    ВсегоСумма = 0;
    КоличествоЗаказов = ТаблицаДанных.Количество();
    
    // Заполняем строки данными
    Для Каждого Строка Из ТаблицаДанных Цикл
        
        ВсегоСумма = ВсегоСумма + Строка.Сумма;
        СтрДатаДок = Формат(Строка.Дата, "ДФ=dd.MM.yyyy HH:mm");
        СтрСуммаДок = Формат(Строка.Сумма, "ЧДЦ=2; ЧГ=3") + " " + Строка.ВалютаСимвол;
        
        HTML = HTML + "<tr>";
        HTML = HTML + "<td>" + Строка.Номер + "</td>";
        HTML = HTML + "<td>" + СтрДатаДок + "</td>";
        HTML = HTML + "<td>" + Строка.Клиент + "</td>";
        HTML = HTML + "<td>" + СтрСуммаДок + "</td>";
        HTML = HTML + "<td>" + Строка.МенеджерФИО + "</td>";
        HTML = HTML + "</tr>";
        
    КонецЦикла;
    
    HTML = HTML + "</table>";
    
    // Формируем сумму прописью
    ПараметрыПрописи = "L=ru_RU; ДП=Истина";
    СуммаПрописью = ЧислоПрописью(ВсегоСумма, ПараметрыПрописи, "рубль, рубля, рублей, м, копейка, копейки, копеек, ж, 2");
    
    // Формируем подвал с итогами
    HTML = HTML + "<div class='summary'>";
    HTML = HTML + "Всего " + КоличествоЗаказов + " заказа(ов) на сумму " + Формат(ВсегоСумма, "ЧДЦ=2; ЧГ=3") + " (" + СуммаПрописью + ") руб.";
    HTML = HTML + "</div>";
    
    HTML = HTML + "<p style='font-size: 11px; color: #888888; margin-top: 20px;'>Сообщение сформировано автоматически из системы 1С.</p>";
    HTML = HTML + "</body></html>";
    
    Возврат HTML;
    
КонецФункции

Шаг 5. Отправка почты с использованием объекта ИнтернетПочта

Для непосредственной отправки подготовленного HTML-текста воспользуемся встроенными объектами платформы 1С — ИнтернетПочтовыйПрофиль и ИнтернетПочта. Настройки подключения (сервер SMTP, порт, логин и пароль) обычно хранятся в системной учетной записи или константах конфигурации, но в нашем примере для наглядности мы укажем демонстрационные параметры.

Рассмотрим процедуру отправки письма:


&НаСервереБезКонтекста
Процедура ОтправитьПисьмоНаАдрес(Тема, HTMLТело, АдресПолучателя)
    
    Профиль = Новый ИнтернетПочтовыйПрофиль;
    // Укажите настройки вашего SMTP-сервера
    Профиль.АдресСервераSMTP = "smtp.mail.ru";
    Профиль.ПортSMTP = 465;
    Профиль.ПользовательSMTP = "robot@mail.ru";
    Профиль.ПарольSMTP = "your_secure_password";
    Профиль.ИспользоватьSSLSMTP = Истина;
    
    Письмо = Новый ИнтернетПочтовоеСообщение;
    Письмо.Тема = Тема;
    Письмо.Отправитель.Адрес = "robot@mail.ru";
    Письмо.Отправитель.ОтображаемоеИмя = "Уведомления 1С";
    
    // Добавляем получателя
    Письмо.Получатели.Добавить(АдресПолучателя);
    
    // Указываем тип текста письма как HTML
    Письмо.УстановитьТекст(HTMLТело, ТипТекстаПочтовогоСообщения.HTML);
    
    Почта = Новый ИнтернетПочта;
    Попытка
        Почта.Подключиться(Профиль);
        Почта.Послать(Письмо);
        Почта.Отключиться();
    Исключение
        Почта.Отключиться();
        ВызватьИсключение "Ошибка отправки почты: " + ОписаниеОшибки();
    КонецПопытки;
    
КонецПроцедуры

Шаг 6. Сборка единого процесса в модуле формы

Теперь свяжем все наши шаги в единый процесс обработки нажатия на кнопку «Отправить» на форме. Мы проверим заполнение параметров на клиенте, обратимся к серверу для выборки данных и формирования письма, после чего отправим его получателю.


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

&НаСервере
Процедура ВыполнитьОтправкуНаСервере()
    
    // Получаем таблицу подходящих заказов
    ТаблицаЗаказов = ПолучитьТаблицуЗаказов(Объект.ПериодОтбора.ДатаНачала, Объект.ПериодОтбора.ДатаОкончания, Объект.МинимальнаяСумма);
    
    Если ТаблицаЗаказов.Количество() = 0 Тогда
        ВызватьИсключение "За выбранный период нет заказов, превышающих сумму порога.";
    КонецЕсли;
    
    // Формируем динамическое HTML тело письма
    HTMLТело = СформироватьHTMLТелоПисьма(ТаблицаЗаказов, Объект.ПериодОтбора.ДатаНачала, Объект.ПериодОтбора.ДатаОкончания);
    
    // Формируем тему письма
    СтрДатаНачала = Формат(Объект.ПериодОтбора.ДатаНачала, "ДФ=dd.MM.yyyy");
    СтрДатаОкончания = Формат(Объект.ПериодОтбора.ДатаОкончания, "ДФ=dd.MM.yyyy");
    ТемаПисьма = "Крупные заказы за период с " + СтрДатаНачала + " по " + СтрДатаОкончания;
    
    // Жестко заданный адрес руководителя (или получаемый из настроек)
    АдресРуководителя = "boss@company.ru";
    
    // Отправка почты
    ОтправитьПисьмоНаАдрес(ТемаПисьма, HTMLТело, АдресРуководителя);
    
КонецПроцедуры

Данная архитектурная схема полностью удовлетворяет лучшим практикам разработки в среде 1С:Предприятие 8.3. Клиентская часть занимается интерфейсом и валидацией данных, тогда как сервер берет на себя тяжелую логику работы с СУБД, построение макета письма и отправку по сетевым протоколам.

← На главную