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
This commit is contained in:
@@ -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<br/>Odoo Community base"]
|
||||
|
||||
core["fusion_accounting_core<br/>shared fields, lock dates, fiscal year base,<br/>company config, security groups, analytic_mixin"]
|
||||
bankrec["fusion_accounting_bank_rec<br/>reconcile widget + auto-reconcile engine"]
|
||||
reports["fusion_accounting_reports<br/>financial reports engine + standard reports"]
|
||||
dashboard["fusion_accounting_dashboard<br/>journal kanban, digest"]
|
||||
followup["fusion_accounting_followup<br/>payment reminders"]
|
||||
assets["fusion_accounting_assets<br/>asset register, depreciation"]
|
||||
budget["fusion_accounting_budget<br/>budgets vs actuals"]
|
||||
ai["fusion_accounting_ai<br/>Claude/GPT copilot + chat + dashboard tiles<br/>(current fusion_accounting code lives here)"]
|
||||
migration["fusion_accounting_migration<br/>transitional Enterprise to fusion data wizard"]
|
||||
|
||||
meta["fusion_accounting<br/>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_<table> SELECT ... FROM <enterprise_table>` 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.<module>.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<br/>while Enterprise still running"]
|
||||
step1 --> step2["fusion_accounting_core declares shared fields<br/>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<br/>into fusion tables"]
|
||||
step6 --> step7["Wizard generates migration report"]
|
||||
step7 --> step8["Operator reviews report"]
|
||||
step8 --> step9["Operator triggers Enterprise uninstall in dep-safe order:<br/>account_reports → account_followup → account_asset →<br/>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<br/>Bank recs intact, deferred links preserved,<br/>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 | `<record id="fusion_view_bank_rec_form">` |
|
||||
| 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_<feature>/
|
||||
├── __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_<feature>_engine.py # abstract zone: core algorithm
|
||||
│ ├── account_<x>.py # mirror zone: inherits Community model
|
||||
│ └── fusion_<y>.py # mirror zone: our own models
|
||||
├── report/
|
||||
├── security/
|
||||
│ ├── ir.model.access.csv
|
||||
│ └── <feature>_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_<feature>_engine.py # abstract zone unit tests
|
||||
│ ├── test_<feature>_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 `<search>` or `<group>`; group-by filters need `domain="[]"`; `<separator/>` before `<group>`
|
||||
- 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: `<sub-module>/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<N+1> ships:
|
||||
|
||||
1. Add the snapshot folder
|
||||
2. For each fusion sub-module:
|
||||
- Run `tools/check_odoo_diff.sh <enterprise_module> v<N> v<N+1> > reports/v<N+1>_<module>_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/<N+1>.0.0.0.0/` for any data-shape changes
|
||||
- Update `UPGRADE_NOTES.md`
|
||||
- Run all tests
|
||||
3. Tag releases on `main-v<N+1>` 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 <enterprise_module> <from_version> <to_version> [<output_file>]
|
||||
```
|
||||
|
||||
Behavior:
|
||||
|
||||
- Runs `diff -ruN /Users/gurpreet/Github/RePackaged-Odoo/accounting-<from>/<module> /Users/gurpreet/Github/RePackaged-Odoo/accounting-<to>/<module>`
|
||||
- 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_<feature>_engine.py` |
|
||||
| **Integration (Odoo TestCase)** | Full Odoo DB; install + create data + exercise workflow + assert state | `tests/test_<feature>_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/<feature>_tour.js` |
|
||||
| **Performance** | Phase 2 reports especially; assert P95 latency at 1k/10k/100k rows | `tests/test_<feature>_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/<country>_<report>.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`
|
||||
Reference in New Issue
Block a user