Skip to content

Шаблоны документов в Word ​

Смета, счёт, акт, договор и доп. соглашение собираются из шаблона Word (DOCX). Выпущенный документ хранится в двух видах: DOCX — поправить руками, PDF — отправить клиенту. PDF из Word делает конвертер Gotenberg (LibreOffice в отдельном контейнере). Этап 5 ТЗ «смета → документы → расходы → план и факт».

Какой шаблон берётся ​

Для документа с продавцом (наше юрлицо проекта или сметы) шаблон ищется так:

  1. шаблон этого вида для этого юрлица;
  2. иначе общий шаблон вида («Для всех юрлиц»);
  3. иначе стандартный — встроенный, его собирает код (internal/modules/documents/app/docx/defaults.go).

Шаблон один на пару «вид + юрлицо»: повторная загрузка — новая версия того же шаблона. Удаление шаблона возвращает документы к следующему по списку; уже выпущенные документы не меняются.

Документ по HTML-шаблону (явный template_id у счёта) собирается по-старому, без DOCX.

Экран «Администрирование → Шаблоны документов» ​

/admin/document-templates. По каждому виду — строка общего шаблона (или «Стандартный») и строки шаблонов отдельных юрлиц. Действия строки:

  • Скачать / Скачать стандартный — файл Word, чтобы поправить;
  • Загрузить свой / Загрузить новую версию — вид, юрлицо, название, файл .docx до 5 МБ;
  • Образец на данных проекта — PDF по шаблону на данных выбранного проекта: номер «0», ничего не выпускается и не сохраняется;
  • Поля шаблона — справочник полей с примерами, поле копируется кнопкой;
  • Удалить — только у загруженного.

Экран и API открыты тем, кто ведёт документы: глобальный администратор, documents.manage, legal.manage (договоры) и финансы (затраты «все»). Во фронте — capability document_templates_manage из /org/me.

Как устроен шаблон ​

Шаблон — обычный документ Word. Поле пишется в двойных фигурных скобках: {{document.number}}, {{client.name}}. Word иногда режет текст поля на куски (проверка орфографии, смена шрифта посреди поля) — система склеивает такие куски сама.

Список — блок {{#lines}} … {{/lines}}:

  • если начало и конец блока стоят в одной строке таблицы (обычно в первой и последней ячейке), строка повторяется для каждой позиции;
  • если это отдельные абзацы, повторяется всё между ними, а сами абзацы с метками убираются;
  • внутри одного абзаца повторяется кусок текста.

Внутри списка поля — без префикса: {{n}}, {{name}}, {{qty}}, {{unit}}, {{price}}, {{total}}. Элемент простого списка (изменения доп. соглашения) — {{.}}.

Необязательное — тот же блок по полю-значению: {{#client.kpp}}, КПП {{client.kpp}}{{/client.kpp}} исчезнет целиком, если КПП не заполнен. Обратный блок {{^notes}}…{{/notes}} выводится, когда поля нет.

Незнакомое поле при загрузке не ошибка: шаблон сохраняется, а экран предупреждает — «поля … система не знает, они останутся пустыми» (обычно опечатка). Незакрытый блок и не-DOCX — ошибка загрузки.

Поля ​

ПолеЧто это
{{document.number}}, {{document.date}}номер и дата документа (номер — по нашему юрлицу при выпуске)
{{document.version}}версия сметы («версия 2»)
{{project.name}}, {{project.dates}}проект и его даты
{{seller.*}}наше юрлицо: name, inn, kpp, ogrn, address, bank, bik, account, corr_account, director, director_title, phone, email
{{client.*}}заказчик — те же поля
{{#lines}}…{{/lines}}строки: n, name, qty, unit, price, total
{{totals.total}}, {{totals.total_words}}сумма документа и прописью (у счёта на аванс — сумма аванса)
{{totals.vat_text}}«В том числе НДС 22 %: … руб.» или «Без НДС»
{{notes}}, {{due_date}}назначение платежа, срок оплаты счёта
{{parent.number}}, {{parent.date}}договор, к которому доп. соглашение
{{#changes}}{{.}}{{/changes}}, {{new_total}}изменения доп. соглашения и новая цена

Полный список по виду с примерами — GET /api/v1/word-templates/fields?type=… и справочник на экране. Суммы приходят уже отформатированными («1 780 000,00»). Места подписи — пустые линии, без картинок подписи и печати (решение заказчика).

Выпуск документа ​

  1. Данные документа собираются как раньше (render.Context: проект, заказчик, наше юрлицо, строки, итоги).
  2. WordData раскладывает их в поля шаблона, шаблон заполняется (docx.Fill).
  3. DOCX уходит в Gotenberg (POST /forms/libreoffice/convert), обратно — PDF.
  4. Оба файла загружаются в папку проекта (смета — «Коммерческие предложения», счета — «Счета» и т.д.); у документа file_id — PDF, docx_file_id — DOCX.

Если конвертер недоступен или упал, документ всё равно выпускается: PDF рисуется кодом (fpdf), как до этапа 5, DOCX сохраняется. В журнале — documents: convert … to pdf: … (fallback to built-in pdf). Образец на экране шаблонов без конвертера недоступен — сервер отвечает понятной ошибкой.

Скачать: GET /api/v1/documents/:id/download — PDF, ?format=docx — Word. Во вкладке «Документы» проекта у документа с DOCX есть кнопка «Скачать Word».

Предпросмотр офисных файлов ​

Тот же Gotenberg готовит предпросмотр загруженных DOC/DOCX/XLS/XLSX/PPT/PPTX/ODT/ODS/ODP/RTF: файл конвертируется в PDF и кладётся в previews/<id>/preview.pdf. Без конвертера — прежний путь через soffice в контейнере, если он есть. Миграция 0168 переводила файлы с неудавшимся предпросмотром обратно в pending — они готовятся при следующем открытии.

Настройка ​

  • APP_GOTENBERG_URL — адрес конвертера; в compose по умолчанию http://gotenberg:3000. Пусто — конвертера нет (PDF кодом, образцы недоступны).
  • Сервис gotenberg (gotenberg/gotenberg:8) в compose.yaml и docker-compose.yml: таймаут 90 с, перезапуск LibreOffice после 50 конвертаций, память до 1 ГБ.

Где в коде ​

  • internal/modules/documents/app/docx — заполнение DOCX, стандартные шаблоны, список полей;
  • internal/modules/documents/app/wordtemplates — загрузка, версии, выбор шаблона;
  • internal/modules/documents/app/generated/word.go — поля из данных документа, DOCX + PDF при выпуске, образец;
  • internal/infra/gotenberg — клиент конвертера;
  • internal/transport/http/word_templates.go — /api/v1/word-templates;
  • миграция 0168_docx_templates.sql — document_templates.format/content/seller_requisite_id, documents.docx_file_id.

Загружаем документацию…