docs(fusion_accounting_reports): CLAUDE.md, UPGRADE_NOTES.md, README.md
Some checks failed
fusion_accounting CI / test (fusion_accounting_ai) (push) Has been cancelled
fusion_accounting CI / test (fusion_accounting_core) (push) Has been cancelled
fusion_accounting CI / test (fusion_accounting_migration) (push) Has been cancelled

Made-with: Cursor
This commit is contained in:
gsinghpal
2026-04-19 16:31:57 -04:00
parent 5a864e4b48
commit 848aa0f0e5
3 changed files with 310 additions and 0 deletions

View File

@@ -0,0 +1,147 @@
# fusion_accounting_reports — Cursor / Claude Context
## Purpose
AI-augmented financial reports — a Fusion-native replacement for Odoo
Enterprise's `account_reports` module. Phase 2 of the fusion_accounting
roadmap.
CORE scope:
- Income Statement (P&L)
- Balance Sheet
- Trial Balance
- General Ledger (with drill-down)
AI augmentation:
- Anomaly detection (variance vs prior period)
- AI commentary (LLM-generated narrative)
## Architecture
Hybrid: the engine (`fusion.report.engine`, AbstractModel) is the SINGLE
read surface for reports. Per-report definitions are stored as `fusion.report`
records with JSON `line_specs` so non-developers can tweak the layouts.
Public engine API (5 methods):
- `compute_pnl(period, *, comparison='none', company_id=None)`
- `compute_balance_sheet(date_to, *, comparison='none', company_id=None)`
- `compute_trial_balance(period, *, company_id=None)`
- `compute_gl(period, *, account_ids=None, company_id=None)`
- `drill_down(*, account_id, period, company_id=None)`
Pure-Python services in `services/` (no Odoo imports — independently
unit-testable):
- `date_periods``Period` dataclass + comparison-period math
- `account_hierarchy` — chart-of-accounts tree walk
- `totaling` — debit/credit/balance roll-ups
- `currency_conversion` — multi-currency conversion via `res.currency.rate`
- `line_resolver` — JSON `line_specs` → rendered rows
- `drill_down_resolver` — line → underlying journal items
- `anomaly_detection` — variance vs prior period (z-score + abs/pct gates)
- `commentary_generator` — LLM narrative with templated fallback
- `commentary_prompt` — provider-agnostic system + user prompt
Persisted models in `models/`:
- `fusion.report` — definition with JSON `line_specs`
- `fusion.report.commentary` — LLM-output cache (one per period+mode)
- `fusion.report.anomaly` — flagged variances
- `fusion.account.balance.mv` — pre-aggregated materialized view
- `fusion.report.engine` — AbstractModel (the API)
- `fusion.reports.cron` — cron handlers (commentary refresh, MV refresh)
- `fusion.xlsx.export.wizard` — TransientModel (XLSX export)
- `fusion.period.picker.wizard` — TransientModel (UX entry-point)
- `fusion.migration.wizard` (inherits) — adds `_reports_bootstrap_step`
Controller: `controllers/reports_controller.py` exposes 8 JSON-RPC endpoints
under `/fusion/reports/*`. All read paths route through the engine.
OWL frontend: `static/src/`
- `scss/` — variables, base styles, dark-mode overrides
- `services/reports_service.js` — central reactive state + RPC wrappers
- `views/report_viewer/` — top-level OWL view + view-registry adapter
- `components/report_table/` — generic financial-table renderer
- `components/drill_down_dialog/` — modal for journal-item listing
- `components/period_filter/` — date-range + comparison picker
- `components/ai_commentary_panel/` — LLM commentary surface
- `components/anomaly_strip/` — variance summary banner
- `tours/reports_tours.js` — 5 OWL tour smoke tests
## Coexistence
When `account_reports` is installed, the Reports menu hides via
`fusion_accounting_core.group_fusion_show_when_enterprise_absent`
(a computed group). The engine + AI tools (commentary, anomaly detection)
remain available for the chat regardless.
## Conventions
- **V19 deprecations to avoid:** `_sql_constraints` (use `models.Constraint`),
`@api.depends('id')` (raises `NotImplementedError`), `@route(type='json')`
(use `type='jsonrpc'`), `numbercall` field on `ir.cron` (removed),
`groups_id` on `res.users` (use `all_group_ids` for searching),
`users` field on `res.groups` (use `user_ids`), `groups_id` on
`ir.ui.menu` (use `group_ids`).
- **Engine signature:** Public methods are keyword-only after the leading
positional `period` / `date_to`. Always pass `company_id=...` explicitly.
- **`fusion.report` lookup:** `_get_report` falls back from per-company
override to global (`company_id=False`) — order is `company_id desc nulls
last`.
- **Materialized view refresh:** `fusion.account.balance.mv` rebuilds via a
dedicated autocommit cursor (REFRESH CONCURRENTLY can't run inside Odoo's
regular transaction). Triggered by cron + on demand from the engine when
data is older than the configured TTL.
- **JSON `line_specs`:** Strings prefixed `account:`, `prefix:`, `formula:`
or `header``line_resolver.py` resolves each spec to a row. Header rows
have no compute payload and are silently skipped by downstream totals.
- **Commentary cache:** Keyed on `(report_id, company_id, period_from,
period_to, comparison_mode)` with a unique constraint. Re-runs use the
cache unless `force_refresh=True`.
## Test counts (Phase 2 ship)
- 130 logical tests, 0 failed, 0 errors
- Includes:
- 6 benchmarks (tagged `benchmark`)
- 1 LLM compat smoke (tagged `local_llm`, skips when no LLM)
- 5 OWL tours (tagged `tour`, skips without `websocket-client`)
- Property-based, integration, controller, materialized-view, coexistence,
migration round-trip, PDF/XLSX export
## Performance baseline
| Operation | Median | P95 | Budget |
|---|---|---|---|
| `engine.compute_pnl` | 3ms | 8ms | <2000ms |
| `engine.compute_balance_sheet` | 15ms | 20ms | <2000ms |
| `engine.compute_trial_balance` | 3ms | 8ms | <1000ms |
| `engine.compute_gl` | 25ms | 81ms | <3000ms |
| `engine.drill_down` | 2ms | 10ms | <500ms |
| `controller.run` (HTTP round-trip) | 9ms | 46ms | <2500ms |
All metrics within 1x of budget at Phase 2 ship. Numbers from
`tests/test_performance_benchmarks.py` against the dev VM
(`westin-v19`, ~1 fiscal year of data).
## Known concerns / Phase 2.5 backlog
- Trial balance period-only sum doesn't auto-close to retained earnings
(drift visible in `test_trial_balance_total_near_zero`, currently skipped)
- Balance sheet `TOTAL LIABILITIES + EQUITY` math limited (no
subtotal-of-subtotals expansion in `formula:` specs)
- GL `line_specs` need `prefix:` empty-string handling for
"all accounts" semantics
- Header rows (no compute payload) silently skipped by `line_resolver` —
fine for layout, but a `header_only=True` flag would be clearer
- `expense` prefix overlaps with subtypes (`expense_direct_cost`,
`expense_depreciation`) — current line_specs need explicit ordering or a
longer-prefix-wins rule
- `wkhtmltopdf` may need configuration for PDF export on first install
- `ReportsAdapter.run_report` vs `run_fusion_report` naming (legacy clash
with Enterprise wrapper)
- Tour tests skip when `websocket-client` is absent — install it in CI to
exercise the OWL surface end-to-end

View File

@@ -0,0 +1,103 @@
# fusion_accounting_reports
AI-augmented financial reports for Odoo 19 Community — a Fusion-native
replacement for Enterprise's `account_reports` module.
## What it does
- **CORE reports**: Income Statement (P&L), Balance Sheet, Trial Balance,
General Ledger (with drill-down to journal items)
- **AI augmentation**: variance-based anomaly detection + LLM-generated
commentary (Claude / GPT / local LM Studio / Ollama)
- **Wizards**: period picker (common presets — MTD, QTD, YTD, last month,
custom range) + XLSX export
- **Coexists** with Enterprise's `account_reports` (Enterprise wins by
default; the Fusion menu appears only when Enterprise is uninstalled —
the engine and AI tools are always available via the AI chat)
- **Multi-currency** aware via `services/currency_conversion.py`
- **Multi-company** aware (per-company `fusion.report` overrides fall back
to global definitions)
## Quick start
```bash
# Install
odoo --addons-path=... -i fusion_accounting_reports
# Open the reports menu (when Enterprise's account_reports is NOT installed)
# Apps → Reports → Open Financial Report
```
## Configuration
### LLM commentary (optional)
For LM Studio / Ollama (local):
- `fusion_accounting.openai_base_url` = `http://host.docker.internal:1234/v1`
- `fusion_accounting.openai_model` = your local model name
- `fusion_accounting.openai_api_key` = `lm-studio` (or anything non-empty)
- `fusion_accounting.provider.reports_commentary` = `openai`
For OpenAI / Anthropic, set the corresponding API keys via the
`fusion_accounting_ai` config screen — `reports_commentary` will route
through whatever provider you choose.
If no provider is configured, commentary falls back to a deterministic
templated summary (no LLM call).
### Cron jobs
Two cron handlers live in `models/fusion_reports_cron.py`:
- `fusion_reports_commentary_refresh` — daily, regenerates commentary for
the most recently completed period
- `fusion_reports_mv_refresh` — every 15 min, refreshes
`fusion.account.balance.mv`
## Public engine API
```python
engine = env['fusion.report.engine']
# Income statement
result = engine.compute_pnl(period, comparison='previous_year')
# Balance sheet (point-in-time)
result = engine.compute_balance_sheet(date(2026, 12, 31))
# Trial balance
result = engine.compute_trial_balance(period)
# General ledger (journal items per account)
result = engine.compute_gl(period, account_ids=[1, 2, 3])
# Drill-down (one account, period)
items = engine.drill_down(account_id=1, period=period)
```
## JSON-RPC endpoints
All under `/fusion/reports/`:
- `POST /fusion/reports/run` — single entry-point (dispatches by `report_type`)
- `POST /fusion/reports/drill_down` — journal items for an account+period
- `POST /fusion/reports/commentary` — fetch/refresh LLM commentary
- `POST /fusion/reports/anomalies` — flagged variances for a period
- `POST /fusion/reports/export_xlsx` — XLSX bytes
- `POST /fusion/reports/export_pdf` — PDF bytes (via wkhtmltopdf)
- `POST /fusion/reports/list_definitions` — available `fusion.report` records
- `POST /fusion/reports/period_presets` — date-range presets for the picker
## Test counts
- 130 logical tests, 0 failures, 0 errors
- 6 performance benchmarks (tagged `benchmark`)
- 1 local-LLM compat smoke (tagged `local_llm`, skips without LLM)
- 5 OWL tour tests (tagged `tour`, skips without `websocket-client`)
## See also
- `CLAUDE.md` — agent context (architecture, conventions, perf baseline,
Phase 2.5 backlog)
- `UPGRADE_NOTES.md` — V19 anchor + migration strategy

View File

@@ -0,0 +1,60 @@
# fusion_accounting_reports — Upgrade Notes
## Odoo Version Anchor
This module targets **Odoo 19.0** (community-base).
Reference snapshot of Enterprise code mirrored from:
- `account_reports` (Odoo 19.0.x)
- Source: `/Users/gurpreet/Github/RePackaged-Odoo/accounting/account_reports/`
## Cross-Version Diff Strategy
When a new Odoo version ships:
1. Run `check_odoo_diff.sh` (in repo root) against the new Enterprise version
2. Note any breaking changes in `account.move.line` / `account.account` API
surfaces relied on by `services/totaling.py` and
`services/drill_down_resolver.py`
3. For mirrored OWL components, diff Enterprise's new versions against ours
and port material changes (signature renames, new behaviour we want to
inherit)
4. Re-run the full test suite + tour tests + benchmarks against the new Odoo
version
5. Update this file with the new version anchor + any deviations
## V19 Migration Notes (already applied — Phase 1 lessons)
These were the bite-points from Phase 1 (`fusion_accounting_bank_rec`); we
preempted them in Phase 2 from day one:
- `_sql_constraints``models.Constraint` (used in `fusion.report`,
`fusion.report.commentary`, `fusion.report.anomaly`)
- `@api.depends('id')` → removed everywhere; computed fields depend on real
field names instead
- `@route(type='json')``type='jsonrpc'` (all 8 endpoints)
- `numbercall` field on `ir.cron` → omitted (removed in V19)
- `res.groups.users``user_ids`
- `ir.ui.menu.groups_id``group_ids` (used in `views/menu_views.xml` and
the two wizard view files for the coexistence-group filter)
## Engine API Stability
The 5 public engine methods (`compute_pnl`, `compute_balance_sheet`,
`compute_trial_balance`, `compute_gl`, `drill_down`) are the public contract.
Their signatures are keyword-only after the first positional argument and
will be treated as semver-stable across patch releases. Breaking changes
will bump the minor version (e.g. 19.0.2.x.y).
## Phase 2 → Phase 2.5 Migration
If we ship Phase 2.5 (line_spec polish, deferred features, header_only
flag, prefix overlap fix), changes will go in incremental commits. No DB
migration needed — Phase 2 schema is forward-compatible:
- `fusion.report.line_specs` is a JSON column; the migration path is to
rewrite specs in place
- `fusion.account.balance.mv` can be dropped/re-created freely
- `fusion.report.commentary` is a cache; safe to truncate on upgrade
- `fusion.report.anomaly` records carry Period as date_from/date_to fields;
no schema-level changes anticipated