Кто вообще любит клепать печатные формы? Вот я точно терпеть не могу. Коллега вчера мучился с печатной формой, которую обязательно необходимо было сделать на базе макета в Word.
Я ему помог, показав, как можно сделать макет договора в Word при помощи механизмов DocVariables. Но будем честны – это изобретение велосипеда.
1С: Библиотека стандартных подсистем – лучший друг 1С: Программиста. Поэтому давайте переделаем нормально. И разработаем шаблон ВПФ для Word файлов (.docx) для подключения к 1С
Шаг 1. Разработка макета
Вот как он выглядит в Word.

Полагаю, что и так всё в целом понятно, но давайте всё таки посмотрим на основные фишки.
{v8 Область.ЗдесьПишемИмяОбласти} – начало области
{/v8 Область.ЗдесьПишемИмяОбласти} – это конец области. Обрати внимание на слеш в начале
{v8 ИмяПараметра} – так определяем имя параметра
Области могут быть предопределенными. Такие как колонтитулы верхние, нижние и прочие.
Шаг 2. Разработка внешней обработки.
В модуле объекта внешней обработки пишем данные для подключения нашей ВПФ к командам печати.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
Функция СведенияОВнешнейОбработке() Экспорт ВерсияБСП = "3.0.1.0"; ПараметрыРегистрации = ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке(ВерсияБСП); ПараметрыРегистрации.Информация = НСтр("ru = 'Договор (Word)'"); ПараметрыРегистрации.Вид = ДополнительныеОтчетыИОбработкиКлиентСервер.ВидОбработкиПечатнаяФорма(); ПараметрыРегистрации.Версия = "1.0.0.0"; ПараметрыРегистрации.Назначение.Добавить("Документ.ЗаказПокупателя"); Команда = ПараметрыРегистрации.Команды.Добавить(); Команда.Представление = НСтр("ru = 'Договор (Word)'"); Команда.Идентификатор = "ПечатьДоговораWord"; Команда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыВызовСерверногоМетода(); Возврат ПараметрыРегистрации; КонецФункции Процедура Печать(МассивОбъектов, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт ПечатнаяФорма = УправлениеПечатью.СведенияОПечатнойФорме(КоллекцияПечатныхФорм, "ПечатьДоговораWord"); Если ПечатнаяФорма <> Неопределено Тогда ОфисныеДокументы = НапечататьДоговор(МассивОбъектов); ПечатнаяФорма.СинонимМакета = НСтр("ru = 'Печать договора (Word)'"); ПечатнаяФорма.ОфисныеДокументы = ОфисныеДокументы; КонецЕсли; КонецПроцедуры |
Код в том же модуле объекта. Но уже сам код для печати документов.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
Функция НапечататьДоговор(МассивОбъектов) ОфисныеДокументы = Новый Соответствие; МакетДокумента = ПолучитьМакет("ПФ_DOCX_Макет"); Шаблон = НСтр("ru = 'Заказ покупателя №[Номер] от [Дата]'"); Макет = УправлениеПечатью.ИнициализироватьМакетОфисногоДокумента(МакетДокумента, Неопределено); Если Макет = Неопределено Тогда Возврат ""; КонецЕсли; ОписаниеОбластей = Новый Структура; УправлениеПечатью.ДобавитьОписаниеОбласти(ОписаниеОбластей, "ВерхнийКолонтитул", "ВерхнийКолонтитул"); УправлениеПечатью.ДобавитьОписаниеОбласти(ОписаниеОбластей, "ВерхнийЧетныйКолонтитул", "ВерхнийЧетныйКолонтитул"); УправлениеПечатью.ДобавитьОписаниеОбласти(ОписаниеОбластей, "Шапка", "Общая"); УправлениеПечатью.ДобавитьОписаниеОбласти(ОписаниеОбластей, "ШапкаТаблицы", "СтрокаТаблицы"); УправлениеПечатью.ДобавитьОписаниеОбласти(ОписаниеОбластей, "СтрокаТаблицы", "СтрокаТаблицы"); УправлениеПечатью.ДобавитьОписаниеОбласти(ОписаниеОбластей, "Подвал", "Общая"); УправлениеПечатью.ДобавитьОписаниеОбласти(ОписаниеОбластей, "НижнийКолонтитул", "НижнийКолонтитул"); УправлениеПечатью.ДобавитьОписаниеОбласти(ОписаниеОбластей, "НижнийЧетныйКолонтитул", "НижнийЧетныйКолонтитул"); ДанныеПоШапке = ПолучитьДанныеДляПечати(МассивОбъектов); Для Каждого ДанныеПечати Из ДанныеПоШапке Цикл ДанныеПечати.Дата = Формат(ДанныеПечати.Дата, "ДЛФ=DD"); ПечатнаяФорма = УправлениеПечатью.ИнициализироватьПечатнуюФорму(Неопределено, Неопределено, Макет); Если ПечатнаяФорма = Неопределено Тогда УправлениеПечатью.ОчиститьСсылки(Макет); Возврат ОфисныеДокументы; КонецЕсли; // Вывод верхних колонтитулов Область = УправлениеПечатью.ОбластьМакета(Макет, ОписаниеОбластей["ВерхнийКолонтитул"]); УправлениеПечатью.ПрисоединитьОбластьИЗаполнитьПараметры(ПечатнаяФорма, Область, ДанныеПечати); // Для четных страниц свои колонтитулы Область = УправлениеПечатью.ОбластьМакета(Макет, ОписаниеОбластей["ВерхнийЧетныйКолонтитул"]); УправлениеПечатью.ПрисоединитьОбластьИЗаполнитьПараметры(ПечатнаяФорма, Область, ДанныеПечати); // Вывод верхней части документа - обычная область с параметрами. Область = УправлениеПечатью.ОбластьМакета(Макет, ОписаниеОбластей["Шапка"]); УправлениеПечатью.ПрисоединитьОбластьИЗаполнитьПараметры(ПечатнаяФорма, Область, ДанныеПечати); // Вывод коллекции данных из информационной базы в виде таблицы. Область = УправлениеПечатью.ОбластьМакета(Макет, ОписаниеОбластей["ШапкаТаблицы"]); УправлениеПечатью.ПрисоединитьОбласть(ПечатнаяФорма, Область, Ложь); // Вывод строка ТЧ ДанныеПечатиТовары = ОбщегоНазначения.ТаблицаЗначенийВМассив(ДанныеПечати.Ссылка.Запасы.Выгрузить()); Область = УправлениеПечатью.ОбластьМакета(Макет, ОписаниеОбластей["СтрокаТаблицы"]); УправлениеПечатью.ПрисоединитьИЗаполнитьКоллекцию(ПечатнаяФорма, Область, ДанныеПечатиТовары); // Вывод подвала документа Область = УправлениеПечатью.ОбластьМакета(Макет, ОписаниеОбластей["Подвал"]); УправлениеПечатью.ПрисоединитьОбластьИЗаполнитьПараметры(ПечатнаяФорма, Область, ДанныеПечати); // Вывод нижних колонтитулов Область = УправлениеПечатью.ОбластьМакета(Макет, ОписаниеОбластей["НижнийКолонтитул"]); УправлениеПечатью.ПрисоединитьОбластьИЗаполнитьПараметры(ПечатнаяФорма, Область, ДанныеПечати); // Для четных страниц свои колонтитулы Область = УправлениеПечатью.ОбластьМакета(Макет, ОписаниеОбластей["НижнийЧетныйКолонтитул"]); УправлениеПечатью.ПрисоединитьОбластьИЗаполнитьПараметры(ПечатнаяФорма, Область, ДанныеПечати); АдресХранилищаПечатнойФормы = УправлениеПечатью.СформироватьДокумент(ПечатнаяФорма); УправлениеПечатью.ОчиститьСсылки(ПечатнаяФорма); ЗначенияРеквизитовДокумента = Новый Структура("Организация,Номер,Дата,Ссылка"); ЗаполнитьЗначенияСвойств(ЗначенияРеквизитовДокумента, ДанныеПечати); ЗначенияРеквизитовДокумента.Дата = Формат(ЗначенияРеквизитовДокумента.Дата, "ДЛФ=D"); ЗначенияРеквизитовДокумента.Номер = ПрефиксацияОбъектовКлиентСервер.НомерНаПечать(ЗначенияРеквизитовДокумента.Номер); ИмяДокумента = СтроковыеФункцииКлиентСервер.ВставитьПараметрыВСтроку(Шаблон, ЗначенияРеквизитовДокумента); ОфисныеДокументы.Вставить(АдресХранилищаПечатнойФормы, ИмяДокумента); КонецЦикла; УправлениеПечатью.ОчиститьСсылки(Макет); Возврат ОфисныеДокументы; КонецФункции Функция ПолучитьДанныеДляПечати(МассивОбъектов) Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ | &ДатаПечати КАК ДатаПечати, | &Город КАК Город, | ЗаказПокупателя.Дата КАК Дата, | ЗаказПокупателя.Номер КАК Номер, | ЗаказПокупателя.Ссылка КАК Ссылка, | ЗаказПокупателя.Договор.НомерДоговора КАК НомерДоговора, | ЗаказПокупателя.Договор.ДатаДоговора КАК ДатаДоговора, | ЗаказПокупателя.Договор.Владелец.ИНН КАК ИннПокупатель, | ЗаказПокупателя.Договор.Организация.ИНН КАК ИннПоставщик, | ЗаказПокупателя.Контрагент.Наименование КАК Покупатель, | ЗаказПокупателя.Организация.Наименование КАК Поставщик |ИЗ | Документ.ЗаказПокупателя КАК ЗаказПокупателя |ГДЕ | ЗаказПокупателя.Ссылка В(&МассивДоговоров)"; Запрос.УстановитьПараметр("Город", "г. Петропавловск-Камчатский"); Запрос.Параметры.Вставить("ДатаПечати", ТекущаяДата()); Запрос.Параметры.Вставить("МассивДоговоров", МассивОбъектов); Возврат ОбщегоНазначения.ТаблицаЗначенийВМассив(Запрос.Выполнить().Выгрузить());; КонецФункции |
Не забываем вставить макет вордовского файла как Двоичные данные.

Подключаем как ВПФ и радуемся


Не всем может быть очевидно, что в ПрисоединитьИЗаполнитьКоллекцию(ПечатнаяФорма, Область, ДанныеПечатиТовары) в качестве данных печати нужно передать массив структур. Для того, чтобы преобразовать табличную часть в Массив Структур можно воспользоваться функцией из пакета БСП ОбщегоНазначения.ТаблицаЗначенийВМассив() и передать туда табличную часть.
1 2 3 |
ДанныеПечатиТовары = ОбщегоНазначения.ТаблицаЗначенийВМассив(ДанныеПечати.Ссылка.Запасы.Выгрузить()); Область = УправлениеПечатью.ОбластьМакета(Макет, ОписаниеОбластей["СтрокаТаблицы"]); УправлениеПечатью.ПрисоединитьИЗаполнитьКоллекцию(ПечатнаяФорма, Область, ДанныеПечатиТовары); |
Шаг 3. Сделать сносную возможность отладки
Всё конечно круто, но постоянно переподключать ВПФку для отладки – мягко говоря гемморойненько. Давайте допилим форму обработки.
- Добавим реквизит ЗаказПокупателя
- Создадим форму обработки
- Добавим комманду печати
- В свойствах формы установим автосохранение реквизита
В модуле формы опишем команду и вызов программы Word.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
&НаКлиенте Процедура Печать(Команда) ОфисныеДокументы = ПечатьНаСервере(); Для каждого Документ Из ОфисныеДокументы Цикл ДокументДвоичныеДанные = ПолучитьИзВременногоХранилища(Документ.Ключ); ИмяФайла = ПолучитьИмяВременногоФайла(".docx"); ДокументДвоичныеДанные.Записать(ИмяФайла); Word = ПолучитьCOMОбъект(ИмяФайла); Word.Application.Visible = 1; Word.Activate(); КонецЦикла; КонецПроцедуры &НаСервере Функция ПечатьНаСервере() Обработка = РеквизитФормыВЗначение("Объект"); МассивОбъектов = Новый Массив; МассивОбъектов.Добавить(Объект.ЗаказПокупателя); ПараметрыПечати = Новый Структура("ДополнитьКомплектВнешнимиПечатнымиФормами", Ложь); КоллекцияПечатныхФорм = УправлениеПечатью.ПодготовитьКоллекциюПечатныхФорм("ПечатьДоговораWord"); Обработка.Печать(МассивОбъектов, КоллекцияПечатныхФорм, Неопределено, Неопределено); Возврат КоллекцияПечатныхФорм[0].ОфисныеДокументы; КонецФункции |
Важно понимать, что этот пример сделан с некоторыми упрощениями без акцента на производительность кода.
Вывод картинок
Дополнение к статье с инструкцией как всё таки выводить картинки. В документации к БСП прямо не говориться о том как выводить картинки в макет, однако метод УправлениеПечатьюСлужебный.ЗаполнитьПараметрыОбласти(ПечатнаяФорма, Область, ДанныеОбъекта) нам прямо намекает, что картинки в макет выводить можно.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// УправлениеПечатьюСлужебный.ЗаполнитьПараметрыОбласти(ПечатнаяФорма, Область, ДанныеОбъекта) ........................ Если ВыводТекста Тогда ЗаписатьТекстВПотоки(СтруктураРазбораXML, ЧтениеXML, "Область", ТекстУзла, УстановленАтрибутСохраненияПробелов); ИначеЕсли ВыводКартинки Тогда ДвоичныеДанные = ПолучитьИзВременногоХранилища(ТекстУзла); // ДвоичныеДанные - СтруктураКартинка = Новый Структура; СтруктураКартинка.Вставить("ДвоичныеДанные", ДвоичныеДанные); СтруктураКартинка.Вставить("ИмяКартинки", "image"); СтруктураКартинка.Вставить("КаталогКартинок", ПечатнаяФорма.СтруктураДокумента.КаталогКартинок); ПараметрыКартинки = ПолучитьАтрибутыИзображения(ДвоичныеДанные); ........................ |
Расковыряв модуль, можно понять, что для заполнения картинки система хочет получить адрес на временное хранилище с картинкой.
Приступаем. Сначала нужно модифицируем наш макет. Добавим колонку “Изображение” и параметр “Рисунок” (название не важно)

Вспоминаем, в каком месте у нас был вывод строк табличной части
1 2 3 4 5 6 7 8 |
............................... // Вывод строка ТЧ ДанныеПечатиТовары = ОбщегоНазначения.ТаблицаЗначенийВМассив(ДанныеПечати.Ссылка.Запасы.Выгрузить()); Область = УправлениеПечатью.ОбластьМакета(Макет, ОписаниеОбластей["СтрокаТаблицы"]); УправлениеПечатью.ПрисоединитьИЗаполнитьКоллекцию(ПечатнаяФорма, Область, ДанныеПечатиТовары); ............................... |
ДанныеПечатиТовары – этот массив структур нам нужно дополнить, ссылкой на временное хранилище, которое будет содержать нашу картинку в виде двоичных данных.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
Функция НапечататьДоговор(МассивОбъектов) // Какой то код ............................................ // Вывод строка ТЧ ДанныеПечатиТовары = ОбщегоНазначения.ТаблицаЗначенийВМассив(ДанныеПечати.Ссылка.Запасы.Выгрузить()); ДополнитьДаннымиКартинки(ДанныеПечатиТовары); Область = УправлениеПечатью.ОбластьМакета(Макет, ОписаниеОбластей["СтрокаТаблицы"]); УправлениеПечатью.ПрисоединитьИЗаполнитьКоллекцию(ПечатнаяФорма, Область, ДанныеПечатиТовары); ............................................ // продолжение какого то кода КонецФункции Процедура ДополнитьДаннымиКартинки(МассивДанныеПечатиТовары) Для каждого Элемент Из МассивДанныеПечатиТовары Цикл Если Элемент.Номенклатура.ФайлКартинки.Пустая() Тогда Картинка = Неопределено; Иначе Картинка = РаботаСФайлами.ДанныеФайла(Элемент.Номенклатура.ФайлКартинки).СсылкаНаДвоичныеДанныеФайла; КонецЕсли; Элемент.Вставить("Рисунок", Картинка); КонецЦикла; КонецПроцедуры |
И вуаля!

Заключение
Учитывайте, что приведенный мною код далёк от состояния оптимального и производительного. Здесь я его пытался максимально упростить, чтобы начинающим программистам было проще понять концепцию того, как работает подсистема печати в БСП.
А вообще, разработка печатной формы в MS Word средствами БСП это не сложнее, чем обычная печатная форма. А в некоторых местах даже проще.