Как вывести несколько печатных форм в разные окна или сохранить их по отдельности в 1С 8.3

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

При разработке внешних печатных форм (ВПФ) в среде 1С:Предприятие 8.3 (особенно в конфигурациях на базе БСП, таких как ЗУП 3.1, ERP или КА 2) разработчики часто сталкиваются с ситуацией, когда стандартный механизм печати «склеивает» все документы в один или выводит только последний сформированный документ. Рассмотрим ситуацию: в документе «Работа в выходной день» указано 20 сотрудников, и для каждого нужно сформировать отдельное уведомление, которое должно открыться в собственном окне или быть отправлено отдельным файлом по e-mail.

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

Почему стандартный подход не работает?

Проанализируем типичную ошибку. В модуле внешней печатной формы разработчик пишет следующий код:


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

Разберем причину неудачи. Процедура ВывестиТабличныйДокументВКоллекцию ищет в структуре КоллекцияПечатныхФорм строку с именем «Уведомление». Если такая строка уже есть, система либо перезаписывает её (в зависимости от версии БСП), либо дополняет существующую. В итоге на клиент возвращается коллекция, содержащая одну-единственную печатную форму, в которую «влиты» все данные, либо только данные последнего сотрудника. Более того, штатное окно предварительного просмотра БСП (форма ПечатьДокументов) спроектировано так, чтобы отображать содержимое коллекции в одном общем окне.

Решение 1. Разделение страниц в одном окне

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

Проанализируем алгоритм:

  1. Создаем один результирующий ТабличныйДокумент.
  2. В цикле по сотрудникам формируем временный табличный документ.
  3. Выводим его в основной документ методом Вывести.
  4. После вывода каждого сотрудника (кроме последнего) вызываем метод ВывестиГоризонтальныйРазделительСтраниц().
  5. Добавляем итоговый документ в коллекцию один раз.

// Внутри процедуры Печать
РезультирующийТабДок = Новый ТабличныйДокумент;
Для каждого СтрокаСотрудника Из СсылкаДокумента.Сотрудники Цикл
    ТекущийУчедомление = СформироватьУведомление(СсылкаДокумента, СтрокаСотрудника.Сотрудник);
    
    // Если в документе уже есть текст, добавляем разрыв страницы
    Если РезультирующийТабДок.ВысотаТаблицы > 0 Тогда
        РезультирующийТабДок.ВывестиГоризонтальныйРазделительСтраниц();
    КонецЕсли;
    
    РезультирующийТабДок.Вывести(ТекущийУчедомление);
КонецЦикла;

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

Решение 2. Вывод в разные окна через клиентскую часть

Как правильно было замечено в обсуждении, метод Показать() доступен только на клиенте. Чтобы открыть 20 разных окон, нам нужно обойти стандартный механизм возврата коллекции в БСП. Рассмотрим, как это реализовать.

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

  1. На клиенте получаем массив сотрудников из документа.
  2. В цикле вызываем серверную функцию, которая возвращает готовый ТабличныйДокумент для конкретного сотрудника.
  3. Сразу после получения документа на клиенте вызываем метод ТабДок.Показать("Уведомление: " + ФИО).

Пример клиентского кода в форме обработки:


&НаКлиенте
Процедура КомандаПечатьВРазныеОкна(Команда)
    Для каждого СотрудникСсылка Из МассивВыбранныхСотрудников Цикл
        // Получаем документ с сервера
        ТабДок = СформироватьПечатнуюФормуНаСервере(СотрудникСсылка);
        
        // Открываем отдельное окно для каждого
        ТабДок.ТолькоПросмотр = Истина;
        ТабДок.ОтображатьСетку = Ложь;
        ТабДок.Показать("Уведомление для " + СотрудникСсылка);
    КонецЦикла;
КонецПроцедуры

Важно: этот метод не рекомендуется использовать для большого количества сотрудников (более 10-15), так как создание большого количества окон может привести к значительному потреблению оперативной памяти клиентским приложением и зависанию интерфейса.

Решение 3. Программное сохранение и отправка по почте

Выясним, как реализовать задачу автора сообщения: не просто показать, а отправить по e-mail. Если открывать каждое окно вручную для отправки, это займет слишком много времени. БСП предоставляет инструменты для автоматизации этого процесса.

Выясним, как использовать функцию УправлениеПечатью.СформироватьПечатныеФормыДляОтправкиПоЭлектроннойПочте. Рассмотрим шаги:

  1. В модуле менеджера или обработки формируем структуру, где ключом будет уникальный идентификатор (например, ID сотрудника), а значением — его индивидуальный ТабличныйДокумент.
  2. Используем штатные средства для конвертации MXL в PDF или другие форматы прямо на сервере.
  3. Для каждого файла создаем письмо через РаботаСПочтовымиСообщениями.

Пример кода для сохранения во временные файлы на сервере:


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

// Далее работаем с файлом: прикрепляем к письму или сохраняем в папку
ДвоичныеДанныеФайла = Новый ДвоичныеДанные(ИмяФайла);
// Отправка...
УдалитьФайлы(ИмяФайла);

Решение 4. Использование уникальных имен в коллекции БСП

Разберем еще один «хитрый» способ. Если мы хотим, чтобы БСП все же открыла одно окно, но внутри него документы были разделены (в некоторых версиях это выглядит как дерево документов слева), мы можем попробовать генерировать уникальные имена макетов в коллекции.

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

Выводы и рекомендации

Проанализировав ситуацию, мы можем подвести итоги:

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

← На главную