Как разработать внешнюю обработку в 1С для отправки отчетов по электронной почте?

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

При изучении платформы 1С:Предприятие начинающие разработчики часто сталкиваются с комплексными задачами, которые требуют одновременно спроектировать удобный пользовательский интерфейс, написать оптимальный запрос к базе данных и настроить интеграцию со сторонними сервисами (например, отправку писем по протоколу SMTP). Разберем по шагам создание внешней обработки на примере реального тестового задания: оповещение руководителя о крупных заказах клиентов за выбранный период.

В этой статье мы подробно рассмотрим, как правильно настроить элементы формы, разделить логику между модулем формы и модулем объекта, составить оптимальный запрос, динамически сформировать красивую HTML-таблицу в теле письма и отправить сообщение адресату.

Шаг 1. Настройка реквизитов формы и сохранение настроек

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

Выясним, какие типы данных и настройки свойств лучше всего использовать для решения этой задачи:

  1. Выбор периода: Для ввода периода в 1С существует специализированный тип данных. Вместо создания двух отдельных реквизитов типа Дата (для начала и окончания периода) мы создадим один реквизит с типом СтандартныйПериод. Это позволит пользователю использовать удобное стандартное диалоговое окно для выбора периода (например, "Текущий месяц", "Прошлая неделя" и т.д.).
  2. Имя и Заголовок элемента управления: При добавлении полей на форму важно различать свойства Имя и Заголовок. Свойство Имя используется для обращения к элементу из программного кода, поэтому оно должно быть написано в одно слово, без пробелов, точек и спецсимволов. Свойство Заголовок — это то, что видит пользователь на экране. В поле Заголовок можно писать любой понятный текст, включая скобки, знаки препинания и пробелы.
  3. Автоматическое сохранение настроек: Чтобы пользователю не приходилось при каждом открытии обработки заново вводить пороговую сумму, настроим сохранение параметров. Для этого в свойствах управляемой формы найдем и установим флаг Автоматическое сохранение данных в настройках (или свойство СохраняемыеДанные для конкретного реквизита). В списке сохраняемых параметров достаточно отметить галочкой реквизит Сумма. Теперь платформа автоматически запишет последнее введенное число в системное хранилище и подставит его при следующем запуске.

Шаг 2. Разделение программного кода между модулем формы и модулем объекта

Правильная архитектура — залог стабильной работы конфигурации и успешного прохождения любого собеседования. Нам нужно четко разграничить ответственность между модулем формы и модулем объекта нашей обработки.

Модуль формы отвечает исключительно за интерактивную работу с пользователем на клиенте. В нем мы выполняем валидацию данных перед отправкой. Если обязательные поля (период или сумма) не заполнены, мы прерываем операцию и выводим пользователю адресное предупреждение.

Проанализируем пример кода проверки заполнения полей на форме с привязкой ошибки к конкретному полю ввода с помощью объекта СообщениеПользователю:


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

Модуль объекта предназначен для выполнения бизнес-логики на сервере. Форма не должна напрямую отправлять почту или строить сложные SQL-запросы. Вместо этого из серверной процедуры формы мы конвертируем реквизит формы во временный прикладной объект в памяти и вызываем его экспортный метод:


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

Шаг 3. Создание запроса к базе данных

Рассмотрим подробнее получение данных о заказах. Часто начинающие программисты делают прямой запрос к таблице документов Документ.ЗаказКлиента. В учебных целях или в простых базах это сработает, но в реальных крупных информационных системах такой подход считается неоптимальным, так как обращение к таблицам документов напрямую может замедлять работу базы данных.

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

Посмотрим на пример построения безопасного и быстрого запроса с использованием параметров:


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

Шаг 4. Формирование HTML-тела письма и суммы прописью

Для того чтобы руководитель получил красивое структурированное письмо, мы должны сформировать его тело в формате HTML-кода. Нам потребуется программно собрать HTML-таблицу, подсчитать количество подходящих документов, вычислить их общую сумму и перевести её в текстовый вид.

Для перевода итоговой суммы в текстовое представление воспользуемся стандартной мощной функцией платформы 1С ЧислоПрописью(). Проанализируем реализацию этой логики:


Функция СформироватьТелоПисьма(ТаблицаДанных, ДатаНачала, ДатаОкончания, МинимальнаяСумма) Экспорт
    
    // Начало разметки документа
    HTML = "<html><body style='font-family: Arial, sans-serif;'>";
    HTML = HTML + "<p>Здравствуйте, Иван Иванович!</p>";
    HTML = HTML + "<p>Список заказов с суммой от <strong>" + Формат(МинимальнаяСумма, "ЧДЦ=2; ЧГ=3") + "</strong> руб. за период с " 
                + Формат(ДатаНачала, "ДФ=dd.MM.yyyy") + " по " + Формат(ДатаОкончания, "ДФ=dd.MM.yyyy") + ":</p>";
    
    // Создаем HTML-таблицу с рамкой и стилями
    HTML = HTML + "<table border='1' cellpadding='5' style='border-collapse: collapse; width: 100%; border-color: #cccccc;'>";
    HTML = HTML + "<tr bgcolor='#f2f2f2'>"
                + "<th><strong>№</strong></th>"
                + "<th><strong>Дата</strong></th>"
                + "<th><strong>Клиент</strong></th>"
                + "<th><strong>Сумма</strong></th>"
                + "<th><strong>Менеджер</strong></th>"
                + "</tr>";
    
    ВсегоСумма = 0;
    КоличествоЗаказов = ТаблицаДанных.Количество();
    
    Для Каждого Строка Из ТаблицаДанных Цикл
        
        HTML = HTML + "<tr>"
                    + "<td align='center'>" + Строка.Номер + "</td>"
                    + "<td align='center'>" + Формат(Строка.Дата, "ДФ=dd.MM.yyyy") + "</td>"
                    + "<td>" + Строка.Клиент + "</td>"
                    + "<td align='right'>" + Формат(Строка.Сумма, "ЧДЦ=2; ЧГ=3") + "</td>"
                    + "<td>" + Строка.Менеджер + "</td>"
                    + "</tr>";
                    
        ВсегоСумма = ВсегоСумма + Строка.Сумма;
        
    КонецЦикла;
    
    HTML = HTML + "</table>";
    
    // Форматируем сумму прописью по правилам русского языка
    ПараметрыПрописи = "L=ru_RU; ДП=Истина";
    СуммаПрописью = ЧислоПрописью(ВсегоСумма, ПараметрыПрописи, "рубль, рубля, рублей, м, копейка, копейки, копеек, ж, 2");
    
    HTML = HTML + "<p>Всего " + КоличествоЗаказов + " заказа(ов) на сумму " 
                + Формат(ВсегоСумма, "ЧДЦ=2; ЧГ=3") + " (" + СуммаПрописью + ") руб.</p>";
    HTML = HTML + "</body></html>";
    
    Возврат HTML;
    
КонецФункции

Шаг 5. Отправка почты по протоколу SMTP

Теперь объединим все шаги в общую бизнес-логику в модуле объекта и выполним отправку сообщения на сервер. Нам понадобятся объекты ИнтернетПочтовыйПрофиль для настройки параметров соединения и ИнтернетПочта для непосредственного взаимодействия с SMTP-сервером.

Важное предупреждение по безопасности: Современные почтовые сервисы (Yandex, Mail.ru, Gmail) требуют использования SSL-соединения и специальных безопасных портов. Кроме того, обычные пароли от почтовых ящиков в коде использовать больше нельзя — предварительно в личном кабинете вашей почты необходимо сгенерировать специальный "Пароль для внешних приложений" (SMTP-пароль).

Ниже представлен завершенный программный код для модуля объекта обработки:


Процедура ВыполнитьРассылку(ДатаНачала, ДатаОкончания, МинимальнаяСумма) Экспорт
    
    // Шаг 1: Получаем данные из базы
    ТаблицаЗаказов = ПолучитьТаблицуЗаказов(ДатаНачала, ДатаОкончания, МинимальнаяСумма);
    
    Если ТаблицаЗаказов.Количество() = 0 Тогда
        Сообщить("За выбранный период крупных заказов не обнаружено. Письмо не отправлено.");
        Возврат;
    КонецЕсли;
    
    // Шаг 2: Формируем текст письма в HTML
    ТекстHTML = СформироватьТелоПисьма(ТаблицаЗаказов, ДатаНачала, ДатаОкончания, МинимальнаяСумма);
    
    // Шаг 3: Настраиваем защищенное подключение к SMTP
    Профиль = Новый ИнтернетПочтовыйПрофиль;
    Профиль.АдресСервераSMTP = "smtp.yandex.ru";
    Профиль.ИспользоватьSSLSMTP = Истина;
    Профиль.ПортSMTP = 465;
    
    // Укажите ваши реальные параметры авторизации
    Профиль.ПользовательSMTP = "report-sender@yandex.ru";
    Профиль.ПарольSMTP = "xxxx-xxxx-xxxx-xxxx"; // Пароль приложения, сгенерированный в Яндексе
    
    // Шаг 4: Подготавливаем сообщение
    Письмо = Новый ИнтернетПочтовоеСообщение;
    Письмо.Тема = "Крупные заказы за период с " + Формат(ДатаНачала, "ДФ=dd.MM.yyyy") + " по " + Формат(ДатаОкончания, "ДФ=dd.MM.yyyy");
    Письмо.Отправитель = "report-sender@yandex.ru";
    
    // Добавляем получателя (руководителя)
    Письмо.Получатели.Добавить("director@mycompany.ru");
    
    // Критически важная деталь: явно указываем тип текста как HTML
    Письмо.Тексты.Добавить(ТекстHTML, ТипТекстаПочтовогоСообщения.HTML);
    
    // Шаг 5: Выполняем отправку сообщения
    Почта = Новый ИнтернетПочта;
    Попытка
        Почта.Подключиться(Профиль);
        Почта.Послать(Письмо);
        Почта.Отключиться();
        Сообщить("Отчет о крупных заказах успешно отправлен руководителю.");
    Исключение
        ВызватьИсключение "Не удалось отправить почту. Описание ошибки: " + ОписаниеОшибки();
    КонецПопытки;
    
КонецПроцедуры

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

← На главную