При работе с современными конфигурациями, такими как 1С:ERP, Комплексная автоматизация или Управление торговлей 11, разработчики часто сталкиваются с трудностями при поиске механизмов печати регламентированных форм. Задача кажется простой: вывести дополнительное поле (например, из договора контрагента) в печатную форму "Счет-фактура выданный". Однако, открыв модуль документа, мы часто не находим там ожидаемого кода формирования макета. Разберем подробно, как устроена эта подсистема и как правильно реализовать доработку, не нарушая обновляемость конфигурации.
Первое, с чем мы сталкиваемся — это отсутствие макета в самом документе СчетФактураВыданныйАванс или СчетФактураВыданный. Это связано с архитектурным решением 1С для унификации печатных форм. Поскольку форма счета-фактуры (постановления №1137, №534 и др.) едина для всех видов операций (реализация, аванс, корректировка), она вынесена в общие механизмы.
Давайте определим, где искать исходные объекты:
ПФ_MXL_СчетФактура.... Обычно актуальный макет содержит в имени номер постановления или год (например, ПФ_MXL_СчетФактура1137_2017).ПечатьОбщихФорм или в общих модулях, связанных с учетом НДС (например, УчетНДСВызовСервера, УчетНДСФормированиеПечатныхФорм).Модуль менеджера конкретного документа (например, СчетФактураВыданныйАванс) обычно содержит лишь "точку входа" — процедуру, которая подготавливает данные и передает управление в общий механизм печати.
Рассмотрим два основных пути решения задачи, которые обсуждались разработчиками, и выберем оптимальный.
Вариант 1: Внешняя печатная форма (ВПФ)
Традиционный подход, при котором мы копируем весь типовой код и макет во внешнюю обработку.
Минусы: Код формирования счета-фактуры в ERP чрезвычайно сложен. Он содержит огромные запросы, временные таблицы и логику, разбросанную по нескольким общим модулям. При каждом обновлении законодательства (новая строка в СФ, счет-фактура в редакции постановления 26, прослеживаемость, ГТД) типовой код меняется. Если мы используем ВПФ, наша форма мгновенно устаревает, и мы вынуждены заново копировать и адаптировать код. Это трудоемко и рискованно.
Вариант 2: Расширение конфигурации
Это наиболее рекомендуемый способ для современных конфигураций. Если, как в нашем случае, дополнительное поле (реквизит договора) уже добавлено через расширение, то логично и логику печати разместить там же. Это позволяет избежать дублирования типового кода и точечно внедрить изменения.
Мы будем использовать метод "пост-обработки" табличного документа. Это самый безопасный способ: мы даем типовому механизму сформировать стандартную печатную форму, а затем программно находим нужное место и вписываем туда наше значение. Это гарантирует, что при обновлении релизов 1С (если не изменилась структура самого макета визуально) наш код продолжит работать.
Убедимся, что наше расширение активно и в нем отключен "Безопасный режим" (если мы планируем использовать серверные вызовы, которые могут требовать привилегий, хотя для печати это не всегда критично). Реквизит договора, значение которого мы хотим вывести, уже должен быть в расширении.
Нам необходимо найти процедуру, которая возвращает готовый табличный документ. В модуле менеджера Обработка.ПечатьОбщихФорм или в модуле документа (в зависимости от релиза и точки вызова) найдем процедуру печати. Часто используется перехват метода, формирующего коллекцию печатных форм.
Однако, для точечного изменения удобнее использовать аннотацию &После для процедуры формирования конкретной печатной формы. Если мы говорим о механизме БСП, то часто перехватывают процедуру в модуле менеджера документа, например Печать(), но там формируется коллекция. Более глубокий уровень — перехват процедур заполнения.
Рассмотрим универсальный вариант: перехват в Общем модуле, отвечающем за формирование СФ, или использование события ОбработкаПечати в модуле менеджера, если мы хотим модифицировать результат.
Но самый надежный способ для вставки "сверху" — это найти процедуру, которая строит табличный документ. Например, в УчетНДСФормированиеПечатныхФорм. Но проще всего "поймать" готовый табличный документ на выходе из процедуры печати в модуле менеджера объекта (если она экспортная) или внедриться в процесс заполнения.
Давайте используем подход изменения уже сформированного документа через расширение модуля менеджера документа СчетФактураВыданныйАванс. Найдем там процедуру Печать.
&После("Печать")
Процедура Расш_Печать(МассивОбъектов, ПараметрыПечати, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода)
// Перебираем коллекцию, чтобы найти нужную нам форму Счета-фактуры
Для Каждого СтрокаКоллекции Из КоллекцияПечатныхФорм Цикл
// Проверяем имя макета (обычно это "СчетФактура")
Если СтрокаКоллекции.ИмяВРЕГ = ВРег("СчетФактура") ИЛИ СтрокаКоллекции.ИмяВРЕГ = "СЧЕТФАКТУРА1137" Тогда
// Получаем сформированный табличный документ
ТабДок = СтрокаКоллекции.ТабличныйДокумент;
// Вызываем нашу процедуру доработки
ДополнитьПечатнуюФормуСчетФактура(ТабДок, МассивОбъектов);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Теперь напишем процедуру, которая непосредственно вносит правки. Поскольку данные для печати (массив объектов) у нас есть, мы можем сделать запрос к базе данных, чтобы получить значение нашего добавленного реквизита.
Важный нюанс: Типовой запрос формирования печатной формы ничего не знает о полях, добавленных вами в расширении. Поэтому нам придется сделать собственный маленький запрос.
Процедура ДополнитьПечатнуюФормуСчетФактура(ТабДок, МассивОбъектов)
// 1. Получаем данные нашего доп. реквизита запросом
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| СФ.Ссылка КАК Ссылка,
| СФ.ДокументОснование.ДоговорКонтрагента.Расш_НашДопРеквизит КАК ЗначениеРеквизита
|ИЗ
| Документ.СчетФактураВыданныйАванс КАК СФ
|ГДЕ
| СФ.Ссылка В(&МассивОбъектов)";
Запрос.УстановитьПараметр("МассивОбъектов", МассивОбъектов);
Выборка = Запрос.Выполнить().Выбрать();
// Поскольку в массиве может быть несколько документов (пакетная печать),
// нам нужно понимать, какая область табличного документа к какому документу относится.
// Обычно области разделены горизонтальными разрывами, но надежнее ориентироваться на параметры области.
// Однако, простой метод для одиночной печати или простой замены — поиск текста.
Пока Выборка.Следующий() Цикл
// Для простоты примера рассмотрим вставку в конкретную ячейку.
// Нам нужно найти место в макете. Допустим, мы хотим вывести это после "К платежно-расчетному документу".
// Ищем область ячеек, содержащую определенный текст
НайденаяОбласть = ТабДок.НайтиТекст("К платежно-расчетному документу", , , Истина);
Если НайденаяОбласть <> Неопределено Тогда
// Определяем координаты найденной ячейки
Верх = НайденаяОбласть.Верх;
Лево = НайденаяОбласть.Лево;
// Смещаемся, например, на одну строку вниз или вправо, туда где пусто
// Или используем именованную область, если она есть в макете
// Пример: Записываем в ячейку справа от найденного текста
ОбластьВывода = ТабДок.Область(Верх, Лево + 5); // Смещение подбирается экспериментально
ОбластьВывода.Текст = "Наш договор: " + Строка(Выборка.ЗначениеРеквизита);
// Если нужно вставить новую строку (раздвинуть макет):
// ТабДок.ВставитьОбласть(..., ...); - но это может нарушить форматирование регламентированной формы
КонецЕсли;
КонецЦикла;
КонецПроцедуры
В сообщениях обсуждения (особенно сообщение 14) упоминался более "тонкий" метод. В некоторых конфигурациях (зависит от версии БСП и ERP) в модулях менеджеров есть процедуры вроде ЗаполнитьРеквизитыПодвалаСчетФактура.
Если такие процедуры доступны и экспортны, их можно перехватить через &После. В параметрах этих процедур обычно передается структура данных, которая затем выводится в макет.
Пример логики:
Тут возникает проблема: типовой макет не содержит параметра для нашего поля. Значит, нам придется:
ТабличногоДокумента, описанный выше. Он не требует правки макета.Подведем итог. Чтобы вывести поле из договора (который доработан в расширении) в печатную форму счета-фактуры на аванс:
&После).НайтиТекст или обращение по адресам ячеек, чтобы вставить ваше значение в нужное место формы. Это сохранит легитимность формы при обновлении законодательства, так как "скелет" формы останется типовым.