Как отправить ТабличныйДокумент из 1С в теле электронного письма в формате HTML?

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

Мы сталкиваемся с распространенной задачей: необходимо отправить отчет, сформированный в 1С как ТабличныйДокумент, по электронной почте таким образом, чтобы его содержимое отображалось непосредственно в теле письма в формате HTML, а не в виде вложения. Часто при попытке реализации этой задачи письма приходят пустыми или содержат только текст "ТабличныйДокумент", что указывает на некорректную обработку объекта. Давайте подробно разберем, как правильно настроить отправку HTML-писем из 1С, обеспечивая корректное отображение отчета.

Решение 1: Базовый подход с использованием временного HTML-файла

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

Шаг 1. Создаем и заполняем ТабличныйДокумент

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


&НаСервере
Процедура ПоказатьНаСервере(ТабДок)
    // Пример формирования отчета
    Обработка = РеквизитФормыВЗначение("Объект");
    Макет = Обработка.ПолучитьМакет("Макет");

    // Здесь должны быть инициализированы параметры макета и запроса,
    // например, СуммаДокумента, Период.ДатаНачала, Период.ДатаОкончания, КоличествоЗаказов.
    // Для демонстрации мы предполагаем, что они доступны в контексте или переданы.

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

    // Установка параметров запроса (заглушки для примера)
    Запрос.УстановитьПараметр("ДатаНач", НачалоГода(ТекущаяДата()));
    Запрос.УстановитьПараметр("ДатаКон", КонецГода(ТекущаяДата()));
    Запрос.УстановитьПараметр("СуммаДокумента", 0);


    РезультатЗапроса = Запрос.Выполнить();

    // Получаем области макета
    ОбластьЗаголовок = Макет.ПолучитьОбласть("Заголовок");
    ОбластьПодвал = Макет.ПолучитьОбласть("Подвал");
    ОбластьШапкаТаблицы = Макет.ПолучитьОбласть("ШапкаТаблицы");
    ОбластьПодвалТаблицы = Макет.ПолучитьОбласть("ПодвалТаблицы");
    ОбластьДетальныхЗаписей = Макет.ПолучитьОбласть("Детали");

    // Очищаем и выводим области в ТабДок
    ТабДок.Очистить();
    ТабДок.Вывести(ОбластьЗаголовок);
    ТабДок.Вывести(ОбластьШапкаТаблицы);
    ТабДок.НачатьАвтогруппировкуСтрок();

    ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
    Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
        ОбластьДетальныхЗаписей.Параметры.Заполнить(ВыборкаДетальныеЗаписи);
        ТабДок.Вывести(ОбластьДетальныхЗаписей, ВыборкаДетальныеЗаписи.Уровень);
    КонецЦикла;

    ТабДок.ЗакончитьАвтогруппировкуСтрок();
    ТабДок.Вывести(ОбластьПодвалТаблицы);
    ТабДок.Вывести(ОбластьПодвал);

КонецПроцедуры

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

Шаг 2. Сохраняем ТабличныйДокумент во временный HTML-файл

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


ИмяВременногоHTMLФайла = ПолучитьИмяВременногоФайла("html");
ТабДок.Записать(ИмяВременногоHTMLФайла, ТипФайлаТабличногоДокумента.HTML);

Мы используем расширение "html" для временного файла, что позволяет почтовым клиентам корректно распознавать его содержимое.

Шаг 3. Читаем содержимое HTML-файла

После сохранения нам нужно прочитать текст из этого временного файла. Для этого мы будем использовать объект ЧтениеТекста. Это позволит нам получить HTML-код в виде строки.


ЧтениеHTML = Новый ЧтениеТекста(ИмяВременногоHTMLФайла);
ТекстHTMLПисьма = ЧтениеHTML.Прочитать();
ЧтениеHTML.Закрыть(); // Обязательно закрываем файл после чтения

Полученная переменная ТекстHTMLПисьма теперь содержит весь HTML-код нашего табличного документа, готовый для вставки в письмо.

Шаг 4. Добавляем HTML-текст в тело почтового сообщения

Теперь, когда у нас есть HTML-строка, мы можем добавить ее в объект ИнтернетПочтовоеСообщение. Самое главное здесь — указать правильный тип текста: ТипТекстаПочтовогоСообщения.HTML. Без этого почтовый клиент не сможет правильно интерпретировать содержимое.


Сообщение = Новый ИнтернетПочтовоеСообщение;
// ... Настройка получателей, отправителя, темы сообщения ...
Сообщение.Тексты.Добавить(ТекстHTMLПисьма, ТипТекстаПочтовогоСообщения.HTML);

Этот шаг гарантирует, что почтовый клиент будет интерпретировать содержимое как HTML, а не как обычный текст, предотвращая появление "ТабличныйДокумент" или пустого тела письма.

Шаг 5. Отправляем сообщение и очищаем временные файлы

После добавления HTML-текста мы подключаемся к почтовому серверу и отправляем письмо. Критически важно не забыть удалить временный файл после использования, чтобы избежать засорения диска и потенциальных проблем с безопасностью.


Почта = Новый ИнтернетПочта;
Почта.Подключиться(ИПП); // ИПП - это ИнтернетПочтовыйПрофиль, настроенный ранее
Почта.Послать(Сообщение);
Почта.Отключиться();

// Удаляем временный файл
УдалитьФайлы(ИмяВременногоHTMLФайла);

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

Полный пример кода для отправки (адаптированный из сообщений форума)

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


&НаКлиенте
Процедура ОтправитьОтчетПоПочте(Команда)
    ТабДок = Новый ТабличныйДокумент;
    // Формирование ТабДок на сервере
    ПоказатьНаСервере(ТабДок); 

    // Проверяем, что ТабДок не пустой
    Если ТабДок.КоличествоСтраниц = 0 Тогда
        Сообщить("Табличный документ пустой. Отправка невозможна.");
        Возврат;
    КонецЕсли;

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

КонецПроцедуры

// Процедура отправки письма, выполняется на сервере
&НаСервере
Процедура ОтправитьУведомленияНаСервере(ТабДок)
    ИПП = Новый ИнтернетПочтовыйПрофиль;
    ИПП.АдресСервераSMTP = "smtp.yandex.ru"; // Адрес SMTP-сервера
    ИПП.ПарольSMTP = "Ваш_Пароль_Приложения_Или_Обычный"; // Замените на реальный пароль приложения/обычный
    ИПП.ПользовательSMTP = "reportMyCompanyName@yandex.ru"; // Замените на реальный адрес отправителя
    ИПП.ПортSMTP = 25; // Или 465 для SSL, 587 для TLS, в зависимости от настроек почтового сервера
    // ИПП.ИспользоватьSSLSMTP = Истина; // Если используется SSL/TLS

    Сообщение = Новый ИнтернетПочтовоеСообщение;
    Сообщение.Кодировка = "UTF-8";
    Сообщение.Получатели.Добавить("reportMyCompanyName@yandex.ru"); // Замените на реальный адрес получателя
    Сообщение.Отправитель.Адрес = "reportMyCompanyName@yandex.ru"; // Замените на реальный адрес отправителя
    Сообщение.Тема = "Регламентное задание: Отчет";

    ИмяВременногоHTMLФайла = ""; // Объявляем переменную для имени временного файла

    Попытка
        // Получаем имя временного файла на сервере
        ИмяВременногоHTMLФайла = ПолучитьИмяВременногоФайла("html");

        // Сохраняем табличный документ в HTML-файл
        ТабДок.Записать(ИмяВременногоHTMLФайла, ТипФайлаТабличногоДокумента.HTML);

        // Читаем содержимое HTML-файла
        ЧтениеHTML = Новый ЧтениеТекста(ИмяВременногоHTMLФайла);
        ТекстHTMLПисьма = ЧтениеHTML.Прочитать();
        ЧтениеHTML.Закрыть();

        // Добавляем прочитанный HTML в тело сообщения как HTML-текст
        Сообщение.Тексты.Добавить(ТекстHTMLПисьма, ТипТекстаПочтовогоСообщения.HTML);

        // Также полезно добавить версию в виде простого текста для почтовых клиентов, не поддерживающих HTML
        Сообщение.Тексты.Добавить("Отчет сформирован. Для просмотра полной версии откройте письмо в почтовом клиенте, поддерживающем HTML.", ТипТекстаПочтовогоСообщения.ПростойТекст);

        // Опционально: Вызов Сообщение.ОбработатьТексты() для корректной работы с изображениями
        // и формирования альтернативных представлений письма (HTML и Plain Text).
        // Это особенно полезно, если в ТабДок есть картинки, которые должны быть встроены.
        Сообщение.ОбработатьТексты();

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

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

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

Рекомендации по форматированию HTML в почтовых клиентах

Мы уже выяснили, что даже при корректной отправке HTML-письма, его отображение в различных почтовых клиентах может "съезжать" или выглядеть не так, как ожидалось. Это связано с особенностями рендеринга HTML и CSS в почтовых программах, которые часто не поддерживают современные веб-стандарты в полной мере. Рассмотрим несколько важных рекомендаций, которые помогут улучшить стабильность отображения.

  1. Использование табличной верстки: Традиционно, для стабильного отображения HTML-писем рекомендуется использовать табличную верстку (теги <table>, <tr>, <td>) вместо блочной (<div>). Таблицы лучше всего поддерживаются большинством почтовых клиентов. К счастью, ТабличныйДокумент 1С при сохранении в HTML уже генерирует табличную структуру, что является преимуществом.
  2. Инлайн-стили CSS: Избегайте использования внешних таблиц стилей (<link rel="stylesheet">) и встроенных блоков стилей (<style>...</style>) в заголовке HTML-документа. Вместо этого применяйте стили непосредственно к HTML-элементам с помощью атрибута style (инлайн-стили). Например: <td style="font-family: Arial; font-size: 12px; color: #333;">. Генерация HTML из ТабличныйДокумент 1С обычно использует инлайн-стили, что соответствует лучшим практикам для почтовых клиентов.
  3. Тестирование на разных клиентах: Всегда тестируйте отправленные HTML-письма в нескольких популярных почтовых клиентах (Outlook, Gmail, Яндекс.Почта, Mail.ru, мобильные клиенты), чтобы убедиться в корректности отображения. То, что хорошо выглядит в одном клиенте, может выглядеть иначе в другом.
  4. Метод Сообщение.ОбработатьТексты(): После добавления HTML-текста в коллекцию Сообщение.Тексты, полезно вызвать метод Сообщение.ОбработатьТексты(). Этот метод может автоматически обработать текст, создать альтернативную текстовую версию письма (для клиентов, не поддерживающих HTML) и, что важно, корректно встроить изображения.

    Если ваш табличный документ содержит рисунки, и вы хотите, чтобы они отображались прямо в теле письма, а не как отдельные вложения, метод ОбработатьТексты() может помочь преобразовать ссылки на локальные файлы изображений в ссылки с использованием cid: (Content-ID). Для этого изображения должны быть добавлены как вложения в почтовое сообщение с указанием их Content-ID, на который затем ссылается HTML. Впрочем, Сообщение.ОбработатьТексты() в некоторых случаях может сделать это автоматически, если HTML корректно ссылается на изображения, уже добавленные как вложения.

    
    // Пример добавления HTML и обработки
    Сообщение.Тексты.Добавить(ТекстHTMLПисьма, ТипТекстаПочтовогоСообщения.HTML);
    Сообщение.Тексты.Добавить("Это текстовая версия отчета. Для полной версии см. HTML-часть.", ТипТекстаПочтовогоСообщения.ПростойТекст);
    Сообщение.ОбработатьТексты(); // Вызов после добавления всех текстов и, возможно, вложений
    

Продвинутое решение: Использование потоков в памяти и манипуляции с HTML

Мы можем пойти дальше и не сохранять файл на диск вовсе, используя потоки в памяти. Это особенно полезно в высоконагруженных системах или при работе с облачными решениями, где доступ к файловой системе может быть ограничен или нежелателен. Кроме того, можно программно манипулировать HTML-кодом для более тонкой настройки.

Шаг 1. Сохранение ТабличногоДокумента в поток в памяти

Вместо сохранения на диск, мы можем записать ТабличныйДокумент в объект ПотокВПамяти. Это позволяет избежать операций с файловой системой.


ПотокHTML = Новый ПотокВПамяти;
ТабДок.Записать(ПотокHTML, ТипФайлаТабличногоДокумента.HTML);
// После записи, мы можем получить двоичные данные из потока или прочитать как текст

Шаг 2. Чтение текста из потока

Для получения HTML-текста из ПотокВПамяти, необходимо создать ЧтениеТекста, указав поток в качестве источника. Это аналогично чтению из файла, но происходит полностью в оперативной памяти.


ЧтениеПотокаТекста = Новый ЧтениеТекста(ПотокHTML);
ТекстHTMLИзПотока = ЧтениеПотокаТекста.Прочитать();
ЧтениеПотокаТекста.Закрыть();

Теперь переменная ТекстHTMLИзПотока содержит наш HTML, и никакие временные файлы на диске не создавались.

Шаг 3. Манипуляции с HTML с помощью COMОбъекта "HtmlFile" (только для Windows)

Если требуется более тонкая настройка HTML (например, добавление общих отступов, изменение стилей, которые 1С не генерирует по умолчанию, или другие сложные преобразования), можно использовать COMОбъект("HtmlFile"). Это мощный инструмент, который позволяет работать с HTML-документом как с объектной моделью (DOM), но он работает только на операционных системах Windows, где доступны необходимые компоненты COM (например, Internet Explorer или его движок).


НовыйHTMLДокумент = Новый COMОбъект("HtmlFile");
НовыйHTMLДокумент.open("text/html");
НовыйHTMLДокумент.write(ТекстHTMLИзПотока); // Используем HTML из потока
НовыйHTMLДокумент.close();

// Пример манипуляции: добавление общих отступов к телу документа
// (это просто пример, реальные изменения могут быть более сложными)
ТегТела = НовыйHTMLДокумент.all.tags("BODY");
Для а = 0 По ТегТела.length - 1 Цикл
    ТегТела.item(а).innerHTML = "    " + ТегТела.item(а).innerHTML; // Добавляем пробелы в начало body
КонецЦикла;

// Извлечение модифицированного HTML-кода из объекта COM
ТекстСообщенияДляОтправки = "";
Для а = 0 По НовыйHTMLДокумент.all.length - 1 Цикл
    Если НовыйHTMLДокумент.all.item(а).tagName = "HTML" Тогда
        ТекстСообщенияДляОтправки = НовыйHTMLДокумент.all.item(а).innerHTML;
        Прервать;
    КонецЕсли;
КонецЦикла;

// Затем ТекстСообщенияДляОтправки добавляется в Сообщение.Тексты
Сообщение.Тексты.Добавить(ТекстСообщенияДляОтправки, ТипТекстаПочтовогоСообщения.HTML);

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

Альтернативные форматы отправки (в виде вложений)

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

  1. Отправка в формате PDF: PDF является универсальным форматом для представления документов, который сохраняет исходное форматирование независимо от устройства или программы просмотра. 1С поддерживает сохранение ТабличныйДокумент в PDF.
    
    ПотокPDF = Новый ПотокВПамяти;
    ТабДок.Записать(ПотокPDF, ТипФайлаТабличногоДокумента.PDF);
    
    ДвоичныеДанныеPDF = ПотокPDF.ЗакрытьИПолучитьДвоичныеДанные();
    
    // Добавляем PDF как вложение
    Сообщение.Вложения.Добавить(ДвоичныеДанныеPDF, "Отчет_" + Формат(ТекущаяДата(), "ДФ=ddMMyyyy") + ".pdf");
    

    Этот способ гарантирует сохранение оригинального вида документа, хотя и требует от получателя открытия вложения.

  2. Отправка в формате XLS (Excel): Для отчетов, которые предполагают дальнейшую обработку данных в электронных таблицах, формат XLS (Excel) может быть предпочтительнее.
    
    ПотокXLS = Новый ПотокВПамяти;
    ТабДок.Записать(ПотокXLS, ТипФайлаТабличногоДокумента.XLS);
    
    ДвоичныеДанныеXLS = ПотокXLS.ЗакрытьИПолучитьДвоичныеДанные();
    
    // Добавляем XLS как вложение
    Сообщение.Вложения.Добавить(ДвоичныеДанныеXLS, "Отчет_" + Формат(ТекущаяДата(), "ДФ=ddMMyyyy") + ".xls");
    

    Это удобно, если получателю нужно работать с данными, а не просто просматривать их.

Заключение

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

← На главную