diff --git a/fusion_accounting_bank_rec/CLAUDE.md b/fusion_accounting_bank_rec/CLAUDE.md new file mode 100644 index 00000000..7ae05d5b --- /dev/null +++ b/fusion_accounting_bank_rec/CLAUDE.md @@ -0,0 +1,103 @@ +# fusion_accounting_bank_rec — Cursor / Claude Context + +## Purpose + +Replaces (or augments — coexists with) Odoo Enterprise's `account_accountant` +bank reconciliation widget with a Fusion-native, AI-assistive implementation. +Ships in Phase 1 of the fusion_accounting roadmap. + +## Architecture + +Hybrid: the engine (`fusion.reconcile.engine`, AbstractModel) is the SINGLE +write surface for reconciliations. Everything else (controller, OWL widget, +AI tools, wizards, cron) routes through the engine's 6-method API: + +- `reconcile_one(line, against_lines, write_off_vals=None)` +- `reconcile_batch(lines, strategy='auto')` +- `suggest_matches(lines, limit_per_line=3)` +- `accept_suggestion(suggestion)` +- `write_off(line, account, amount, label, tax_id=None)` +- `unreconcile(partial_reconciles)` + +Pure-Python services live in `services/`: +- `memo_tokenizer` — Canadian bank memo regex +- `exchange_diff` — FX gain/loss pre-compute +- `matching_strategies` — AmountExact, FIFO, MultiInvoice +- `precedent_lookup` — K-nearest search +- `pattern_extractor` — per-partner aggregate +- `confidence_scoring` — 4-pass pipeline (statistical → AI re-rank) +- `precedent_backfill` — migration helper + +Persistent models in `models/`: +- `fusion.reconcile.pattern` — per-(company, partner) learned profile +- `fusion.reconcile.precedent` — per-decision history +- `fusion.reconcile.suggestion` — AI suggestions with state lifecycle +- `fusion.bank.rec.widget` — TransientModel for OWL round-trip +- `fusion.unreconciled.bank.line.mv` — pre-aggregated MV for fast UI listing +- `fusion.bank.rec.cron` — cron handler (suggest, pattern refresh, MV refresh) +- `fusion.auto.reconcile.wizard` / `fusion.bulk.reconcile.wizard` — TransientModel wizards +- `fusion.migration.wizard` (inherits) — adds `_bank_rec_bootstrap_step` +- `account.bank.statement.line` (inherits) — adds fusion_top_suggestion_id, fusion_confidence_band, etc. +- `account.reconcile.model` (inherits) — adds fusion_ai_confidence_threshold + +Controller: `controllers/bank_rec_controller.py` exposes 10 JSON-RPC endpoints +under `/fusion/bank_rec/*`. All calls route through the engine. + +OWL frontend: `static/src/` +- `services/bank_reconciliation_service.js` — central reactive state + RPC wrappers +- `views/kanban/bank_rec_kanban_*.js` — top-level controller + renderer +- `components/bank_reconciliation/<...>` — 14 mirrored Enterprise components + 8 fusion-only components (ai_suggestion folder, batch_action_bar, reconcile_model_picker, attachment_strip, partner_history_panel) +- `tours/bank_rec_tours.js` — 5 OWL tour smoke tests + +## 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`). + +- **Coexistence:** When `account_accountant` is installed, the fusion menu + is hidden via `fusion_accounting_core.group_fusion_show_when_enterprise_absent` + (a computed group). Engine model is always available. + +- **Materialized view refresh:** Triggered on `fusion.reconcile.suggestion` + create/write (best-effort, non-blocking). Cron refreshes every 5 min via + a dedicated autocommit cursor (REFRESH CONCURRENTLY can't run inside + Odoo's regular transaction). + +- **Test factories:** `tests/_factories.py` provides `make_bank_journal`, + `make_bank_line`, `make_invoice`, `make_reconcileable_pair`, `make_suggestion`, + `make_pattern`, `make_precedent`. NOTE: `make_bank_journal` defaults to + code `'TEST'` so multiple calls in one test will collide; pass an explicit + unique code or share a journal across calls. + +- **Hypothesis property tests:** Use `@settings(suppress_health_check=[...])` + to silence function_scoped_fixture warnings in TransactionCase. + +## Test counts (as of Phase 1 complete) + +- 157 logical tests total in fusion_accounting_bank_rec +- 0 failures, 0 errors +- Includes: 4 benchmark tests (tagged 'benchmark'), 1 local LLM smoke (tagged 'local_llm', skips when no LLM), 5 OWL tour tests (tagged 'tour') + +## Performance baseline + +| Operation | P95 | Budget | +|---|---|---| +| `engine.suggest_matches` (1 line) | 234ms | <500ms | +| `engine.reconcile_batch` (50 lines) | 3318ms | <5000ms | +| `controller.list_unreconciled` (50 lines) | 77ms | <200ms | +| MV refresh | 60ms | <2000ms | + +All within 1x of budget at Phase 1 ship. + +## Known concerns / Phase 1.5 backlog + +- `accept_suggestion` returns `partial_ids` but not `is_reconciled` — UI reads it post-call +- `engine.write_off` mixed mode (write-off + against_lines) implemented but untested +- `engine.reconcile_one` returns `exchange_diff_move_id: None` (Odoo's reconcile() handles FX inline; surfacing the move_id needs an extra query) +- `against_lines` early-break in `reconcile_one` silently drops excess; auto strategy avoids this but manual callers should pre-validate +- Reconcile-model bulk wizard `_apply_lines_for_bank_statement_line` is Enterprise-only (Community falls back to per-line error) +- OWL tour tests skip-mode when websocket-client absent diff --git a/fusion_accounting_bank_rec/README.md b/fusion_accounting_bank_rec/README.md new file mode 100644 index 00000000..50c7a91a --- /dev/null +++ b/fusion_accounting_bank_rec/README.md @@ -0,0 +1,41 @@ +# fusion_accounting_bank_rec + +AI-assisted bank reconciliation for Odoo 19 Community — a Fusion-native +replacement for Enterprise's `account_accountant` bank reconciliation widget. + +## What it does + +- Side-by-side parity with Enterprise's bank reconciliation UI (kanban + side + panel, multi-currency, write-offs, attachments, chatter) +- AI-assistive: confidence-scored suggestions per bank line via the + `fusion.reconcile.engine` 4-pass scoring pipeline (statistical + optional + LLM re-rank) +- Coexists with `account_accountant` (Enterprise wins by default; Fusion menu + appears only when Enterprise is uninstalled) +- Migration-aware: bootstrap step backfills `fusion.reconcile.precedent` from + existing `account.partial.reconcile` rows so the AI has memory from day 1 + +## Quick start + +```bash +# Install +odoo --addons-path=... -i fusion_accounting_bank_rec + +# Open the widget (when Enterprise's account_accountant is NOT installed) +# Apps → Bank Reconciliation → Reconcile Bank Lines + +# When Enterprise IS installed: use Enterprise's UI; the engine + AI tools +# are still available via the AI chat. +``` + +## Configuration + +- Local LLM (LM Studio, Ollama): + - `fusion_accounting.openai_base_url` = `http://host.docker.internal:1234/v1` + - `fusion_accounting.openai_model` = your local model name + - `fusion_accounting.provider.bank_rec_suggest` = `openai` + +## See also + +- `CLAUDE.md` — agent context +- `UPGRADE_NOTES.md` — Odoo version anchoring diff --git a/fusion_accounting_bank_rec/UPGRADE_NOTES.md b/fusion_accounting_bank_rec/UPGRADE_NOTES.md new file mode 100644 index 00000000..83af2799 --- /dev/null +++ b/fusion_accounting_bank_rec/UPGRADE_NOTES.md @@ -0,0 +1,34 @@ +# fusion_accounting_bank_rec — Upgrade Notes + +## Odoo Version Anchor + +This module targets **Odoo 19.0** (community-base). + +Reference snapshot of Enterprise code mirrored from: +- `account_accountant` (Odoo 19.0.x) +- Source: `/Users/gurpreet/Github/RePackaged-Odoo/accounting/account_accountant/` + +## 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.bank.statement.line` API +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 against the new Odoo version +5. Update this file with the new version anchor + any deviations + +## V19 Migration Notes (already applied) + +- `_sql_constraints` → `models.Constraint` (Tasks 14, 15) +- `@api.depends('id')` → removed (Task 17) +- `@route(type='json')` → `type='jsonrpc'` (Task 26) +- `numbercall` removed from `ir.cron` (Task 25) +- `res.groups.users` → `user_ids` (Task 43) +- `ir.ui.menu.groups_id` → `group_ids` (Tasks 42, 43) + +## Phase 1 → Phase 1.5 Migration + +If we ship Phase 1.5 (UI polish, deferred features), changes will go in +incremental commits. No DB migration needed (Phase 1 schema is forward-compatible).