Files
public/docx_report/TECHNICAL.md

186 lines
9.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 для тестовой модели.