# 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