5.1 KiB
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 regexexchange_diff— FX gain/loss pre-computematching_strategies— AmountExact, FIFO, MultiInvoiceprecedent_lookup— K-nearest searchpattern_extractor— per-partner aggregateconfidence_scoring— 4-pass pipeline (statistical → AI re-rank)precedent_backfill— migration helper
Persistent models in models/:
fusion.reconcile.pattern— per-(company, partner) learned profilefusion.reconcile.precedent— per-decision historyfusion.reconcile.suggestion— AI suggestions with state lifecyclefusion.bank.rec.widget— TransientModel for OWL round-tripfusion.unreconciled.bank.line.mv— pre-aggregated MV for fast UI listingfusion.bank.rec.cron— cron handler (suggest, pattern refresh, MV refresh)fusion.auto.reconcile.wizard/fusion.bulk.reconcile.wizard— TransientModel wizardsfusion.migration.wizard(inherits) — adds_bank_rec_bootstrap_stepaccount.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 wrappersviews/kanban/bank_rec_kanban_*.js— top-level controller + renderercomponents/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(usemodels.Constraint),@api.depends('id')(raisesNotImplementedError),@route(type='json')(usetype='jsonrpc'),numbercallfield onir.cron(removed),groups_idonres.users(useall_group_idsfor searching),usersfield onres.groups(useuser_ids),groups_idonir.ui.menu(usegroup_ids). -
Coexistence: When
account_accountantis installed, the fusion menu is hidden viafusion_accounting_core.group_fusion_show_when_enterprise_absent(a computed group). Engine model is always available. -
Materialized view refresh: Triggered on
fusion.reconcile.suggestioncreate/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.pyprovidesmake_bank_journal,make_bank_line,make_invoice,make_reconcileable_pair,make_suggestion,make_pattern,make_precedent. NOTE:make_bank_journaldefaults 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_suggestionreturnspartial_idsbut notis_reconciled— UI reads it post-callengine.write_offmixed mode (write-off + against_lines) implemented but untestedengine.reconcile_onereturnsexchange_diff_move_id: None(Odoo's reconcile() handles FX inline; surfacing the move_id needs an extra query)against_linesearly-break inreconcile_onesilently drops excess; auto strategy avoids this but manual callers should pre-validate- Reconcile-model bulk wizard
_apply_lines_for_bank_statement_lineis Enterprise-only (Community falls back to per-line error) - OWL tour tests skip-mode when websocket-client absent