В процессе автоматизации бизнеса на платформе 1С предприятия довольно часто сталкиваются с ситуацией, когда крупные клиенты или сетевые контрагенты требуют предоставлять им печатные формы документов (такие как счета, накладные, акты или УПД) по их собственным уникальным шаблонам. Стандартный механизм печати в 1С выводит один и тот же макет для всех участников процесса. В этой статье мы подробно разберем, как решить эту задачу без изменения типовой конфигурации, используя современные и надежные архитектурные подходы.
Мы рассмотрим несколько различных вариантов реализации — от простых "быстрых решений" до гибких и масштабируемых систем на базе Библиотеки стандартных подсистем (БСП) и расширений конфигурации.
Этот подход является наиболее сбалансированным по соотношению трудозатрат программиста и удобства последующего администрирования. Суть метода заключается в том, что мы не создаем десятки внешних обработок печатных форм, а используем одну универсальную внешнюю печатную форму (ВПФ). Выбор нужного макета внутри этой формы привязывается к дополнительному реквизиту контрагента.
Рассмотрим по шагам, как это реализовать:
Контрагенты дополнительный реквизит с именем "Макет" (или "ВариантПечатнойФормы"). Типом значения этого реквизита можно сделать простой "Дополнительное значение" (список строк с названиями макетов) или ссылку на справочник Файлы.Проанализируем пример кода для модуля объекта внешней печатной формы, реализующий этот алгоритм:
// Функция определяет имя макета для конкретного контрагента
Функция ПолучитьИмяМакетаДляКонтрагента(СсылкаНаКонтрагента)
ИмяМакета = "СтандартныйМакет"; // По умолчанию
// Получаем значение дополнительного реквизита "Макет"
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| КонтрагентыДополнительныеРеквизиты.Значение КАК ЗначениеМакета
|ИЗ
| Справочник.Контрагенты.ДополнительныеРеквизиты КАК КонтрагентыДополнительныеРеквизиты
|ГДЕ
| КонтрагентыДополнительныеРеквизиты.Ссылка = &СсылкаНаКонтрагента
| И КонтрагентыДополнительныеРеквизиты.Свойство.Наименование = ""Макет""";
Запрос.УстановитьПараметр("СсылкаНаКонтрагента", СсылкаНаКонтрагента);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Если ЗначениеЗаполнено(Выборка.ЗначениеМакета) Тогда
ИмяМакета = Строка(Выборка.ЗначениеМакета);
КонецЕсли;
КонецЕсли;
Возврат ИмяМакета;
КонецФункции
После получения имени макета мы можем динамически передать его в стандартный метод получения области печати:
ИмяНужногоМакета = ПолучитьИмяМакетаДляКонтрагента(ДокументОбъект.Контрагент);
Попытка
Макет = ПолучитьМакет(ИмяНужногоМакета);
Исключение
// Защита на случай, если в реквизите указали несуществующее имя макета
Макет = ПолучитьМакет("СтандартныйМакет");
КонецПопытки;
Что делать, если макеты контрагентов постоянно меняются, добавляются новые колонки или логотипы, а привлекать программиста каждый раз для обновления кода внешней обработки нерационально? В таком случае мы можем хранить макеты прямо в пользовательской базе данных в формате табличных документов (файлы с расширением .mxl) внутри типового справочника Файлы.
В этом сценарии в дополнительном реквизите контрагента мы будем указывать прямую ссылку на конкретный элемент справочника Файлы. При формировании печатной формы наша обработка будет программно считывать двоичные данные прикрепленного файла макета и загружать его в память "на лету".
Разберем, как выглядит этот программный прием загрузки макета:
// Считаем двоичные данные прикрепленного файла MXL из справочника "Файлы"
ДвоичныеДанныеМакета = РаботаСФайлами.ДвоичныеДанныеФайла(СсылкаНаФайлИзСправочника);
Если ДвоичныеДанныеМакета <> Неопределено Тогда
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("mxl");
ДвоичныеДанныеМакета.Записать(ИмяВременногоФайла);
// Инициализируем и считываем табличный документ
МакетТабличногоДокумента = Новый ТабличныйДокумент;
МакетТабличногоДокумента.Прочитать(ИмяВременногоФайла);
// Удаляем временный файл с диска после загрузки в ОЗУ
УдалитьФайлы(ИмяВременногоФайла);
Иначе
// Если файл не найден, берем дефолтный макет из состава обработки
МакетТабличногоДокумента = ПолучитьМакет("СтандартныйМакет");
КонецЕсли;
Важный нюанс: Чтобы при таком подходе система не выдавала программных ошибок из-за отсутствия каких-либо параметров в макетах, созданных пользователями самостоятельно, рекомендуется использовать безопасное заполнение параметров через соответствие или структуру. Для этого мы можем передавать параметры вывода через метод Область.Параметры.Заполнить(). Все лишние переменные будут проигнорированы платформой, а нужные — заполнены без генерации исключений.
Если уникальных контрагентов не так много (например, всего 3-5 крупных клиентов со своими специфическими требованиями), то нет необходимости усложнять архитектуру хранением файлов. Мы можем создать в нашей ВПФ один большой макет, но разделить его на разные именованные области.
Например, вместо одной стандартной области Шапка мы создадим области Шапка_Стандарт, Шапка_Ромашка, Шапка_Вектор. При выводе печатной формы мы будем анализировать контрагента и компоновать итоговый табличный документ только из тех именных областей, которые соответствуют нашему покупателю:
Если Контрагент = Справочники.Контрагенты.НайтиПоРеквизиту("ИНН", "7701020304") Тогда
ОбластьШапки = Макет.ПолучитьОбласть("Шапка_Ромашка");
ИначеЕсли Контрагент = Справочники.Контрагенты.НайтиПоРеквизиту("ИНН", "7801020305") Тогда
ОбластьШапки = ... // Загружаем другую специфичную область
Иначе
ОбластьШапки = Макет.ПолучитьОбласть("Шапка_Стандарт");
КонецЕсли;
Этот способ прост в реализации, однако имеет существенный минус: при добавлении нового уникального контрагента программисту придется открывать конфигуратор, рисовать новые области в макете и дополнять условный код выбора областей.
Нельзя забывать и про штатные возможности современных конфигураций 1С (ERP, КА, УТ 11, УНФ). Если ваша задача связана с печатью договоров, спецификаций к заказам клиентов или соглашений, вы можете обойтись вообще без написания кода, используя типовой справочник Шаблоны договоров.
В этом механизме вы можете загружать макеты в формате Microsoft Word (.docx) или OpenOffice (.odt) непосредственно в пользовательском режиме. Платформа самостоятельно заменит специальные текстовые теги (например, {Контрагент.Наименование}, {СуммаДокумента}) на реальные значения из базы данных 1С при выводе на печать. Для каждого контрагента в его карточке договора просто прикрепляется свой индивидуальный шаблон.
Для продвинутых разработчиков наиболее технологичным и чистым решением является использование механизмов Расширения конфигурации. Вместо регистрации сотен внешних печатных форм, вы можете перехватить стандартную процедуру формирования печатной формы в модуле менеджера документа.
С помощью аннотации &Вместо для процедуры печати конкретного документа, расширение анализирует контрагента и динамически подсовывает нужный макет из внутренней коллекции макетов самого расширения или из внешнего хранилища:
&Вместо("СформироватьПечатнуюФорму")
Процедура Расш_СформироватьПечатнуюФорму(ПараметрыПечати, КоллекцияПечатныхФорм)
// Вызываем оригинальный алгоритм, если нет индивидуального макета
Если Не ТребуетсяОсобыйМакет(ПараметрыПечати.СсылкаНаДокумент) Тогда
ПродолжитьВызов(ПараметрыПечати, КоллекцияПечатныхФорм);
Возврат;
КонецЕсли;
// Сюда помещаем наш собственный алгоритм формирования печатной формы
// с использованием персонального макета из расширения
...
КонецПроцедуры
Такой подход гарантирует стабильность работы системы, централизует весь код управления печатью в одном месте и сохраняет конфигурацию на полной поддержке поставщика обновлений.
Выбирайте наиболее подходящий для вашего случая метод в зависимости от масштаба задачи, квалификации пользователей и требований по частоте изменения печатных форм клиентов!