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

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

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

Мы рассмотрим несколько различных вариантов реализации — от простых "быстрых решений" до гибких и масштабируемых систем на базе Библиотеки стандартных подсистем (БСП) и расширений конфигурации.

Вариант 1. Использование механизма дополнительных реквизитов (Рекомендуемый способ)

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

Рассмотрим по шагам, как это реализовать:

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

Проанализируем пример кода для модуля объекта внешней печатной формы, реализующий этот алгоритм:


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

После получения имени макета мы можем динамически передать его в стандартный метод получения области печати:


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

Вариант 2. Хранение индивидуальных макетов в справочнике "Файлы"

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

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

Разберем, как выглядит этот программный прием загрузки макета:


// Считаем двоичные данные прикрепленного файла MXL из справочника "Файлы"
ДвоичныеДанныеМакета = РаботаСФайлами.ДвоичныеДанныеФайла(СсылкаНаФайлИзСправочника);

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

Важный нюанс: Чтобы при таком подходе система не выдавала программных ошибок из-за отсутствия каких-либо параметров в макетах, созданных пользователями самостоятельно, рекомендуется использовать безопасное заполнение параметров через соответствие или структуру. Для этого мы можем передавать параметры вывода через метод Область.Параметры.Заполнить(). Все лишние переменные будут проигнорированы платформой, а нужные — заполнены без генерации исключений.

Вариант 3. Использование областей в одном макете

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

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


Если Контрагент = Справочники.Контрагенты.НайтиПоРеквизиту("ИНН", "7701020304") Тогда
    ОбластьШапки = Макет.ПолучитьОбласть("Шапка_Ромашка");
ИначеЕсли Контрагент = Справочники.Контрагенты.НайтиПоРеквизиту("ИНН", "7801020305") Тогда
    ОбластьШапки = ... // Загружаем другую специфичную область
Иначе
    ОбластьШапки = Макет.ПолучитьОбласть("Шапка_Стандарт");
КонецЕсли;

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

Вариант 4. Стандартный механизм БСП "Шаблоны договоров"

Нельзя забывать и про штатные возможности современных конфигураций 1С (ERP, КА, УТ 11, УНФ). Если ваша задача связана с печатью договоров, спецификаций к заказам клиентов или соглашений, вы можете обойтись вообще без написания кода, используя типовой справочник Шаблоны договоров.

В этом механизме вы можете загружать макеты в формате Microsoft Word (.docx) или OpenOffice (.odt) непосредственно в пользовательском режиме. Платформа самостоятельно заменит специальные текстовые теги (например, {Контрагент.Наименование}, {СуммаДокумента}) на реальные значения из базы данных 1С при выводе на печать. Для каждого контрагента в его карточке договора просто прикрепляется свой индивидуальный шаблон.

Вариант 5. Динамическая подмена макета через Расширения конфигурации

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

С помощью аннотации &Вместо для процедуры печати конкретного документа, расширение анализирует контрагента и динамически подсовывает нужный макет из внутренней коллекции макетов самого расширения или из внешнего хранилища:


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

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

Выбирайте наиболее подходящий для вашего случая метод в зависимости от масштаба задачи, квалификации пользователей и требований по частоте изменения печатных форм клиентов!

← На главную