6.2. Создание и автоматическое заполнение бланков стандартных документов Создание vba-программ
Пример 39. При оплате за обучение студент вносит деньги в кассу, где бухгалтер заполняет от руки квиток об оплате. Автоматизировать ручную работу бухгалтера, создав форму, заполняющую карточку оплаты, которую можно при необходимости отправить на печать.
Технология выполнения
Интерфейс шаблона и приложения
В качестве интерфейса сначала рассмотрим самую простую форму приложения, отвечающего условиям задачи и включающего в себя соответствующие текстовые поля ввода и рабочие кнопки (рис. 85).
Рис. 85. Форма примера 39 в рабочем состоянии
Далее рассмотрим, как автоматизировать печать квитанции об оплате за обучение.
Начнем с разработки бланка стандартного документа (печатной формы). Эту форму можно сделать на базе шаблона с текстовыми полями, в которые будут заноситься изменяющиеся сведения. Для этого выполните следующие команды: Файл + Создать. В правой части окна нового документа выберите раздел Шаблоны и гиперссылку На моем компьютере (рис. 86). После чего появится диалоговое окно Шаблоны (рис. 87), в котором на вкладке Общие необходимо выделить пиктограмму Новый документ и поставить переключатель Шаблон в рамке Создать.
Включите панель Формы (Вид + Панели инструментов + Формы). Для дальнейшей работы понадобится только один элемент этой панели – Текстовое поле

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

Рис. 87. Диалоговое окно Шаблоны
Создайте шаблон документа, придерживаясь рис. 88.

Рис. 88. Шаблон документа word, в который вносятся данные для печати
В некоторых полях (поля выделены серым фоном) можно ввести значения по умолчанию. Значение по умолчанию устанавливается при помощи диалогового окна Свойства, которое можно вывести на экран следующим образом:
• щелкнуть правой клавишей мыши по необходимому текстовому полю;
• в появляющемся контекстном меню выбрать Свойства;
• указать значение по умолчанию (рис. 89).
Значения по умолчанию позволяют менять только те данные, которые должны изменяться нечасто. Например, оплата за обучение в основном одна и та же – 1500 руб., поэтому значение этого поля будет меняться не очень часто.

Рис. 89. Установка параметров текстового поля «сумма_опл»
Рассмотрим и другие необходимые элементы рабочего окна «Параметры текстового поля». В поле Тип указывается тип значения, которое можно помещать в поле. Можно заполнить поле Максимальная длина, хотя наиболее важным для дальнейшего использования кодом VBA является поле Закладка. Значение этого поля будет служить меткой, в которую должен устанавливаться курсор, перед помещением в документ очередной порции информации. Поэтому, как только все необходимые поля определены, нужно указать для каждого из них осмысленные метки. Для бланка документа Word, приведенного на рис. 86, зададим (последовательно) такие метки:
Фамилия – фамилия студента;
Имя – имя студента;
Отчество – отчество студента;
Группа – наименование группы;
Месяц_опл – название месяца, за который вносится оплата;
Сумма_опл – внесенная сумма;
ФИО_бух – фамилия принявшего бухгалтера;
Дата_опл – дата оплаты.
После окончания разработки формы документа сохраним его под именем Оплата за учебу в каталоге Шаблоны. На рис. 88 представлен шаблон созданного документа с изменяющимися полями.
Создание модуля Печать для шаблона документа
Далее необходимо перейти в редактор VBA и создать форму приложения, отвечающего условиям задачи (рис. 85).
В листинге представлен код процедуры печати бланка квитанции об оплате, сопутствующие и отладочные процедуры.
Листинг (занести в Модуль1 проекта Оплата за учебу)
OptioNExplicit
OptioNBase 1
‘ переменные формы приложения
Public фам1 As String
Public имя1 As String
Public отчество1 As String
Public групп1 As String
Public месяц As String
Public сумма As String
Public бух As String
Public дата As String
‘ переменные формы шаблона документа
Public фамилия As String
Public имя As String
Public отчество As String
Public группа As String
Public месяц_опл As String
Public сумма_опл As String
Public фио_бух As String
Public дата_опл As String
Dim book(8) As String
Dim dataMas(8) As String
Dim i As Integer
Sub Печать() ‘процедура печати бланка
‘Заносим информацию в массивы bookmarmas, datamas
book(1) = «фамилия»: dataMas(1) = фамилия
book(2) = «имя»: dataMas(2) = имя
book(3) = «отчество»: dataMas(3) = отчество
book(4) = «группа»: dataMas(4) = группа
book(5) = «месяц_опл»: dataMas(5) = месяц_опл
book(6) = «сумма_опл»: dataMas(6) = сумма_опл
book(7) = «фио_бух»: dataMas(7) = фио_бух
book(8) = «дата_опл»: dataMas(8) = дата
‘заполнение полей квитанции:
For i = 1 To 8
ActiveDocument.FormFields(ActiveDocument.Bookmarks(book(i)).Name).Result = dataMas(i)
ActiveDocument.PrintPreview Выполняется предварительный просмотр печати.
‘Для того чтобы реально распечатать документ, применяется метод PrintOut объекта ApplicatioN(см. Help)
UserForm1.Hide
В связи с различием переменных, принимаемых и передаваемых на печать, объявим как Public переменные двух видов: получаемые интерфейсной частью приложения и печатаемые в форме шаблона.
Обратите внимание на то, что здесь все данные имеют тип String. Такие однотипные данные позволяют записывать их в поля формы в цикле, содержащем всего два оператора (см. описание листинга).
Создание модулей работы кнопок формы приложения
Работа кнопки Выдать справку состоит в считывании введенной информации в текстовые поля формы приложения, передаче их значений переменным формы шаблона документа с дальнейшим выводом информации в шаблон документа.
OptioNExplicit
Private Sub CommandButton1_Click()
фамилия = фам1
отчество = отчество1
группа = групп1
месяц_опл = месяц
сумма_опл = сумма
фио_бух = бух
дата_опл = дата
Call Печать
Процедуры присваивания переменным формы приложения введенных значений в текстовые поля формы.
Как я запускал шаблонизатор офисных документов как сервис
Прошло некоторое время с момента запуска, и я думаю пришла пора поделиться опытом создания собственного сервиса для подготовки документов. Возможно он будет полезен начинающим программистам о том, как не стоит делать и на чем можно сэкономить время.
Постановка проблемы
Однажды ко мне обратился друг со следующим вопросом: есть накопленная экспертиза в виде базы юридических документов, хотелось бы эту экспертизу монетезировать в виде сервиса. На что было предложено встретиться через некоторое время и обсудить техническое задание.
Анализ спроса
На сервисе подбора слов от Яндекса были найдены обнадеживающие цифры. Люди постояно ищут образцы документов.

Анализ конкурентов
В качестве конкурентов виделся только Freshdoc. Он позволяет после заполнения анкеты за небольшую сумму скачивать документы. Как преимущество можно было предоставить возможность редактировать текст, т.к основными пользователями сервиса должны были стать опытные юристы. Они отвечали бы за дальнейшее наполнение и актуализацию базы документов. А также убрать необходимость вводить однотипные параметры несколько раз: фио, адреса. Второй категорией пользователей должны были стать обычные пользователи и менее опытные юристы, которые бы скачивали готовые документы, созданные по готовым шаблонам.
Техническое задание
Необходимо разработать сервис, в котором пользователь без предварительного обучения сможет подготовить под себя шаблоны юридических документов (.doc/.docx файлы) а также вести свои дела. В деле необходимо предусмотреть возможность заводить параметры. Примерами параметров могут служить: телефоны, адрес и наименования судов, ФИО, реквизиты и т.д.
Организовывать шаблоны документов по разделам/папкам в виде дерева. Дополнительно необходимо автоматически предзаполнять поля документов из шаблонов, используя значения параметров в карточках дела.
Минимально значимый продукт
На коленке была быстро написана первая версия приложения. Технически это был монолит на .net core версии 2.1. На фронтенде выбор пал на VueJs с Vuetify как более простого, если сравнивать с React, и популярного на тот момент решения. Для импорта и конвертации документов использовался сервис docsbox от dveselov. Docsbox написан на питоне и имеет удобное HTTP API и при необходимости мог масштабироваться, т.к в нем есть очередь на Redis и воркер процессы для вызова API LibreOffice (pyuno). На фронтенде использовался rich text editor: tinymce с плагином templates. В качестве хостинга выбор пал на Digital Ocean с dokku. Был поднят CI на Gitlab и Nexus репозиторий для хранения пакетов и библиотек.

внешний вид первой версии
Впечатления и недостатки первой версии
Заказчику прототип понравился, было решено продолжить работу. Но возникли технические трудности на более чем тривиальных документах: в редакторе HTML после импорта .doc и генерации PDF постоянно съезжала верстка документов. Были попытки заменить SDK LibreOffice на библиотеки, которые могли дать прирост в качестве документа HTML для редактирования, максимальная ставка делалась на Open XMl SDK, но даже она давала артефакты в HTML редакторе.
Была даже выпущена отдельная версия с редактором .tex на библиотеке pandoc по аналогии с сервисом Overleaf. Но использовать .tex человеку далекому от программирования не представлялась возможным.

пример генерации превью документа .tex
Ненужные улучшения
Монолит был преобразован в микросервисы. От MVC части отделился API проект, добавлен отдельный процесс Worker для обработки событий из API. Здеь очень помогла библиотека Marten и ее поддержка стримов событий.
Очень много времени было потрачено на настройку SSR и webpack. Изначально использовалилсь штатные node services для пререндерера от Microsoft, но их вскоре задепрекейтили. Для чего был написан свой велосипед на ViewComponent и KoaJs
и тогда из Index.cshtml вызывается следующий код
Также ушло время на прикручивание и настройку OAuth 2 с OpenID Connect. Был взят стандартный IdentityServer 4 с хорошим дополнением в виде админки Skoruba Identity Server.
В IdentityServer были добавлены тенанты на уровне единой базы. Далее идентификатор тенанта использовался для запросов данных в ORM Marten.
Также был применен подход backend for frontend. Это позволило уйти от CORS запросов для API с одной стороны, с другой стороны реализовать автоматическое продление токена.
С помощью велосипеда на SSR можно было бы добавлять новые frontend фреймворки, разделяя их на уровне ViewComponent. Хоть эта идея и казалась очень интересной, но такой необходимости так и не возникло.
Принципиально другое решение. Версия 2
Как-то вечером я наткнулся на статью про LibreOffice Online. В нем для рендеринга использовался canvas, что решало мою проблему с отображением документов, но весь код для подстановки параметров в tinymce пришлось выкинуть, а вместо них писать макросы на питоне

внешний вид второй версии
Недостатки версии 2
Почти сразу я столкнулся с ограничением LibreOffice Online на 10 документов и пришлось собирать его из исходников. Также была исправлена проблема с терминированием TLS. Сервис просто не запускался. После того, как были изменены конфиги и поправлен код проверки инициализации TLS сервис наконец ожил. Для WOPI сервера было решено использовать открытый проект на .net. Для хранения файлов s3-совместимый продукт — Minio. Так как сервис предполагал хранение персональных данных, то у заказчика всегда должна была быть возможность развертывания локально. Было понятно к этому времени, что любовь LibreOffice Online к потреблению памяти на открытый документ

убивает идею хостинга сервиса на одном сервере. Нужно было переходить на более отказоустойчивую архитектуру. Как стандарт выбор пал на Kubernetes, а так как денег у меня было мало, то его дистрибутив k3s от Rancher.
Kubernetes на Hetzner
Целью было создать демо-кластер для разработки стоимостью до 50 euro в месяц.
Был найден удобный terraform скрипт и написаны helm-чарты.
Для мониторинга сначала прикрутил New Relic c pixie, но из-за высокой стоимости New Relic (на конец месяца из-за одних только метрик насчиталось 400 usd) и повышенных требований на агентов pixie (2 GB RAM на агент) пришлось заменить на cilium.
Для cilium в скриптах понадобилось лишь выключить flannel.

архитектура приняла свой финальный вид
Лендинг и уроки
Чтобы не наполнять базу пользователей ботами регистрацию в IdentityServer закрыл. Для запроса доступов в систему поднял простейший статический сайт на eleventy с шаблоном от google. До добавления аналитики сайт выдавал 100 по оценкам page speed test.

После добавления сервисов аналитики сайт деградировал до приемлимых 80.
Вместо документации записал на youtube обучащие ролики с субтитрами.
Запуск и аналитика в Яндекс.Бизнесе
Перед запуском я написал в несколько популярных юридических форумов. Ответ получил только в одном с предупреждением о рекламе. Самое ценное, что было в ответе, следующее:
Забавно. На сколько мне известно, люди обычно ищут шаблоны, а не программы для их составления.

результаты работы рекламы за неделю
Выводы
Разработку нужно было начинать с оценки реальных заявок на поднятый лендинг, а не надеяться на статистику поисковых запросов.
Как ни печально, все технические решения и архитектура не будут играть никакой роли если проект никому не нужен.
Заполнение бланков данными из таблицы
Имеем базу данных (список, таблицу — называйте как хотите) с информацией по платежам на листе Данные:
Задача: быстро распечатывать приходно-кассовый ордер (платежку, счет-фактуру. ) для любой нужной записи выбранной из этого списка. Поехали!
Шаг 1. Создаем бланк
На другом листе книги (назовем этот лист Бланк) создаем пустой бланк. Можно самостоятельно, можно воспользоваться готовыми бланками, взятыми, например, с сайтов журнала "Главный Бухгалтер" или сайта Microsoft. У меня получилось примерно так:

В пустые ячейки (Счет, Сумма, Принято от и т.д.) будут попадать данные из таблицы платежей с другого листа — чуть позже мы этим займемся.
Шаг 2. Подготовка таблицы платежей
Прежде чем брать данные из таблицы для нашего бланка, таблицу необходимо слегка модернизировать. А именно — вставить пустой столбец слева от таблицы. Мы будем использовать для ввода метки (пусть это будет английская буква "икс") напротив той строки, данные из которой мы хотим добавить в бланк:
Шаг 3. Связываем таблицу и бланк
Для связи используем функцию ВПР (VLOOKUP) — подробнее про нее можно почитать здесь. В нашем случае для того, чтобы вставить в ячейку F9 на бланке номер помеченного "x" платежа с листа Данные надо ввести в ячейку F9 такую формулу:
Т.е. в переводе на "русский понятный" функция должна найти в диапазоне A2:G16 на листе Данные строку, начинающуюся с символа "х" и выдать нам содержимое второго столбца этой строки, т.е. номер платежа.
Аналогичным образом заполняются все остальные ячейки на бланке — в формуле меняется только номер столбца.
В итоге должно получиться следующее:

Шаг 4. Чтобы не было двух "х".
Если пользователь введет "х" напротив нескольких строк, то функция ВПР будет брать только первое найденное значение. Чтобы не было такой многозначности, щелкните правой кнопкой мыши по ярлычку листа Данные и выберите Исходный текст (Source Code) . В появившееся окно редактора Visual Basic скопируйте следующий код:
Этот макрос не дает пользователю ввести больше одного "х" в первый столбец.
Автоматическое заполнение бланков документов данными из Excel
Как известно, табличный редактор Excel используется для работы с табличными данными, а текстовый редактор Word для работы с текстами. И одно и другое приложение широко используются пользователями для подготовки различного рода документов, таких как договоры, сметы, акты выполненных работ, квитанции, путевые листы, доверенности и так далее. Кроме того, зачастую различные реестры и базы данных с информацией по клиентам, продавцам, покупателям, поставщикам и подрядным организациям создаются в приложении Excel, а сами данные из этих реестров используются для составления документов и заполнения шаблонов в текстовом редакторе Word. Далее рассматриваются варианты автоматизации при заполнении документов данными из рабочих книг Excel.
Заполнение бланков Excel данными активного рабочего листа
Большое количество документов создается пользователями в табличном редакторе Excel, это всевозможные приказы, ведомости, графики, журналы, ордера, акты, чеки, счета и счет-фактуры. Все это изобилие документов требует своего заполнения. Какие-то бланки заполняются специальными программами по учету финансово-хозяйственной деятельности, такими как 1С или SAP, какие-то просто распечатываются для дальнейшего заполнения их вручную, а какие-то требуют своего заполняются на компьютере с последующей печатью. Достаточно часто возникает необходимость сформировать сразу несколько документов, в которых используются одни и те же данные, например счет, счет-фактура и акт выполненных работ.
Процесс заполнения бланков документов в Excel можно автоматизировать. Одним из способов автоматизации является использование надстройки для заполнения подготовленных бланков документов.
Надстройка для Excel позволяет выбрать как один, так и несколько бланков документов и быстро заполнить их данными активного рабочего листа, предварительно подготовленными для этого.

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