From 956678dd272ab2b57da0a6847d02223972eb4ecd Mon Sep 17 00:00:00 2001 From: gsinghpal Date: Sat, 18 Apr 2026 20:55:22 -0400 Subject: [PATCH] docs(fusion_accounting): roadmap design for Enterprise takeover Adds the brainstormed roadmap design that turns fusion_accounting from an AI-only extension into a full replacement for Odoo 19 Enterprise accounting (account_accountant, account_reports, accountant, account_followup, plus selected satellites) for Nexa client deployments. Covers: - Sub-module topology (9 modules + meta-module): _core, _bank_rec, _reports, _dashboard, _followup, _assets, _budget, _ai, _migration - Data preservation strategy: bank reconciliations verified preserved automatically (live in Community account.partial.reconcile); shared-field-ownership pattern for Enterprise extension fields on account.move; pre-uninstall migration wizard for Enterprise-only tables - Phased roadmap: Phase 0 foundation through Phase 7+ optional satellites, with Bank Rec as Phase 1 priority and Reports as the largest phase - Architecture rules: hybrid mirror/abstract zones, fusion.* naming, runtime coexistence detection, zero hard Enterprise deps - Cross-version upgrade workflow: pinned Odoo source snapshots per version, annual diff ritual, UPGRADE_NOTES.md per sub-module - AI integration via adapter pattern (current AI tools route through adapters that prefer fusion native, fall back to Enterprise, then to pure Community) - Testing strategy, security, performance, multi-company/currency, localization, hosting Implementation of each phase happens in subsequent sessions, each with its own writing-plans pass starting with Phase 0 Foundation. Made-with: Cursor --- ...ting-enterprise-takeover-roadmap-design.md | 949 ++++++++++++++++++ 1 file changed, 949 insertions(+) create mode 100644 fusion_accounting/docs/superpowers/specs/2026-04-18-fusion-accounting-enterprise-takeover-roadmap-design.md diff --git a/fusion_accounting/docs/superpowers/specs/2026-04-18-fusion-accounting-enterprise-takeover-roadmap-design.md b/fusion_accounting/docs/superpowers/specs/2026-04-18-fusion-accounting-enterprise-takeover-roadmap-design.md new file mode 100644 index 00000000..e8c858bb --- /dev/null +++ b/fusion_accounting/docs/superpowers/specs/2026-04-18-fusion-accounting-enterprise-takeover-roadmap-design.md @@ -0,0 +1,949 @@ +# Fusion Accounting — Enterprise Takeover Roadmap + +**Status:** Design (approved 2026-04-18) +**Owner:** Nexa Systems Inc. +**Target:** Odoo 19 Community + fusion_accounting becomes a feature-complete drop-in replacement for Odoo 19 Enterprise accounting (`account_accountant`, `account_reports`, `accountant`, `account_followup`, plus selected satellite modules) for clients deployed by Nexa Systems. + +--- + +## 1. Context and Goals + +### 1.1 Current State + +`fusion_accounting` today is a thin AI co-pilot that depends on three Enterprise modules: + +```python +'depends': ['account', 'account_accountant', 'account_reports', 'account_followup', 'mail'] +``` + +It adds Claude/GPT-driven tool calling, a chat panel, a dashboard, an approval workflow, and rule-based automation on top of Odoo's accounting features. It does not own any core accounting capability — it orchestrates Enterprise's APIs. + +### 1.2 Business Driver + +Nexa Systems deploys Odoo to clients. The Enterprise subscription cost is a friction point. The goal is to deliver Enterprise-equivalent accounting capability on Odoo 19 Community via fusion_accounting, so clients can run on Community without losing core accounting features. fusion_accounting is **not** distributed publicly (no Odoo App Store listing); it ships only as part of a Nexa client engagement. + +### 1.3 Scope of "Takeover" + +The Enterprise modules being targeted, with verified file counts: + +| Enterprise Module | Files | Role | Targeted Phase | +|---|---|---|---| +| `account_accountant` | 232 | bank-rec widget, journal dashboard, fiscal year, auto-reconcile, deferred revenue/expense, signing | Phases 1, 3 | +| `account_reports` | 618 | financial reports engine + 18 standard reports | Phase 2 | +| `accountant` | 26 | menu root + glue | Phase 0 | +| `account_followup` | 58 | customer payment reminders | Phase 5 | +| `account_asset` | n/a | asset register, depreciation | Phase 6 | +| `account_budget` | n/a | budgets vs actuals | Phase 6 | +| `account_loans`, `account_3way_match`, `account_check_printing`, `account_batch_payment`, `account_iso20022`, `account_intrastat`, `account_saft`, `account_sepa_direct_debit`, `account_online_synchronization`, `account_edi_*` | n/a | various | Phase 7+ (per client need) | + +### 1.4 Existing Reference Material + +- `/Users/gurpreet/Github/Odoo-Modules/fusion_accounting/` — current AI module (will be reorganized in Phase 0) +- `/Users/gurpreet/Github/Odoo-Modules/Work in Progress/fusion_accounting/` — abandoned earlier attempt; contains 461 files of code that a Feb 2026 audit (in that folder's `AUDIT_REPORT.md`) determined to be near-verbatim copies of Odoo Enterprise. **The WIP code is not continued.** Its `__manifest__.py` is harvested as a feature checklist; its file structure as a target-architecture sanity check +- `/Users/gurpreet/Github/RePackaged-Odoo/accounting/` — pinned snapshot of Odoo 19 Enterprise accounting source; used as reference-only for clean-room rewrites and as the diff baseline for V19→V20 upgrades + +### 1.5 Non-Goals + +- Not building a public commercial product (no App Store distribution, no commercial licensing pricing model) +- Not replicating every Enterprise feature (Phase 7+ items are deferred until a real client needs them) +- Not maintaining backward compatibility with Odoo versions before 19 +- Not rewriting Community `account` — fusion_accounting builds on top of, never replaces, Community accounting + +--- + +## 2. Sub-Module Topology + +fusion_accounting is split into independently installable sub-modules. Each has a single, well-bounded responsibility and a clear Enterprise counterpart it replaces. + +### 2.1 The Sub-Modules + +```mermaid +graph TD + community["account
Odoo Community base"] + + core["fusion_accounting_core
shared fields, lock dates, fiscal year base,
company config, security groups, analytic_mixin"] + bankrec["fusion_accounting_bank_rec
reconcile widget + auto-reconcile engine"] + reports["fusion_accounting_reports
financial reports engine + standard reports"] + dashboard["fusion_accounting_dashboard
journal kanban, digest"] + followup["fusion_accounting_followup
payment reminders"] + assets["fusion_accounting_assets
asset register, depreciation"] + budget["fusion_accounting_budget
budgets vs actuals"] + ai["fusion_accounting_ai
Claude/GPT copilot + chat + dashboard tiles
(current fusion_accounting code lives here)"] + migration["fusion_accounting_migration
transitional Enterprise to fusion data wizard"] + + meta["fusion_accounting
meta-module: depends on all sub-modules"] + + core --> community + bankrec --> core + reports --> core + dashboard --> core + followup --> reports + assets --> core + budget --> core + ai --> core + migration --> core + + ai -.optional adapter calls.-> bankrec + ai -.optional adapter calls.-> reports + ai -.optional adapter calls.-> followup + ai -.optional adapter calls.-> assets + + meta --> core + meta --> bankrec + meta --> reports + meta --> dashboard + meta --> followup + meta --> assets + meta --> budget + meta --> ai + meta -.transitional only.-> migration +``` + +### 2.2 Sub-Module Responsibilities + +| Sub-module | Replaces | Owns | Phase | +|---|---|---|---| +| `fusion_accounting_core` | `accountant` (menu glue), shared bits of `account_accountant` | Shared field declarations on `account.move`/`account.bank.statement.line` (deferred fields, signing user), `fusion.fiscal.year`, lock-date wizard, security groups, settings page, `analytic_mixin` shared ownership | Phase 0 | +| `fusion_accounting_bank_rec` | `account_accountant` bank rec widget + `account_accountant/wizard/account_auto_reconcile_wizard.py` | OWL bank-rec widget, `fusion.reconcile.engine`, auto-reconcile wizard, reconcile model extensions | Phase 1 | +| `fusion_accounting_reports` | `account_reports` (entire 618-file engine + reports) | `fusion.account.report`, `fusion.account.report.line`, PDF templates, OWL report viewer, P&L/BS/TB/GL/Aged/Partner/CashFlow/Executive Summary | Phase 2 | +| `fusion_accounting_dashboard` | `account_accountant` journal dashboard, `accountant/data/account_accountant_data.xml`, digest | Journal kanban, digest tiles, "Needs Attention" data shape | Phase 3 | +| `fusion_accounting_followup` | `account_followup` | `fusion.followup.line`, follow-up workflow, multi-level reminders | Phase 5 | +| `fusion_accounting_assets` | `account_asset` | `fusion.asset`, `fusion.asset.group`, depreciation engine, asset-register report | Phase 6 | +| `fusion_accounting_budget` | `account_budget` | `fusion.budget`, budget-vs-actual report | Phase 6 | +| `fusion_accounting_ai` | (none — original) | Existing AI orchestrator, tools, chat panel, approval workflow, scoring, rules — moved verbatim from current `fusion_accounting` | Phase 0 | +| `fusion_accounting_migration` | (none — transitional) | Wizard that copies Enterprise-only data into fusion tables before Enterprise uninstall; safety guard that blocks Enterprise uninstall until wizard runs | Phase 0 | +| `fusion_accounting` (meta) | (none — packaging) | Empty shell; `depends` on every sub-module so a single install gets everything | Phase 0 | + +### 2.3 Why Split (vs. monolith) + +- Sub-modules can be enabled per client need (a small client without payroll-style assets installs core + bank_rec + reports + ai only) +- Each sub-module has independent test runs and CI (faster feedback loop) +- Each sub-module's cross-version upgrade is independent — `fusion_accounting_reports` can absorb V20 changes without touching `fusion_accounting_bank_rec` +- The AI sub-module stays cleanly separate, which makes it easy to keep using fusion's AI on top of Odoo Enterprise (when a client retains Enterprise) by installing `_ai` only + +### 2.4 Open Sub-Module Naming Decisions + +The meta-module retains the name `fusion_accounting` so existing client installs don't see a name change. Sub-modules use the `fusion_accounting_*` prefix consistently. + +--- + +## 3. Data Preservation and Client Switchover Strategy + +The single most important guarantee in this entire design: **client switchover from Odoo Enterprise to Odoo Community + fusion_accounting must lose zero accounting data**, especially bank reconciliations. + +This section is the contract that backs that guarantee. + +### 3.1 What Survives an Enterprise Uninstall Automatically + +Verified by direct read of `RePackaged-Odoo/accounting/account/` source. These models and fields live in the Community `account` module and are unaffected by any Enterprise uninstall: + +| Data | Storage | Verified Location | +|---|---|---| +| Bank reconciliation links | `account.partial.reconcile` | `account/models/account_partial_reconcile.py` | +| Full reconciliation markers | `account.full.reconcile` | `account/models/account_partial_reconcile.py` | +| Bank statement lines + `is_reconciled` flag | `account.bank.statement.line` | `account/models/account_bank_statement_line.py` | +| Invoices, bills, payments | `account.move`, `account.payment` | `account/models/account_move.py`, `account_payment.py` | +| Journal entries + lines | `account.move`, `account.move.line` | `account/models/account_move_line.py` | +| Chart of accounts | `account.account` | `account/models/account_account.py` | +| Taxes | `account.tax` | `account/models/account_tax.py` | +| Journals | `account.journal` | `account/models/account_journal.py` | +| Partners | `res.partner` | `base` | +| Reconciliation rule base | `account.reconcile.model` | `account/models/account_reconcile_model.py` | +| `checked` (Reviewed) flag on moves | `account.move.checked` | `account/models/account_move.py` line 315 | + +**Critical observation about bank reconciliation in Odoo 19:** The Enterprise `account_accountant` module does **not** define a `bank.rec.widget` Python model in V19. The bank-rec widget is implemented entirely as frontend OWL components in `account_accountant/static/src/components/bank_reconciliation/`, with a thin `BankReconciliationService` (`bank_reconciliation_service.js`) that calls Community ORM methods directly. There is no Enterprise-side persistent storage for the widget. When the widget is removed (Enterprise uninstall), the underlying `account.partial.reconcile` rows are untouched; fusion's replacement widget reads the same rows and shows every historical reconciliation as already-matched. + +(The Work-in-Progress code at `Work in Progress/fusion_accounting/models/bank_rec_widget.py` uses the V17/V18 architecture where `bank.rec.widget` was a `_auto = False` Python model. That architecture was removed in V19. Our Phase 1 implementation must match V19 architecture.) + +**Verified Enterprise uninstall hook safety**: `account_accountant/__init__.py` line 32-42 only revokes security group assignments. There are zero destructive DB operations in the uninstall hook. + +**Verified absence of cascade hazards**: grep for `ondelete='cascade'` in `account_accountant/models/` returns zero matches. No Enterprise model deletion can cascade-delete a reconciliation. + +### 3.2 What Is Lost on Enterprise Uninstall (Without Mitigation) + +| Enterprise-owned data | Importance | Mitigation Strategy | +|---|---|---| +| `account.fiscal.year` records (fiscal year closing definitions) | Medium | Migration wizard → `fusion.fiscal.year` | +| `account.asset` records + asset-line links on moves | High if assets used | Migration wizard → `fusion.asset` | +| `account.loan` records | Low (rare) | Migration wizard → `fusion.loan` (Phase 7+) | +| Budget records | Medium if used | Migration wizard → `fusion.budget` | +| Follow-up rule definitions + history | Medium | Migration wizard → `fusion.followup.*` | +| `account.move.deferred_move_ids`, `deferred_original_move_ids`, `deferred_entry_type` | **High** if deferred revenue/expense used — breaks the link between original and deferred postings | **Shared-field ownership** in `fusion_accounting_core` | +| `account.move.signing_user` (audit signer) | Medium | **Shared-field ownership** | +| `account.move.payment_state_before_switch` | Throwaway (technical) | Ignore | +| `account.reconcile.model.created_automatically` | Throwaway (single boolean) | Shared-field ownership in `_bank_rec` | +| `account.bank.statement.line.cron_last_check` | Throwaway (technical) | Ignore | +| Report XML records (P&L, BS structure) | None — reference data, not client data | fusion ships its own equivalents in `_reports` | +| Enterprise-only menus, actions | None — UI only | fusion installs its own | + +### 3.3 Mitigation Pattern A: Shared-Field Ownership + +For Enterprise-added fields on Community models (the `deferred_*`, `signing_user`, `created_automatically` fields), `fusion_accounting_core` declares **identical** field definitions with the **same** relation table names: + +```python +class AccountMove(models.Model): + _inherit = "account.move" + + deferred_move_ids = fields.Many2many( + comodel_name='account.move', + relation='account_move_deferred_rel', # identical relation table to Enterprise + column1='original_move_id', + column2='deferred_move_id', + copy=False, + ) + deferred_original_move_ids = fields.Many2many( + comodel_name='account.move', + relation='account_move_deferred_rel', + column1='deferred_move_id', + column2='original_move_id', + copy=False, + ) + deferred_entry_type = fields.Selection( + selection=[('expense', 'Deferred Expense'), ('revenue', 'Deferred Revenue')], + copy=False, + ) + signing_user = fields.Many2one(comodel_name='res.users', copy=False) + payment_state_before_switch = fields.Char(copy=False) +``` + +**Mechanism**: Odoo's module registry tracks every module that declares a given field on a given model. When `account_accountant` uninstalls, Odoo only drops the column (or relation table) if no other installed module also declares it. Because `fusion_accounting_core` declares these identically, Odoo retains the column/table. Existing data values are preserved row-by-row. + +**Caveat**: this pattern creates a schema dependency on Enterprise's choices. If Odoo ever renames `account_move_deferred_rel` in V20, both the Enterprise and fusion versions of that field break together — the migration is just `ALTER TABLE ... RENAME` in our migration script. We accept this risk because the alternative (renaming to fusion-namespaced fields) requires a much heavier migration of every existing row. + +### 3.4 Mitigation Pattern B: Pre-Uninstall Migration Wizard + +For Enterprise-only models (`account.asset`, `account.fiscal.year`, `account.loan`, budgets, followups), `fusion_accounting_migration` provides a wizard accessible from Settings → Fusion Accounting → Migrate from Enterprise. + +The wizard: + +1. Detects which Enterprise modules are installed +2. For each detected module, checks the corresponding fusion module is also installed (and prompts to install if missing) +3. Shows a preview: row counts per Enterprise table that will be migrated, listing target fusion table for each +4. On confirm, runs `INSERT INTO fusion_ SELECT ... FROM ` for each migration step, preserving primary keys and `ir.model.data` xml_ids +5. Generates a migration report (record counts, any rows that failed validation, warnings) +6. Marks each Enterprise table as "migrated" via an `ir.config_parameter` flag (`fusion_accounting.migration..completed`) +7. Re-running the wizard is idempotent: already-migrated tables are skipped unless explicitly re-migrated + +A separate **safety guard** in `fusion_accounting_migration` overrides `ir.module.module.button_immediate_uninstall` for Enterprise accounting modules; if the migration flag for that module is False and it has data, the uninstall is blocked with a UserError linking to the wizard. + +### 3.5 Switchover Protocol (the operator workflow) + +```mermaid +graph TD + start[Client on Odoo 19 Enterprise] --> step1["Install fusion_accounting meta-module
while Enterprise still running"] + step1 --> step2["fusion_accounting_core declares shared fields
Odoo registers dual ownership for deferred_*, signing_user, etc."] + step2 --> step3["Open Settings → Fusion Accounting → Migrate from Enterprise"] + step3 --> step4["Wizard shows preview: row counts per table"] + step4 --> step5["Operator confirms"] + step5 --> step6["Wizard copies asset, fiscal year, loan, budget, followup rows
into fusion tables"] + step6 --> step7["Wizard generates migration report"] + step7 --> step8["Operator reviews report"] + step8 --> step9["Operator triggers Enterprise uninstall in dep-safe order:
account_reports → account_followup → account_asset →
account_budget → account_loans → account_accountant → accountant"] + step9 --> step10["Safety guard verifies migration flags before each uninstall"] + step10 --> done["Done: Client on Community + fusion_accounting
Bank recs intact, deferred links preserved,
migrated data accessible via fusion menus"] +``` + +### 3.6 Empirical Verification Test (Phase 0 deliverable) + +The shared-field-ownership analysis and the inventory of "what survives" is based on reading source. Strong, but not conclusive. **Phase 0 includes a one-time empirical test**: + +1. Provision a throwaway Odoo 19 Enterprise instance +2. Install full Enterprise accounting stack +3. Create representative test data: + - 50 invoices, 30 vendor bills, mix of paid/unpaid + - 15 bank reconciliations (full and partial) + - 5 deferred revenue entries with `deferred_move_ids` populated + - 3 fiscal year closings + - 10 asset records with depreciation history + - 2 budgets with actuals + - Multi-currency journal entries + - 1 cash-basis tax move +3. Take `pg_dump` snapshot +4. Uninstall Enterprise modules in dep-safe order **without** running the migration wizard (this is the worst-case test) +5. Diff schema and row counts before and after +6. Document findings in `docs/superpowers/specs/2026-04-18-empirical-uninstall-test-results.md` +7. If gaps are found vs. Section 3.2, expand the wizard scope or shared-field declarations accordingly + +This test is a Phase 0 acceptance gate. The roadmap does not advance to Phase 1 until empirical verification confirms or expands the analysis. + +### 3.7 Reverse-Migration Note + +The reverse direction (client on Community + fusion adds an Enterprise subscription later) is not a hard requirement. fusion's runtime feature-gating (Section 4.4) handles the coexistence case: when Enterprise is detected, fusion's conflicting menus hide and the AI module continues running on top of Enterprise. A reverse-migration wizard can be added in Phase 7+ if a real client needs it. + +### 3.8 Backup and Rollback + +Every client deployment must include, before any switchover step: + +- `pg_dump` of the live database +- Snapshot of all installed module versions (`SELECT name, latest_version FROM ir_module_module WHERE state='installed'`) +- Snapshot of `/mnt/extra-addons/` contents + +Rollback procedure: restore DB from `pg_dump`, restore extra-addons from snapshot, restart Odoo. The migration wizard's "Generate Backup First" checkbox is checked by default and must be explicitly unchecked to skip. + +--- + +## 4. Phased Roadmap + +Each phase produces shippable value. Phase order is locked. Time estimates are rough single-engineer figures and are not binding deadlines — the user has explicitly stated "no rush, product-first". + +### 4.1 Phase Overview + +| Phase | Focus | Estimate | Depends On | +|---|---|---|---| +| 0 | Foundation, sub-module split, migration scaffold, empirical test | 1-2 wks | (none) | +| 1 | Bank reconciliation (priority) | 3-5 wks | 0 | +| 2 | Financial reports engine | 6-10 wks | 0 | +| 3 | Dashboard + fiscal year + lock dates | 2-3 wks | 1, 2 | +| 4 | Tax reports + returns | 3-5 wks | 2 | +| 5 | Payment follow-ups | 2-3 wks | 3, 4 | +| 6 | Assets + budgets | 3-5 wks | 5 | +| 7+ | Optional satellites (loans, check printing, batch payment, 3-way match, EDI, SEPA, SAFT, intrastat, online sync) | per item | 6 | + +Phases 1 and 2 can run in parallel after Phase 0 (no shared scope). + +### 4.2 Phase 0 — Foundation + +No user-facing features. Pure plumbing so every later phase is cheaper. + +**Scope:** + +- Create sub-module scaffolding for `fusion_accounting_core`, `fusion_accounting_migration`, `fusion_accounting_ai` +- Move existing AI copilot code from current `fusion_accounting/` into `fusion_accounting_ai/`. Files moved: `models/`, `services/`, `controllers/`, `wizards/`, `data/`, `static/src/`, `views/`, `security/`, `report/`, `tests/`. Update internal imports +- Convert current `fusion_accounting/` into the meta-module: empty `__init__.py`, manifest with `depends = ['fusion_accounting_core', 'fusion_accounting_ai', ...]` (sub-modules added as later phases ship), no Python/JS/XML code of its own +- Strip hard Enterprise deps from `fusion_accounting_ai/__manifest__.py`. Replace `account_accountant`, `account_reports`, `account_followup` with `account` (Community). Add runtime detection (Section 4.4) +- Refactor every AI tool in `fusion_accounting_ai/services/tools/` that calls Enterprise APIs to go through an adapter layer (`services/adapters/bank_rec_adapter.py`, `reports_adapter.py`, `followup_adapter.py`). Adapters pick between Enterprise APIs (when present) and fusion native (when present) and a "feature-unavailable" stub (when neither) +- Create `fusion_accounting_core/models/account_move.py` with shared-field declarations (Section 3.3) +- Create `fusion_accounting_migration/` shell: empty wizard, safety guard scaffold (no migrations yet) +- Create `tools/check_odoo_diff.sh` script that diffs two pinned Odoo source snapshots and outputs a categorized change list +- Move security groups: `group_fusion_accounting_user/manager/admin` move from current to `fusion_accounting_core/security/`. Multi-company record rule on `fusion.accounting.session` added (currently missing per existing CLAUDE.md "Known Issues") +- Create per-sub-module `CLAUDE.md` (factor common rules from existing `fusion_accounting/CLAUDE.md`) and `UPGRADE_NOTES.md` template +- Run the empirical verification test (Section 3.6) on a throwaway V19 Enterprise instance +- CI: GitHub Actions or gitea workflow that runs `pytest` per sub-module on every push + +**Exit criteria:** + +- Current AI copilot installs and runs on pure Community (no Enterprise modules present) +- Current AI copilot still installs and runs alongside Enterprise (coexistence mode) +- Empirical test report committed +- All adapter calls wired (no direct Enterprise API access from AI tools) +- CI green + +**Risks and mitigations:** + +- **Risk**: moving code between modules breaks existing client deployments. **Mitigation**: meta-module install upgrade hook handles model-record reassignment via `ir_model_data` updates; pre-migration script runs on first install of Phase 0 +- **Risk**: empirical test reveals gaps. **Mitigation**: scope-expand the migration wizard before declaring Phase 0 complete + +### 4.3 Phase 1 — Bank Reconciliation + +The user's stated priority. Replaces `account_accountant`'s bank-rec widget end-to-end. + +**Scope:** + +- Create `fusion_accounting_bank_rec/` sub-module +- **Frontend (mirror zone)**: build `static/src/components/bank_reconciliation/` mirroring the file layout of `account_accountant/static/src/components/bank_reconciliation/` (`kanban_controller`, `kanban_renderer`, `bank_reconciliation_service`, `apply_amount`, `bankrec_form_dialog`, `button`, `button_list`, `chatter`, `file_uploader`, `line_info_pop_over`, `line_to_reconcile`, `list_view`, `quick_create`, `reconciled_line_name`, `search_dialog`, `statement_line`, `statement_summary`). Mirror is structural — class names, file names, OWL component boundaries — not copy-paste. Implementation written fresh against documented Odoo behavior +- **Backend (abstract zone)**: `models/fusion_reconcile_engine.py` containing the matching algorithm (FIFO, partial reconcile, write-off lines, exchange-rate diff posting, tax splits). Original implementation against documented requirements. Operates on Community `account.partial.reconcile` +- `models/fusion_reconcile_model.py` extending Community `account.reconcile.model` with auto-rules, partner mapping, journal mapping. Shared-field ownership for `created_automatically` +- `wizards/auto_reconcile_wizard.py` clean-room rewrite of `account_accountant/wizard/account_auto_reconcile_wizard.py` +- `wizards/reconcile_wizard.py` clean-room rewrite of `account_accountant/wizard/account_reconcile_wizard.py` +- `views/bank_rec_widget_views.xml` defines the action that opens the OWL widget; `views/account_reconcile_model_views.xml` for rule editing +- Menu: "Bank Reconciliation" under fusion accounting menu, with feature-gate (hidden if `account_accountant` installed) +- AI integration: existing AI tools `get_unreconciled_bank_lines`, `find_similar_bank_lines`, `get_bank_line_details`, `find_missing_itc_bills`, `find_duplicate_bills`, `get_overdue_invoices` get refactored to call fusion's bank rec engine via `fusion_accounting_ai/services/adapters/bank_rec_adapter.py`. The Tier 3 tools `create_vendor_bill`, `register_bill_payment`, `create_expense_entry` keep their existing logic (they write to Community `account.move`) +- Migration: wizard validates `account.partial.reconcile` row count is preserved across switchover (read-only check, no migration needed) +- Tests: + - Unit (engine): matching correctness with fixtures (single partner, multi-partner, multi-currency, partial, exchange diff, write-off, tax split) + - Integration: install + create statement + reconcile via UI + assert `account.partial.reconcile` rows + - Tour (JS): smoke through the full bank rec workflow + - Migration: install Enterprise, create 10 reconciliations, install fusion, uninstall Enterprise, assert reconciliations visible in fusion widget + +**Exit criteria:** + +- Community + fusion_accounting user can reconcile bank statements with feature parity to Enterprise +- All Phase 1 tests passing +- Migration round-trip (Enterprise → fusion) preserves every reconciliation +- AI tools work against fusion bank rec engine + +### 4.4 Phase 2 — Financial Reports Engine + +The largest phase. Replaces `account_reports` (618 files). + +**Scope:** + +- Create `fusion_accounting_reports/` sub-module +- **Backend (abstract zone)**: `models/fusion_account_report.py` defining `fusion.account.report` and `fusion.account.report.line`. Generic engine that takes a report definition (sections, filters, computation rules) and produces report rows from `account.move.line` data. Original computation kernel — does not copy `account_reports`'s `account_report.py` +- **Backend (mirror zone)**: report definition records mirror Odoo's data files. Files: `data/balance_sheet.xml`, `data/profit_and_loss.xml`, `data/cash_flow_report.xml`, `data/general_ledger.xml`, `data/trial_balance.xml`, `data/aged_partner_balance.xml`, `data/partner_ledger.xml`, `data/executive_summary.xml`, `data/sales_report.xml`, `data/multicurrency_revaluation_report.xml`, `data/bank_reconciliation_report.xml`, `data/deferred_reports.xml`, `data/journal_report.xml`, `data/customer_statement.xml`. XML structure follows Odoo's so V20 ports are diff-and-apply +- **Frontend (mirror zone)**: `static/src/components/` mirrors `account_reports/static/src/components/` — filters bar, comparison toggle, drill-down, foldable sections, footnotes +- **PDF export**: QWeb templates in `report/` mirror Odoo's `data/pdf_export_templates.xml` and `data/customer_reports_pdf_export_templates.xml`. Asset bundle `fusion_accounting_reports.assets_pdf_export` defined in manifest +- Performance: denormalized read paths for trial balance and general ledger (materialized aggregations refreshed on `account.move` post). Drill-down lazy-loads line detail. Per-(company, period, filter_hash) cache invalidated on `account.move.line` write +- Multi-company, multi-currency, cash-basis toggle — all handled by the engine +- AI integration: tools `get_profit_loss`, `get_balance_sheet`, `get_trial_balance`, `get_aged_receivables`, `get_aged_payables`, `get_partner_ledger`, `answer_financial_question` refactored via `reports_adapter.py` +- Migration: report XML records are reference data, not client data. fusion ships its own equivalent records; no migration of report definitions needed. Existing journal entry data (which the reports compute from) is in Community `account` and untouched +- Tests: + - Unit (engine): SQL-fixture comparisons (compute report → compare against hand-rolled SQL) for every standard report, every filter combination + - Integration: install + post entries + open report + assert numbers + - Multi-currency: single + multi + revaluation period + - Performance: 1k / 10k / 100k journal lines, assert P95 latency under 5s + - PDF: render every report to PDF, assert no QWeb errors + - Tour: smoke through report viewer with filters + +**Exit criteria:** + +- All 14 standard reports rendering correctly (numerical match against SQL fixtures) +- PDF export working for every report +- Performance targets met +- AI tools backed by fusion reports + +### 4.5 Phase 3 — Dashboard + Fiscal Year + Lock Dates + +**Scope:** + +- Create `fusion_accounting_dashboard/` sub-module +- **Journal kanban dashboard**: mirror layout of `account_accountant/views/account_journal_dashboard_views.xml`. Computed metrics in `models/account_journal.py` extending Community `account.journal` with kanban-state fields (counts, totals, action buttons). Original computation; mirror UI +- `models/fusion_fiscal_year.py` defining `fusion.fiscal.year` (replaces `account.fiscal.year`) +- Fiscal year wizard: closing workflow, period locks, initial-balance carry-forward +- Lock date wizard: clean-room rewrite of `account_accountant/wizard/account_change_lock_date.py`. Operates on Community `account.lock_exception` model (verified at `account/models/account_lock_exception.py`) +- Digest tile contributions: extend `mail.digest` with fusion accounting metrics (revenue, expense, AR, AP) +- "Needs Attention" panel — connect data already returned by current AI dashboard endpoint to a frontend rendering. Dashboard endpoint (currently in `fusion_accounting_ai/controllers/`) moves to `fusion_accounting_dashboard/controllers/`; AI module's dashboard tiles call dashboard's endpoint via adapter +- Tests: + - Journal dashboard kanban metrics match expected values for fixtures + - Fiscal year close locks subsequent edits + - Lock date wizard prevents posting before lock date + - Digest renders without errors + +**Exit criteria:** + +- Journal dashboard at parity with Enterprise +- Fiscal year management functional +- Lock dates enforced +- Digest emails delivering + +### 4.6 Phase 4 — Tax Reports + Returns + +**Scope:** + +- Build on Phase 2 reports engine; tax reports are specialized `fusion.account.report` records +- Generic tax report (`data/generic_tax_report.xml`) with country-specific overrides +- Canadian HST: unify the existing HST workflow in `fusion_accounting_ai` (currently in `services/prompts/domain_prompts.py` and tool functions) with the new tax report engine. The existing `find_missing_itc_bills`, `get_overdue_invoices`, etc. tools call into the tax report +- `fusion.account.return` model (replaces `account.return` from `account_reports`) tracking tax return drafts, submitted state, payment status +- Return creation wizard, return submission wizard, return generic payment wizard — clean-room rewrites of the corresponding `account_reports` wizards +- Tax closing entries (move generation on tax period close) +- Tests: + - Tax report numbers match SQL fixtures + - Return workflow: draft → review → submitted → paid + - HST 4-phase workflow (per existing CLAUDE.md) end-to-end + +**Exit criteria:** + +- Generic tax report functional +- Canadian HST workflow runs through fusion (no Enterprise dependency) +- Return tracking working + +### 4.7 Phase 5 — Payment Follow-ups + +**Scope:** + +- Create `fusion_accounting_followup/` sub-module +- `models/fusion_followup_line.py` (replaces `account_followup.followup.line`) +- `models/res_partner.py` extends `res.partner` with follow-up level, last reminder date, dunning history +- `models/account_move.py` extends `account.move` with follow-up state (overdue days, last reminder) +- Multi-level reminder workflow: each level has email template, days delay, optional SMS, optional `mail.activity` +- `wizards/followup_send_wizard.py` for manual sends; cron for automatic +- Follow-up report (PDF): clean-room template +- AI integration: `fusion_accounting_ai` adds tools `draft_followup_message_for_partner`, `send_followup_to_overdue_partners` calling the followup engine via adapter +- Migration: wizard copies `account_followup.followup.line` and partner-level follow-up state into `fusion.followup.line` and shared-field-owned partner fields +- Tests: + - Multi-level escalation + - Email template rendering + - SMS delivery (mock) + - AI-drafted message quality (snapshot tests) + +**Exit criteria:** + +- Multi-level dunning working +- Migration from `account_followup` preserves history + +### 4.8 Phase 6 — Assets + Budgets + +**Scope:** + +- Create `fusion_accounting_assets/` sub-module + - `models/fusion_asset.py` (replaces `account.asset`) + - `models/fusion_asset_group.py` (replaces `account.asset.group`) + - Depreciation engine: linear, declining, custom schedules. Original implementation + - `wizards/asset_modify.py` for revaluation, sale, disposal — clean-room rewrite + - Asset register report integrates with Phase 2 reports engine + - Migration wizard copies `account.asset` rows + line links on moves +- Create `fusion_accounting_budget/` sub-module + - `models/fusion_budget.py` (replaces `budget.analytic`) + - Budget vs actual report integrates with Phase 2 reports engine + - Migration wizard copies budget records +- Tests for both + +**Exit criteria:** + +- Asset depreciation schedules computed correctly +- Disposal generates correct GL entries +- Budget variance report functional + +### 4.9 Phase 7+ — Optional Satellites + +Not scheduled. Each is its own brainstorming → spec → plan → implementation cycle when a real client needs it. Candidate satellite modules: + +- `fusion_accounting_loans` — loan amortization +- `fusion_accounting_check_printing` — check printing +- `fusion_accounting_batch_payment` — batch payments +- `fusion_accounting_3way_match` — purchase 3-way match +- `fusion_accounting_edi` — UBL/CII e-invoicing +- `fusion_accounting_sepa` — SEPA direct debit + credit transfer +- `fusion_accounting_saft` — SAFT export +- `fusion_accounting_intrastat` — intrastat report +- `fusion_accounting_iso20022` — ISO 20022 payment files +- `fusion_accounting_online_sync` — online bank sync (Yodlee/Plaid integration) + +### 4.10 Per-Phase Deliverables (uniform) + +Each phase produces: + +1. A separate **design document** in `docs/superpowers/specs/YYYY-MM-DD-fusion-accounting-phase-N-*-design.md` (brainstormed in its own session) +2. A separate **implementation plan** via the `writing-plans` skill +3. Working code with passing tests +4. Entry in the sub-module's `UPGRADE_NOTES.md` listing Odoo source files referenced and intentional deltas +5. Coverage in `fusion_accounting_migration` if the phase replaces an Enterprise data-bearing model +6. Manual QA checklist (install, migrate, smoke, uninstall) committed to the sub-module +7. Update to the meta-module `__manifest__.py` adding the new sub-module to its `depends` + +--- + +## 5. Architecture Rules + +These rules apply to every sub-module and every phase. They are the discipline that keeps V19→V20 upgrades mechanical and prevents the WIP-style descent into copied code with stale architecture. + +### 5.1 The Hybrid Split + +Every sub-module has two zones with different rules: + +**Mirror zone** (follows Odoo structure 1:1): + +- XML view definitions and xpath targets +- Frontend OWL component file layout, service registration, widget props +- PDF/QWeb templates: structure, CSS class names +- Wizard flows: step order, field names where they appear in views +- Asset bundle declarations in manifests + +**Locations**: `views/`, `static/src/components/`, `report/` QWeb templates, `wizards/*_views.xml`, `__manifest__.py` asset bundles + +**Abstract zone** (our own design, insulated from Odoo internals): + +- Core algorithms: matching, aggregation, computation, depreciation +- Data access helpers +- Business validation, approval flows +- AI integration adapters +- Engine classes (e.g. `fusion_reconcile_engine.py`) + +**Locations**: `models/fusion_*_engine.py`, `services/`, `controllers/` (business logic only — request routing is mirror-zone) + +**Rule of thumb**: if Odoo refactors it every release, mirror it. If it's been stable for a decade (FIFO matching, accrual rules, depreciation math), abstract it. + +### 5.2 Naming Conventions + +| Thing | Convention | Example | +|---|---|---| +| Model `_name` | `fusion.*` prefix always | `fusion.bank.rec.widget`, `fusion.account.report`, `fusion.fiscal.year` | +| Model `_inherit` on Community | Keep `account.*` (no rename) | `class AccountMove(models.Model): _inherit = 'account.move'` | +| Model `_inherit` on Enterprise | **Forbidden** — duplicate fields via shared-field-ownership instead | n/a | +| Python class names | `Fusion` prefix for new models | `FusionBankRecWidget`, `FusionAccountReport` | +| Table names (auto-derived) | Follows model prefix | `fusion_bank_rec_widget`, `fusion_account_report` | +| XML record IDs | `fusion_*` prefix | `` | +| Menu IDs | `fusion_menu_*` prefix | Avoids collision with `account_menu_*` | +| Action IDs | `fusion_action_*` | Same | +| Controller routes | `/fusion_accounting/*` | Already in use; carries forward | +| Security groups | `group_fusion_*` | Already in use | +| Field names on inherited Community models | Identical to Enterprise if shared-field-owned; otherwise `x_fusion_*` prefix | `deferred_move_ids` (shared), `x_fusion_ai_confidence` (our own) | +| CSS/SCSS classes | `.fusion_*` or `.o_fusion_*` | Avoids Bootstrap/Odoo collision | +| `ir.config_parameter` keys | `fusion_accounting.*` | Already in use | + +### 5.3 Coexistence Detection + +Every sub-module that replaces an Enterprise feature must detect Enterprise at install time and at runtime, and feature-gate accordingly. + +**Helper function** (lives in `fusion_accounting_core/models/ir_module_module.py`): + +```python +class IrModuleModule(models.Model): + _inherit = "ir.module.module" + + @api.model + def _fusion_is_enterprise_accounting_installed(self): + return bool(self.sudo().search_count([ + ('name', 'in', ['account_accountant', 'account_reports', 'accountant']), + ('state', '=', 'installed'), + ])) +``` + +**Three coexistence modes per sub-module**, configurable in Settings → Fusion Accounting → Integration Mode: + +1. **Replace** (default when Enterprise absent): fusion menus visible, fusion views primary, fusion workflows active +2. **Augment** (default when Enterprise present): fusion menus hidden, fusion widgets disabled, fusion AI module continues to call Enterprise APIs via adapters +3. **Force-replace** (manual): fusion menus visible alongside Enterprise (operator's choice — risk of confusion, used during migration) + +Menu visibility achieved via `groups` attribute referencing a dynamically-computed group (`group_fusion_show_menus_when_enterprise_absent`), implemented as a `@api.depends` computed field on `res.users` that recomputes membership when modules change state. + +### 5.4 Zero Hard Enterprise Dependencies + +After Phase 0: + +- `fusion_accounting_core/__manifest__.py`: `depends = ['account', 'mail', 'web_tour']` +- `fusion_accounting_ai/__manifest__.py`: `depends = ['fusion_accounting_core']` plus `external_dependencies` for `anthropic`, `openai` +- Every other `fusion_accounting_*/__manifest__.py`: `depends = ['fusion_accounting_core']` plus fusion siblings as needed (e.g., `_followup` depends on `_reports`) + +**No `fusion_accounting_*` module may have `account_accountant`, `account_reports`, `accountant`, `account_followup`, `account_asset`, `account_budget`, `account_loans`, `account_3way_match`, `account_check_printing`, `account_batch_payment`, `account_iso20022`, `account_intrastat`, `account_saft`, `account_sepa_direct_debit`, `account_online_synchronization`, or any `account_edi_*` in its `depends`.** + +Runtime detection (Section 5.3) replaces compile-time dependency. + +### 5.5 Canonical Sub-Module Directory Layout + +``` +fusion_accounting_/ +├── __manifest__.py +├── __init__.py +├── CLAUDE.md # module-specific context for Cursor agent +├── UPGRADE_NOTES.md # Odoo version deltas absorbed +├── README.md # operator-facing install/configure/troubleshoot +├── docs/ +│ └── odoo_diff/ # snapshots of relevant Odoo source for diffing +│ └── v19/ +│ └── account_accountant__bank_reconciliation_service.js +├── controllers/ +│ └── __init__.py +├── data/ +├── demo/ +├── i18n/ +├── models/ +│ ├── __init__.py +│ ├── fusion__engine.py # abstract zone: core algorithm +│ ├── account_.py # mirror zone: inherits Community model +│ └── fusion_.py # mirror zone: our own models +├── report/ +├── security/ +│ ├── ir.model.access.csv +│ └── _security.xml +├── services/ # AI / heavy business logic +├── static/ +│ ├── description/ +│ │ ├── icon.png +│ │ └── index.html +│ └── src/ +│ ├── components/ # mirror zone: OWL components +│ ├── scss/ +│ ├── services/ # frontend services +│ └── views/ +├── tests/ +│ ├── __init__.py +│ ├── test__engine.py # abstract zone unit tests +│ ├── test__integration.py # full-stack integration tests +│ ├── test_migration.py # Enterprise → fusion round-trip +│ └── tours/ +├── views/ +├── wizards/ +└── migrations/ # Odoo version migration scripts (XX.0.x.y.z) + └── 19.0.1.0.0/ + ├── pre-migration.py + └── post-migration.py +``` + +### 5.6 Odoo 19 Gotchas (carried forward, factored across CLAUDE.md files) + +The current `fusion_accounting/CLAUDE.md` documents Odoo 19-specific traps that have already cost time. All carry forward: + +- Search views: no `string` attribute on `` or ``; group-by filters need `domain="[]"`; `` before `` +- OWL client actions: `static props = ["*"]` (accept any), not `static props = []` (accept none) +- OWL rich HTML: `markup()` and `t-out` unreliable in Odoo 19; use `onMounted` + `onPatched` + direct `innerHTML` +- Cron `safe_eval`: no `import` statements; use `datetime.datetime.now()` not `from datetime import datetime` +- `read_group()` deprecated → use `_read_group()` +- `ir_config_parameter` Selection field migrations: stored DB value must match new options or Settings page crashes +- `implied_ids` on groups only applies to newly-added users — existing users need SQL backfill +- `TransientModel` in controllers: use `.new({...})` not `.create({...})` +- HTTP routes: `type="jsonrpc"`, not `type="json"` (deprecated) +- `res.config.settings`: only boolean/integer/float/char/selection/many2one/datetime; no Date fields +- `res.groups`: no `users` field, no `category_id` field +- Search views: no `group expand="0"` syntax +- SCSS imports: `@import "./partial"` is forbidden in Odoo 19 custom SCSS; register every SCSS file as a separate entry in `web.assets_backend` +- Card styling: don't rely on `var(--bs-border-color)` or `var(--bs-body-bg)`; use Odoo's kanban explicit-hex pattern with custom-property tokens +- Dark mode: branch on `$o-webclient-color-scheme` at SCSS compile time, not runtime DOM class +- Asset bundle cache busting: bump manifest version + `DELETE FROM ir_attachment WHERE url LIKE '/web/assets/%'` if needed + +These rules belong in each sub-module's `CLAUDE.md` (the relevant subset) plus the workspace-root `CLAUDE.md` (common rules). + +### 5.7 Manifest Versioning and Branch Strategy + +- Per-sub-module manifest: `'version': 'XX.0.x.y.z'` where XX is the Odoo version (e.g., `19.0.1.0.0` for V19, first release) +- Bump `XX` on Odoo version change (V19 → V20 → V21) +- Bump `x` on major feature additions within an Odoo version +- Bump `y` on minor features and bug fixes +- Bump `z` on hotfixes +- Git branches: `main-v19`, `main-v20`, etc. Each client deployment is pinned to one branch +- Release tags: `/v19.0.1.0.0` per sub-module per release + +--- + +## 6. Cross-Version Upgrade Workflow + +This section is the user's stated top concern: how to keep porting Enterprise changes forward each year without it becoming a rewrite project. + +### 6.1 Snapshot Discipline + +Maintain one pinned snapshot of the relevant Odoo source per Odoo version: + +``` +/Users/gurpreet/Github/RePackaged-Odoo/ +├── accounting-v19/ # current snapshot (already in place at accounting/) +├── accounting-v20/ # added when V20 ships +├── accounting-v21/ # added when V21 ships +``` + +Older snapshots are never deleted — they are the diff source for upgrade work. + +### 6.2 Annual Upgrade Ritual + +When Odoo V ships: + +1. Add the snapshot folder +2. For each fusion sub-module: + - Run `tools/check_odoo_diff.sh v v > reports/v__diff.md` + - Manually classify each change in the diff: + - `[MIRROR]` — apply the same hunk to fusion's mirror-zone files (mechanical) + - `[ABSTRACT]` — verify the Odoo public API surface our adapter uses still works; update the adapter if signatures changed + - `[NEW FEATURE]` — decide port or defer + - `[BUG FIX]` — port (usually cheap) + - `[REMOVED]` — clean up our equivalent + - Apply mirror-zone hunks (these are usually direct `patch -p1` operations) + - Write Odoo version migration scripts in `migrations/.0.0.0.0/` for any data-shape changes + - Update `UPGRADE_NOTES.md` + - Run all tests +3. Tag releases on `main-v` branch +4. Pilot upgrade on one client first; ratchet outward + +### 6.3 `UPGRADE_NOTES.md` Template + +```markdown +# UPGRADE_NOTES — fusion_accounting_bank_rec + +## V19.0.1.0.0 (initial) +- Ported from: account_accountant V19 (snapshot date 2026-04-18) +- Mirror sources: + - account_accountant/static/src/components/bank_reconciliation/* → fusion_accounting_bank_rec/static/src/components/bank_reconciliation/* + - account_accountant/wizard/account_auto_reconcile_wizard.py → fusion_accounting_bank_rec/wizards/auto_reconcile_wizard.py (clean-room) +- Abstract zone: + - models/fusion_reconcile_engine.py — original implementation +- Intentional deltas from Odoo: + - AI hook in reconcile step (calls fusion_accounting_ai.suggest_match adapter) + - Different default colour palette (SCSS var overrides) + +## V20.0.x.y.z (planned, not yet shipped) +- Odoo changes account_accountant V19 → V20 absorbed: + - [MIRROR] kanban_renderer.js: column layout changed, applied identical hunk + - [ABSTRACT] account.reconcile.model._apply_lines_for_bank_widget signature changed — updated adapter + - [NEW FEATURE] batch-reconcile-across-journals — deferred to V20.1 +- Migration scripts: + - migrations/20.0.0.0.0/pre-migration.py: rename column foo → bar +``` + +### 6.4 `tools/check_odoo_diff.sh` Specification + +The script lives at `fusion_accounting/tools/check_odoo_diff.sh` (workspace root, shared across sub-modules). Usage: + +```bash +tools/check_odoo_diff.sh [] +``` + +Behavior: + +- Runs `diff -ruN /Users/gurpreet/Github/RePackaged-Odoo/accounting-/ /Users/gurpreet/Github/RePackaged-Odoo/accounting-/` +- Splits output into per-file sections +- For each file, classifies based on file path: `views/` and `static/src/components/` and `report/` → `[MIRROR]` candidate; `models/*_engine.py`-like → `[ABSTRACT]` review; new files → `[NEW FEATURE]` review +- Outputs a markdown report with per-file sections and classification suggestions +- Exit code: 0 if no changes, non-zero if changes (CI can use to flag annual upgrades) + +### 6.5 Pinning and Rollback + +- Git: `main-v19`, `main-v20`, etc. branches in fusion repo. Each client stays on their pinned Odoo version +- Manifest version pinned per sub-module per Odoo version +- Client deployment: never auto-upgrade. Upgrade is a deliberate, tested, per-client migration +- Rollback: restore DB from `pg_dump` taken before upgrade, restore `fusion_accounting_*` checkout from git tag, restart Odoo + +### 6.6 Cross-Version Migration Scripts + +Odoo's standard migration mechanism applies. Each sub-module has a `migrations/` folder with subfolders named after manifest versions. Scripts run automatically when the manifest version bumps in the database vs. on disk. + +```python +# fusion_accounting_assets/migrations/20.0.0.0.0/pre-migration.py +def migrate(cr, version): + # V20 renamed fusion_asset.original_value to fusion_asset.acquisition_cost + cr.execute("ALTER TABLE fusion_asset RENAME COLUMN original_value TO acquisition_cost") +``` + +--- + +## 7. AI Integration, Testing, Documentation + +### 7.1 AI Integration + +The AI copilot (existing `fusion_accounting/services/`, `fusion_accounting/static/src/`, `fusion_accounting/controllers/` etc.) moves to `fusion_accounting_ai/` in Phase 0 and stays original code. What changes: + +**Adapter pattern**: every AI tool that today calls Enterprise APIs gets routed through an adapter: + +``` +fusion_accounting_ai/services/adapters/ +├── bank_rec_adapter.py +├── reports_adapter.py +├── followup_adapter.py +├── assets_adapter.py +└── _registry.py +``` + +Adapter behavior (uniform pattern across all adapters): + +```python +class BankRecAdapter: + def __init__(self, env): + self.env = env + + def list_unreconciled_lines(self, journal_id, limit=100): + # Prefer fusion native if installed + if 'fusion.bank.rec.widget' in self.env.registry: + return self.env['fusion.bank.rec.widget'].sudo().get_unreconciled(journal_id, limit) + # Fall back to Enterprise if installed + elif self.env['ir.module.module']._fusion_is_module_installed('account_accountant'): + return self._enterprise_unreconciled_lines(journal_id, limit) + # Last resort: pure Community search + else: + return self.env['account.bank.statement.line'].sudo().search([ + ('journal_id', '=', journal_id), + ('is_reconciled', '=', False), + ], limit=limit) +``` + +This pattern means `fusion_accounting_ai` always works, regardless of which other modules are installed. The AI tool functions in `fusion_accounting_ai/services/tools/` get refactored once in Phase 0 to call adapters; subsequent phases just enrich the adapters. + +**New AI capabilities unlocked by native implementations**: each native phase exposes engine internals to AI tools that Enterprise didn't expose cleanly. Examples: + +- Phase 1: AI gets access to fusion's match-confidence scores +- Phase 2: AI can request a report computation with custom comparison periods on the fly +- Phase 4: AI has direct access to tax-grid-by-account decomposition +- Phase 5: AI drafts follow-up messages with full payment history context + +**Existing AI patterns carry forward unchanged**: + +- Tool tiering (Tier 1 / 2 / 3 with auto-promotion) +- Provider pinning per session (Claude vs OpenAI consistency within a session) +- Tier 3 approval flow with `pending_approval` placeholder swap on approve/reject +- Rich-text chat output via `mdToHtml()` and `innerHTML` injection +- Interactive `fusion-table` blocks for actionable results +- Session ownership / multi-company record rules (the `fusion.accounting.session` rule that's currently missing gets added in Phase 0) + +### 7.2 Testing Strategy + +Every phase must pass these test categories before exit: + +| Category | Scope | Where it lives | +|---|---|---| +| **Unit (engine)** | Pure-Python; no Odoo DB. Algorithm correctness with fixtures | `tests/test__engine.py` | +| **Integration (Odoo TestCase)** | Full Odoo DB; install + create data + exercise workflow + assert state | `tests/test__integration.py` | +| **Migration round-trip** | Install Enterprise, create Enterprise-only data, install fusion, run wizard, uninstall Enterprise, assert data integrity | `tests/test_migration.py` | +| **Tour (JS)** | End-to-end widget UI smoke | `tests/tours/_tour.js` | +| **Performance** | Phase 2 reports especially; assert P95 latency at 1k/10k/100k rows | `tests/test__performance.py` | +| **Multi-matrix** | Single-company, multi-company, multi-currency, cash-basis on/off | parameterized within other tests | + +CI runs all tests on every push. A nightly job runs migration tests against a fixture Enterprise DB. + +### 7.3 Documentation Deliverables + +Per sub-module: + +- `CLAUDE.md` — module-specific context for Cursor/AI assistance +- `UPGRADE_NOTES.md` — Odoo version porting log +- `README.md` — operator-facing: install, configure, troubleshoot, common gotchas +- One screencast or animated GIF per major user workflow, in `static/description/` +- Per-feature feature flag documentation in `CLAUDE.md` if applicable + +Workspace-root documentation: + +- `/Users/gurpreet/Github/Odoo-Modules/CLAUDE.md` — common Odoo 19 conventions (already substantial; carries forward) +- `/Users/gurpreet/Github/Odoo-Modules/fusion_accounting/CLAUDE.md` — meta-module overview pointing at sub-modules +- `/Users/gurpreet/Github/Odoo-Modules/fusion_accounting/docs/superpowers/specs/` — design and plan docs (this doc and future ones) + +### 7.4 Security + +- Three groups carry forward from existing module: `group_fusion_accounting_user/manager/admin`. Move from current location to `fusion_accounting_core/security/security.xml` in Phase 0 +- Auto-assignments from Community accounting groups: `account.group_account_user` → fusion User; `account.group_account_manager` → fusion Admin (already in place) +- Multi-company record rules on every fusion model with `company_id`. Add the missing rule on `fusion.accounting.session` in Phase 0 +- ACLs in `security/ir.model.access.csv` per sub-module, scoped to that sub-module's models only +- Approve/reject endpoints continue to use `auth='user'` with imperative `has_group()` check inside the handler (Odoo has no built-in `auth='manager'`) + +### 7.5 Performance Considerations (Phase 2 in particular) + +Odoo Enterprise reports have known performance issues on large databases. The Phase 2 design doc must lock in: + +- Denormalized read paths for trial balance and general ledger (materialized aggregations refreshed on `account.move` post) +- Lazy-load line detail (drill-down fetches separately, not all at once) +- Cache report runs per `(company_id, period, filter_hash)` with invalidation on `account.move.line` write/post/cancel +- Parallel computation across companies in multi-company reports +- SQL query review (no Python aggregation of large result sets) + +### 7.6 Multi-Company, Multi-Currency, Analytic + +Not a separate phase. Woven into every phase's exit criteria: + +- Every fusion model with company-scoped data has `company_id` field and a multi-company record rule +- Every monetary field pairs with `currency_id` +- `analytic_mixin` (currently in `account_accountant/models/analytic_mixin.py`): declared in `fusion_accounting_core` via shared-field-ownership pattern so analytic tags survive Enterprise uninstall + +### 7.7 Localization + +Canadian HST is built into the existing AI module (`fusion_accounting_ai/services/prompts/domain_prompts.py`) and carries forward. Other localizations are deferred: + +- Each country-specific tax report becomes a `fusion.account.report` record in `fusion_accounting_reports/data/_.xml` +- Country-specific chart of accounts: continue to use Odoo's `account.chart.template` mechanism (Community) +- New countries are added on demand, per client engagement + +### 7.8 Hosting and Deployment + +Out of scope for this design doc; covered in workspace-root operational docs. fusion_accounting deploys to the existing Nexa Odoo infrastructure (per existing `fusion_accounting/CLAUDE.md`: `odoo-westin` for Westin Healthcare, equivalents for other clients). Deploy commands in CLAUDE.md carry forward. + +--- + +## 8. Acceptance Criteria for This Roadmap + +This roadmap is considered "done" (and ready for the first writing-plans session for Phase 0) when: + +- The user has reviewed this document and signed off +- No unresolved ambiguity remains in any of the locked decisions (sub-module topology, data preservation, phase order, architecture rules, upgrade workflow) +- The empirical verification test (Section 3.6) is scheduled as part of Phase 0 and not deferred + +The next session's deliverable will be the Phase 0 implementation plan (via the `writing-plans` skill), which will turn Section 4.2 into actionable, testable tasks. + +--- + +## 9. Open Questions Deferred to Future Sessions + +Items consciously left open here, to be resolved in their respective phase brainstorming sessions: + +- Phase 1: exact UI deltas from Odoo's bank rec widget (colour palette, AI confidence badge placement, keyboard shortcuts) +- Phase 2: report definition data format (XML mirroring Odoo vs. our own simpler format) +- Phase 2: caching layer implementation (in-memory vs. Redis vs. PostgreSQL materialized views) +- Phase 4: which non-Canadian tax jurisdictions to seed +- Phase 5: SMS provider integration (Twilio? `mail.sms` Odoo built-in?) +- Phase 6: depreciation methods to support beyond linear/declining (sum-of-years-digits, units-of-production) +- Phase 7+: which satellites have actual client demand right now + +--- + +## 10. References + +- Workspace root: `/Users/gurpreet/Github/Odoo-Modules/` +- Current AI module: `/Users/gurpreet/Github/Odoo-Modules/fusion_accounting/` +- Current AI module conventions: `/Users/gurpreet/Github/Odoo-Modules/fusion_accounting/CLAUDE.md` +- Workspace conventions: `/Users/gurpreet/Github/Odoo-Modules/CLAUDE.md` +- WIP code (not continued): `/Users/gurpreet/Github/Odoo-Modules/Work in Progress/fusion_accounting/` +- WIP audit report: `/Users/gurpreet/Github/Odoo-Modules/Work in Progress/fusion_accounting/AUDIT_REPORT.md` +- Pinned Odoo source: `/Users/gurpreet/Github/RePackaged-Odoo/accounting/` +- Plan file (this session): `/Users/gurpreet/.cursor/plans/fusion_accounting_takeover_roadmap_c851fdb4.plan.md`