odoo-reportslisted
Install: claude install-skill tuanle96/odoo-ai-skills
# Odoo QWeb reports (PDF / HTML)
An Odoo report is **three layers**, and most bugs come from touching the wrong one:
| Layer | What it is | Lives in |
|-------|-----------|----------|
| **Action** | `ir.actions.report` — `model`, `report_name`→template, `report_type`, `paperformat_id`, Print-menu binding | data XML |
| **Template** | QWeb: `web.html_container` → `web.external_layout` → your `<div class="page">` | data XML (loaded via `__manifest__['data']`) |
| **Data** | *optional* `models.AbstractModel` named `report.<module>.<report_name>` with `_get_report_values()` | Python |
PDFs are rendered from the HTML by **wkhtmltopdf** (0.12.6, qt-patched). Version floor: Odoo 17/18, through Odoo 19 (current LTS). Use `t-out` for output (v17+ — `t-esc` superseded, `t-raw` removed).
**The rule: there is almost always an existing report — find its action, template, and parser, then *inherit*. Don't rebuild and don't guess ids.**
## Discover before you customize (introspect-first)
Use the `odoo-introspect` skill:
- `metadata.py` enumerates `ir.actions.report` records with their QWeb template + paperformat; `entrypoints.py` (reads the resolved `get_view`) surfaces the reports bound to a model (the **Print** menu). Get `report_name` → the template external id → whether a `report.<module>.<name>` AbstractModel parser already exists. Never hand-guess the template id.
- **Two-template trap:** real reports split into a *main* template (loops `docs`, sets `lang`) and a `*_document` *