Как программно вызвать внешнюю печатную форму другого документа и сохранить ее в PDF в 1С 8.3

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

В практике разработки на платформе 1С:Предприятие 8.3 часто возникает задача, когда из одного документа (например, специального рабочего места, реестра или акта сверки) необходимо сформировать и открыть внешнюю печатную форму (ВПФ) совершенно другого, связанного документа. Дополнительно может потребоваться не просто вывести форму на экран, а программно сохранить её в формате PDF для последующей отправки клиенту.

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

Сценарий 1. Интерактивный вызов внешней печатной формы стороннего документа

Рассмотрим ситуацию, когда у нас есть документ, в табличной части которого содержатся ссылки на другие документы (например, «Оказание услуг» или «Платежное требование»). Нам нужно сделать так, чтобы при установке флага или нажатии кнопки в строке табличной части открывалось стандартное окно печати 1С с уже сформированным макетом ВПФ для выбранного документа.

Для интерактивного открытия печатных форм в БСП предусмотрена специальная общая форма ОбщаяФорма.ПечатьДокументов. Чтобы её запустить, нам необходимо передать в неё правильную структуру параметров: ссылку на саму внешнюю обработку печатной формы, идентификатор команды печати и массив объектов назначения.

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


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

&НаСервереБезКонтекста
Функция НайтиВПФНаСервере(НаименованиеВПФ)
    Возврат Справочники.ДополнительныеОтчетыИОбработки.НайтиПоНаименованию(НаименованиеВПФ);
КонецФункции

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

Сценарий 2. Программное формирование ВПФ и сохранение в PDF на сервере

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

Для этого мы можем использовать серверные методы БСП, которые позволяют «тихо» сформировать табличный документ ВПФ, после чего конвертировать его в PDF. Рассмотрим, как это сделать правильно, избегая записи лишних временных файлов на жесткий диск сервера, работая напрямую с оперативной памятью через потоки (доступно в платформе 1С начиная с версии 8.3.9).


&НаСервереБезКонтекста
Функция СформироватьPDFВнешнейПечатнойФормы(ДокументСсылка, ИмяВнешнейПечатнойФормы, ИдентификаторКоманды)
    
    // 1. Находим внешнюю печатную форму в справочнике
    СсылкаНаВПФ = Справочники.ДополнительныеОтчетыИОбработки.НайтиПоНаименованию(ИмяВнешнейПечатнойФормы);
    Если СсылкаНаВПФ.Пустая() Тогда
        ВызватьИсключение "Не найдена внешняя обработка: " + ИмяВнешнейПечатнойФормы;
    КонецЕсли;
    
    // 2. Получаем объект внешней обработки через БСП
    ОбъектВПФ = ДополнительныеОтчетыИОбработки.ПолучитьОбъектВнешнейОбработки(СсылкаНаВПФ);
    
    // 3. Подготавливаем необходимые структуры данных для БСП печати
    КоллекцияПечатныхФорм = УправлениеПечатью.НоваяКоллекцияПечатныхФорм(ИдентификаторКоманды);
    ОбъектыПечати = Новый СписокЗначений;
    ПараметрыВывода = УправлениеПечатью.ПодготовитьСтруктуруПараметровВывода();
    
    МассивОбъектов = Новый Массив;
    МассивОбъектов.Добавить(ДокументСсылка);
    
    // 4. Вызываем экспортный метод "Печать" самой внешней обработки
    ОбъектВПФ.Печать(МассивОбъектов, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода);
    
    // Проверяем, вернулся ли табличный документ
    СтрокаПечатнойФормы = КоллекцияПечатныхФорм.Найти(ИдентификаторКоманды, "ИмяВХранилище");
    Если СтрокаПечатнойФормы = Неопределено Или СтрокаПечатнойФормы.ТабличныйДокумент = Неопределено Тогда
        ВызватьИсключение "Не удалось сформировать табличный документ для команды: " + ИдентификаторКоманды;
    КонецЕсли;
    
    ТабДок = СтрокаПечатнойФормы.ТабличныйДокумент;
    
    // 5. Конвертируем Табличный Документ в PDF в оперативной памяти (без создания файлов на сервере)
    ПотокВПамяти = Новый ПотокВПамяти;
    ТабДок.Записать(ПотокВПамяти, ТипФайлаТабличногоДокумента.PDF);
    ДвоичныеДанныеФайла = ПотокВПамяти.ЗакрытьИПолучитьДвоичныеДанные();
    
    // Помещаем во временное хранилище, чтобы передать на клиент или использовать для отправки
    Возврат ПоместитьВоВременноеХранилище(ДвоичныеДанныеФайла);

КонецФункции

Используя этот серверный метод, мы получаем адрес во временном хранилище, где лежат готовые двоичные данные PDF-файла. Этот подход работает быстро, не засоряет серверную папку Temp и полностью безопасен в многопоточной среде.

Критическая ошибка: отправка писем при проведении документа

Часто у разработчиков возникает соблазн встроить отправку полученного PDF-файла по электронной почте непосредственно в обработчик проведения документа (событие ОбработкаПроведения в модуле объекта). Делать это категорически запрещено!

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

  1. Блокировка базы данных и зависание транзакций. Проведение документа всегда выполняется внутри жесткой транзакции СУБД. Отправка почты — это сетевой запрос к SMTP-серверу. Если почтовый сервер будет долго отвечать или временно "зависнет", вся транзакция в 1С останется открытой. Это заблокирует таблицы базы данных для других пользователей, парализовав работу всей компании.
  2. Проблема отката транзакции (Rollback). Если письмо успешно отправится на почтовый сервер, но затем в процессе проведения документа возникнет ошибка (например, не хватило остатков по складу), транзакция 1С откатится. В итоге документ в базе останется непроведенным, а клиент уже получит по почте письмо с подтверждением отгрузки.
  3. Спам при групповом перепроведении. При восстановлении последовательности или закрытии месяца документы перепроводятся пакетно. Если отправка встроена в проведение, клиенты получат лавину дублирующих писем за старые периоды.

Правильная архитектура отправки почты

Как же правильно организовать процесс отправки сформированных PDF-файлов клиентам? Разберем три основных надежных способа.

Способ А. Очередь отправки сообщений (Рекомендуемый промышленный подход)

Этот способ идеально подходит для высоконагруженных систем. Мы разделяем процесс проведения документа и процесс сетевого взаимодействия:

  1. При успешном проведении документа (или при его записи) мы лишь делаем быструю запись в специальный вспомогательный регистр сведений (например, «ОчередьОтправкиПисем»), записывая туда ссылку на документ и адрес получателя.
  2. Настраивается регламентное задание, которое запускается в фоновом режиме по расписанию (например, каждые 30 секунд).
  3. Это регламентное задание считывает записи из регистра очереди, на сервере без участия пользователя формирует печатную форму в PDF (по алгоритму из Сценария 2), отправляет письмо через БСП и удаляет запись из очереди после успешной отправки.

Способ Б. Асинхронная отправка через фоновое задание

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


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

Метод ОтправитьДокументНаПочтуФоном выполнится асинхронно. Пользователь сможет сразу продолжить работу с программой, а система в фоне сформирует и отправит файл.

Способ В. Отправка из формы после записи документа

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

Приведем пример кода отправки письма с вложенным PDF-файлом с использованием стандартного почтового модуля БСП РаботаСПочтовымиСообщениями:


&НаСервереБезКонтекста
Процедура СформироватьИОтправитьПисьмоНаСервере(ДокументСсылка)
    
    // 1. Получаем PDF-файл в виде временного хранилища
    АдресХранилища = СформироватьPDFВнешнейПечатнойФормы(ДокументСсылка, "Платежное требование", "ПлатежноеТребованиеСПодписями");
    ДвоичныеДанныеФайла = ПолучитьИзВременногоХранилища(АдресХранилища);
    
    // 2. Настраиваем параметры отправки через БСП
    УчетнаяЗапись = РаботаСПочтовымиСообщениями.СистемнаяУчетнаяЗапись();
    
    ПараметрыПисьма = Новый Структура;
    ПараметрыПисьма.Вставить("Кому", "client@email.ru");
    ПараметрыПисьма.Вставить("Тема", "Документ " + Строка(ДокументСсылка));
    ПараметрыПисьма.Вставить("Тело", "Добрый день! Во вложении к письму находится ваш документ.");
    
    // Подготавливаем массив вложений
    МассивВложений = Новый Массив;
    Вложение = Новый Структура;
    Вложение.Вставить("Представление", "Документ.pdf");
    Вложение.Вставить("АдресВоВременномХранилище", ПоместитьВоВременноеХранилище(ДвоичныеДанныеФайла));
    МассивВложений.Добавить(Вложение);
    
    ПараметрыПисьма.Вставить("Вложения", МассивВложений);
    
    // 3. Безопасная отправка сообщения
    РаботаСПочтовымиСообщениями.ОтправитьПочтовоеСообщение(УчетнаяЗапись, ПараметрыПисьма);
    
КонецПроцедуры

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

← На главную