186 lines
9.5 KiB
Markdown
186 lines
9.5 KiB
Markdown
# DOCX-шаблоны
|
||
name: docx_report
|
||
version: 19.0.1.0.0
|
||
author: MK.Lab
|
||
depends: `base`, `account`, `sale`, `l10n_ru_contract`, `docx_report_generation`
|
||
external_dependencies: `docxtpl`, `docxcompose`, `bs4`
|
||
|
||
## Архитектура
|
||
|
||
Модуль надстраивается над `docx_report_generation` и добавляет интерфейс управления шаблонами. Ключевая модель `docx.template` наследует технические поля в `ir.actions.report` через `_inherits`.
|
||
|
||
```
|
||
docx_report
|
||
├── docx.template — шаблон (наследует ir.actions.report через _inherits)
|
||
├── docx.custom.field — кастомные переменные шаблона
|
||
├── docx.template.mixin — AbstractModel для привязки шаблона к модели
|
||
├── ir.actions.report — расширение: рендер DOCX, кастомные переменные, валидация
|
||
├── ir.model — расширение: display_name в контексте docx_template
|
||
├── ir.model.fields — расширение: вычисляемый тип поля для подсказки
|
||
└── DocxReportController — HTTP-контроллер: скачивание DOCX и PDF
|
||
```
|
||
|
||
---
|
||
|
||
## Модели
|
||
|
||
### `docx.template` — Шаблон
|
||
|
||
**Файл:** `models/docx_template.py`
|
||
|
||
Использует `_inherits = {"ir.actions.report": "report_id"}` — все технические поля (`name`, `model`, `report_type`, `report_docx_template`) берутся из связанного `ir.actions.report`.
|
||
|
||
| Поле | Тип | Описание |
|
||
|---|---|---|
|
||
| `report_id` | Many2one → `ir.actions.report` (required, cascade) | Связанное действие отчёта |
|
||
| `filename_pattern` | Char | Python-выражение для имени скачиваемого файла (без расширения). Переменные: `object`, `time` |
|
||
| `docx_output_type` | Selection (`docx`) | Формат вывода |
|
||
| `docx_model_id` | Many2one → `ir.model` | Модель документа (устанавливает `model` в `ir.actions.report`) |
|
||
| `available_field_ids` | Many2many → `ir.model.fields` (computed) | Доступные поля модели |
|
||
| `global_template` | Boolean (default: True) | Глобальный шаблон — доступен для всех записей модели |
|
||
| `hint_model_id` | Many2one → `ir.model` | Модель для подсказки полей |
|
||
| `report_docx_template_filename` | Char | Имя загруженного файла шаблона |
|
||
|
||
#### Методы
|
||
|
||
**`_compute_available_field_ids()`** — вычисляет список полей модели для вкладки «Доступные переменные».
|
||
|
||
**`create()` / `write()`** — при сохранении синхронизируют `docx_output_type` → `report_type` (`docx` → `docx-docx`) и `docx_model_id` → `model` в связанном `ir.actions.report`.
|
||
|
||
**`action_bind_to_actions()`** — создаёт `binding` для отображения шаблона в меню.
|
||
|
||
**`action_unbind_from_actions()`** — удаляет привязку через `unlink_action()`.
|
||
|
||
**`action_bind_all_to_actions()`** — добавляет в меню печати все шаблоны сразу.
|
||
|
||
**`action_validate_docx_template()`** — валидирует загруженный шаблон:
|
||
1. Распаковывает `.docx` как ZIP, читает XML-файлы внутри `word/`
|
||
2. Извлекает все выражения `{{ }}` и переменные циклов `{% for %}`
|
||
3. Для каждого выражения разбирает цепочку полей и проверяет их существование через `self.env[model_name]._fields`
|
||
4. Возвращает уведомление: успех или список неизвестных переменных
|
||
|
||
---
|
||
|
||
### `docx.custom.field` — Кастомная переменная
|
||
|
||
**Файл:** `models/docx_custom_field.py`
|
||
|
||
| Поле | Тип | Описание |
|
||
|---|---|---|
|
||
| `report_id` | Many2one → `ir.actions.report` (required, cascade) | Основной отчёт |
|
||
| `technical_name` | Char (required) | Имя переменной в шаблоне |
|
||
| `name` | Char (required) | Отображаемое название |
|
||
| `value_python` | Text (required) | Python-выражение. Контекст: `env`, `user`, `record`, `docs`, `time`, `context` |
|
||
|
||
---
|
||
|
||
### `docx.template.mixin` — Миксин
|
||
|
||
**Файл:** `models/docx_template_mixin.py`
|
||
|
||
Добавляет поле `docx_template_id` (Many2one → `docx.template`) в любую модель. Используется для хранения шаблона по умолчанию для конкретного объекта (например, вид договора → шаблон).
|
||
|
||
---
|
||
|
||
### `ir.actions.report` — Расширение
|
||
|
||
**Файл:** `models/ir_actions_report.py`
|
||
|
||
Добавляет поля и реализует рендер DOCX.
|
||
|
||
| Поле | Тип | Описание |
|
||
|---|---|---|
|
||
| `docx_custom_field_ids` | One2many → `docx.custom.field` | Кастомные переменные |
|
||
| `report_docx_template` | Binary | Загруженный `.docx`-файл шаблона |
|
||
| `report_type` | Selection (расширение) | Добавлен тип `docx-docx` |
|
||
| `report_name` | Char (computed) | Формат: `{model}-docx_report+{id}` для DOCX-отчётов |
|
||
|
||
#### Метод `_render_docx_template(template, values)`
|
||
|
||
Основной метод рендеринга. Последовательность:
|
||
|
||
1. Формирует базовый контекст: `record`, `time`, `user`, `res_company`, `web_base_url`, `website`
|
||
2. Вычисляет кастомные переменные через `safe_eval` и добавляет их в контекст
|
||
3. Рендерит шаблон через `DocxTemplate` (библиотека `docxtpl`)
|
||
4. Возвращает `BytesIO` с готовым DOCX
|
||
|
||
#### Метод `_render_docx_docx(res_ids, data)`
|
||
|
||
Оркестрирует рендер для списка записей: поддерживает кэш вложений, рендерит по одной записи, объединяет через `_merge_docx()` если несколько.
|
||
|
||
#### Метод `_merge_docx(streams)`
|
||
|
||
Объединяет несколько DOCX-файлов в один через `docxcompose.Composer` с разрывами страниц между ними.
|
||
|
||
#### Метод `_parse_markup(markup_data)`
|
||
|
||
Конвертирует HTML-поля Odoo в plain text для вставки в DOCX через `BeautifulSoup`.
|
||
|
||
---
|
||
|
||
### `ir.model.fields` — Расширение
|
||
|
||
**Файл:** `models/ir_model_fields.py`
|
||
|
||
Добавляет вычисляемое поле `docx_type_label` — человекочитаемый тип поля для вкладки «Доступные переменные»:
|
||
- Many2one/One2many/Many2many: `many2one (res.partner)`
|
||
- Selection: `selection (value1, value2)`
|
||
|
||
---
|
||
|
||
## HTTP-контроллер
|
||
|
||
**Файл:** `controllers/main.py`
|
||
**Класс:** `DocxReportController` (наследует `ReportController`)
|
||
|
||
### Маршрут `report_routes`
|
||
|
||
Расширяет стандартный маршрут `/report/<converter>/<reportname>/<docids>`:
|
||
|
||
| Converter | Действие |
|
||
|---|---|
|
||
| `docx` | Вызывает `_render_docx_docx()`, возвращает DOCX с MIME-типом `application/vnd.openxmlformats-officedocument.wordprocessingml.document` |
|
||
| `pdf` (для DOCX-отчёта) | Вызывает `_render_docx_pdf()` (из `docx_report_generation`) |
|
||
| Остальные | Передаёт в родительский `ReportController` |
|
||
|
||
### Маршрут `report_download`
|
||
|
||
Обрабатывает скачивание файлов типов `docx-docx` и `docx-pdf`. Формирует имя файла через `_get_docx_output_filename()`:
|
||
|
||
| Приоритет | Источник имени |
|
||
|---|---|
|
||
| 1 | `docx.template.filename_pattern` (Python-выражение) |
|
||
| 2 | `ir.actions.report.print_report_name` |
|
||
| 3 | `ir.actions.report.name` |
|
||
|
||
---
|
||
|
||
## Представления и меню
|
||
|
||
**Файл:** `views/docx_template_views.xml`
|
||
|
||
Форма шаблона содержит:
|
||
- Кнопка **«Валидация»** в header
|
||
- Кнопки **«Добавить печать в действия»** / **«Убрать печать из действий»** в button_box
|
||
- Основные поля: название, формат, модель, файл шаблона, шаблон имени файла, флаг «Глобальный»
|
||
- Вкладка **«Кастомные переменные»** — список `docx_custom_field_ids`
|
||
- Вкладка **«Доступные переменные»** — readonly список полей модели с техническим именем, названием и типом
|
||
|
||
Пункт меню **«Шаблоны для отчетов»** добавляется в корень главного меню.
|
||
|
||
---
|
||
|
||
## Безопасность
|
||
|
||
**Файл:** `security/ir.model.access.csv`
|
||
|
||
CRUD-права на `docx.template` и `docx.custom.field` для стандартных групп пользователей.
|
||
|
||
---
|
||
|
||
## Тесты
|
||
|
||
**Файл:** `tests/test_docx.py`
|
||
|
||
Покрывает создание шаблона, валидацию, рендер DOCX для тестовой модели.
|