From 51b26838b92cf27f64baf07776e46d6f4d0afc57 Mon Sep 17 00:00:00 2001 From: gsinghpal Date: Sun, 19 Apr 2026 01:09:42 -0400 Subject: [PATCH] docs(fusion_accounting): per-sub-module CLAUDE.md, UPGRADE_NOTES.md, README.md Task 20 of Phase 0: document the sub-module split. - fusion_accounting_core: foundation doc covering security groups, shared-field schema preservation, and the Enterprise-detection helper. - fusion_accounting_ai: preserves the original module's AI-specific design decisions, Odoo 19 gotchas, deployment commands, controllers, models, theme rules, and known issues. Adds a new Data-adapter pattern section documenting tri-mode routing (fusion / enterprise / community). - fusion_accounting_migration: doc for the Enterprise uninstall safety guard and the wizard shell that future feature sub-modules will extend. - fusion_accounting (meta): rewritten CLAUDE.md as a pure overview pointing at sub-modules, plus a new README.md covering one-click install/uninstall. Each sub-module now has CLAUDE.md (Cursor/Claude context), UPGRADE_NOTES.md (version-by-version deltas / reference sources), and README.md (user-facing install/usage docs). 11 files total. Made-with: Cursor --- fusion_accounting/CLAUDE.md | 270 +++--------------- fusion_accounting/README.md | 38 +++ fusion_accounting_ai/CLAUDE.md | 272 +++++++++++++++++++ fusion_accounting_ai/README.md | 31 +++ fusion_accounting_ai/UPGRADE_NOTES.md | 22 ++ fusion_accounting_core/CLAUDE.md | 25 ++ fusion_accounting_core/README.md | 39 +++ fusion_accounting_core/UPGRADE_NOTES.md | 28 ++ fusion_accounting_migration/CLAUDE.md | 20 ++ fusion_accounting_migration/README.md | 22 ++ fusion_accounting_migration/UPGRADE_NOTES.md | 10 + 11 files changed, 541 insertions(+), 236 deletions(-) create mode 100644 fusion_accounting/README.md create mode 100644 fusion_accounting_ai/CLAUDE.md create mode 100644 fusion_accounting_ai/README.md create mode 100644 fusion_accounting_ai/UPGRADE_NOTES.md create mode 100644 fusion_accounting_core/CLAUDE.md create mode 100644 fusion_accounting_core/README.md create mode 100644 fusion_accounting_core/UPGRADE_NOTES.md create mode 100644 fusion_accounting_migration/CLAUDE.md create mode 100644 fusion_accounting_migration/README.md create mode 100644 fusion_accounting_migration/UPGRADE_NOTES.md diff --git a/fusion_accounting/CLAUDE.md b/fusion_accounting/CLAUDE.md index 04826be6..8b2834c7 100644 --- a/fusion_accounting/CLAUDE.md +++ b/fusion_accounting/CLAUDE.md @@ -1,248 +1,46 @@ -# fusion_accounting — AI Accounting Co-Pilot +# fusion_accounting (meta-module) — Cursor / Claude Context -## What This Module Does -An AI agent (Claude/GPT with tool-calling) embedded in Odoo 19 Enterprise Accounting. Conversational interface backed by a dashboard for bank reconciliation, HST/GST management, AR/AP analysis, journal review, month-end close, payroll, inventory, ADP reconciliation, financial reporting, and auditing. +## Purpose -## Architecture -``` -fusion_accounting/ -├── models/ 7 files (5 new models + 2 inherits: account.move, res.config.settings) -├── services/ -│ ├── agent.py AI orchestrator (prompt assembly, tool dispatch loop) -│ ├── adapters/ Claude + OpenAI adapters with native tool-calling -│ ├── tools/ 93 tool functions across 11 domain files -│ ├── prompts/ System prompt builder + 12 domain-specific prompts -│ └── scoring.py Confidence scoring + tier promotion logic -├── controllers/ 10 JSON-RPC endpoints -├── wizards/ Rule creation wizard -├── static/src/ OWL dashboard + chat panel + approval cards -├── views/ List/form/search views, menus, settings -├── security/ 3 groups (User/Manager/Admin), record rules, ACLs -├── data/ 88 tool definitions, 2 default rules, 2 crons, 1 sequence -├── tests/ API integration tests -└── report/ Audit report QWeb template -``` +Meta-module that installs the entire Fusion Accounting sub-module suite with +one click. Owns no Python, JS, XML data, or views of its own. Just a manifest +that depends on the sub-modules. -## Key Design Decisions +## Sub-modules (current) -### AI Provider Integration -- Uses `fusion.api.service` (from fusion_api module) for API key resolution with fallback to `ir.config_parameter` — NO hard dependency on fusion_api -- Claude adapter: native `tool_use` blocks, extended thinking enabled (8K budget) for all Claude 4.x models -- OpenAI adapter: Chat Completions API with o-series reasoning model support (`developer` role, `max_completion_tokens`, `reasoning_effort`) -- API keys stored in `ir.config_parameter` with `fusion_accounting.` prefix -- API key fields in Settings use `password="True"` widget — labels include "(Fusion AI)" suffix to avoid conflicts with other modules' key fields -- **Provider pinning**: Sessions remember which provider was used. If the global provider changes mid-session, the session continues with its original provider to prevent cross-adapter message format contamination. - -### Tool Tiering -- **Tier 1** (Free): Read-only, execute immediately — 60+ tools -- **Tier 2** (Auto-approved): Low-risk writes, logged — ~10 tools -- **Tier 3** (Requires approval): Financial writes, user must approve — ~15 tools -- Auto-promotion: Tier 3 → Tier 2 at 95% accuracy over 30+ decisions (atomic SQL counters on `fusion.accounting.rule._record_decision`) -- Tool descriptions include tier labels (e.g., `[Tier 3: Requires user approval]`) so the AI knows which tools need approval -- When a Tier 3 tool is encountered during the chat loop, the loop short-circuits: a final text response is forced so the AI can present approval cards to the user - -### Tier 3 Approval Flow -- When a Tier 3 action is approved/rejected, the session's `message_ids_json` is updated to replace the `pending_approval` placeholder with the actual tool result — this prevents dangling `tool_use` blocks that would cause API errors on the next chat turn -- After approval, `scoring.check_promotions()` is called to check if any rules should be promoted - -### Menu Location -- **Parent**: `accountant.menu_accounting` (NOT `account.menu_finance` — that's Community Edition only) -- Enterprise uses `accountant.menu_accounting` (ID 1663) as the visible menu root -- `account.menu_finance` (ID 180) exists but has NO visible children in Enterprise — it's the Community root - -### Session Persistence -- Chat sessions stored in `fusion.accounting.session` with `message_ids_json` (JSON text field) -- On page load, chat panel calls `/session/latest` to restore the most recent active session -- Empty assistant messages (tool-call-only responses with no text) are filtered out by the controller -- "New Chat" button closes current session and creates a fresh one -- Session name (e.g., FAS/2026/00001) shown in the chat header -- **Session ownership**: Controllers verify the current user owns the session (managers can access any session) - -### Rich Text Chat Output -- AI responses are rendered as rich HTML, not plain text -- Markdown-to-HTML conversion happens client-side in `chat_panel.js` via `mdToHtml()` function -- HTML is injected via `innerHTML` on `onMounted` + `onPatched` (NOT via OWL's `markup()` / `t-out` — those proved unreliable in Odoo 19) -- The `_renderRichMessages()` method finds `.fusion_rich_slot[data-idx]` divs and sets their innerHTML -- Supported: headers (# through #####), **bold**, *italic*, `code`, tables, bullet/numbered lists, horizontal rules, [links](url) -- System prompt instructs AI to use markdown formatting and include Odoo record links like `[INV/2026/00123](/odoo/accounting/123)` - -### Interactive Tables (fusion-table) -- AI can return `fusion-table` fenced code blocks instead of Markdown tables for actionable results -- `mdToHtml()` detects these blocks, extracts JSON, and renders `FusionInteractiveTable` OWL components via `mount()` -- **Interactive mode**: checkbox column + data columns + AI Recommendation column (colour-coded badge) + Your Input column (text field per row) + bottom bulk action bar -- **Read-only mode**: styled table, no inputs/actions -- Actions: Apply Recommendations, Flag Selected, Create Rules, Dismiss Selected, Submit All Notes to AI -- Action button clicks format a `[TABLE_ACTION]` structured message and send it back through the chat endpoint -- The AI decides per-response whether to use interactive or Markdown tables based on whether the data is actionable -- Used for: `find_missing_itc_bills`, `find_duplicate_bills`, `get_overdue_invoices`, `find_draft_entries`, `get_unreconciled_bank_lines`, etc. -- NOT used for: `get_profit_loss`, `get_balance_sheet`, `get_trial_balance` (informational, read-only) -- All styles use Odoo CSS variables — dark/light mode handled automatically - -### Dashboard Layout -- Health cards row at top (6 cards: Bank Recon, AR, AP, HST, Audit Score, Month-End) -- Below: side-by-side layout — "Needs Attention" panel (flex-grow) + Chat panel (720px fixed width) -- Chat panel is 720px (80% larger than original 400px design) -- Dashboard endpoint returns `needs_attention` and `recent_activity` JSON arrays alongside health card metrics - -## Odoo 19 Gotchas (Learned the Hard Way) - -### Search Views -- NO `string` attribute on `` element -- NO `string` attribute on `` element inside search views -- Group-by filters MUST have `domain="[]"` attribute -- Add `` before `` in search views - -### OWL Client Actions -- Components registered as client actions receive props: `action`, `actionId`, `updateActionState`, `className` -- Must use `static props = ["*"]` (accept any) — NOT `static props = []` (accept none) - -### OWL Rich HTML Rendering -- `markup()` from `@odoo/owl` + `t-out` is UNRELIABLE in Odoo 19 for rendering HTML in OWL components -- Use `onMounted` + `onPatched` hooks to find DOM elements and set `innerHTML` directly -- Pattern: render a placeholder `
`, then in the hook find it and set `.innerHTML` -- Always use BOTH `onMounted` AND `onPatched` — `onPatched` alone misses the first render - -### Cron Safe Eval -- NO `import` statements (forbidden opcode `IMPORT_NAME`) -- `datetime` module available as `datetime` (use `datetime.datetime.now()`, `datetime.timedelta()`) -- NO `from datetime import X` pattern - -### read_group Deprecated -- `read_group()` is deprecated in Odoo 19 — use `_read_group()` instead -- Still works but throws DeprecationWarning -- Dashboard `accounting_dashboard.py` still uses `read_group()` — migrate to `_read_group()` when the new API is stable - -### Config Parameter Values -- When changing a Selection field's options, the stored DB value in `ir_config_parameter` must match one of the new options or Settings page will crash with `ValueError: Wrong value` -- Fix: UPDATE the value in DB after changing selection options: - ```sql - UPDATE ir_config_parameter SET value = 'new_value' WHERE key = 'fusion_accounting.field_name'; - ``` - -### Field Label Conflicts -- Odoo warns if two fields on the same model have the same `string` label -- Our `display_name_field` conflicted with built-in `display_name` — renamed string to "Tool Label" -- API key fields use "(Fusion AI)" suffix to avoid label conflicts with other modules -- Tool model uses `domain` (not `domain_name`) and `parameters_schema` (not `parameters`) as field names - -### Group Assignment -- `implied_ids` on groups only applies to NEWLY added users, not existing ones -- After installing, manually add existing users to groups via SQL: - ```sql - INSERT INTO res_groups_users_rel (gid, uid) - SELECT , gu.uid FROM res_groups_users_rel gu - JOIN ir_model_data imd ON imd.res_id = gu.gid AND imd.model = 'res.groups' - WHERE imd.module = 'account' AND imd.name = 'group_account_manager' - ON CONFLICT DO NOTHING; - ``` - -### TransientModel in Controllers -- Use `.new({...})` NOT `.create({...})` for TransientModels in controller endpoints -- `.create()` writes a DB row on every request; `.new()` is in-memory only -- Dashboard controller uses `.new()` to compute health metrics without DB writes - -## Server Details -- **Server**: odoo-westin (192.168.1.40, SSH via `ssh odoo-westin`) -- **Container**: odoo-dev-app (Odoo), odoo-dev-db (PostgreSQL) -- **Database**: westin-v19 -- **Module path**: `/mnt/extra-addons/fusion_accounting/` -- **Python deps**: anthropic (v0.88.0), openai (v2.30.0) — installed with `--break-system-packages` -- **URL**: erp.westinhealthcare.ca - -## Deployment Commands -```bash -# Full deploy cycle (clean + copy + upgrade + restart) -ssh odoo-westin "docker exec -u 0 odoo-dev-app rm -rf /mnt/extra-addons/fusion_accounting" -scp -r "K:\Github\Odoo-Modules\fusion_accounting" odoo-westin:/tmp/fusion_accounting -ssh odoo-westin "docker cp /tmp/fusion_accounting odoo-dev-app:/mnt/extra-addons/fusion_accounting && rm -rf /tmp/fusion_accounting" -ssh odoo-westin "docker exec odoo-dev-app odoo -d westin-v19 -u fusion_accounting --stop-after-init --http-port=8099 -c /etc/odoo/odoo.conf" -ssh odoo-westin "docker restart odoo-dev-app" - -# Check logs -ssh odoo-westin "docker logs odoo-dev-app --tail 100" - -# Quick DB queries -ssh odoo-westin "docker exec odoo-dev-db psql -U odoo -d westin-v19 -t -c \"\"" - -# Check module state -ssh odoo-westin "docker exec odoo-dev-db psql -U odoo -d westin-v19 -t -c \"SELECT name, state, latest_version FROM ir_module_module WHERE name = 'fusion_accounting';\"" -``` - -## Security Groups -| Group ID | XML ID | Name | Access | -|---|---|---|---| -| 564 | `group_fusion_accounting_user` | User | Dashboard, chat (read-only tools) | -| 565 | `group_fusion_accounting_manager` | Manager | + Approve/reject, Tier 2 tools, rules | -| 566 | `group_fusion_accounting_admin` | Administrator | + Config, all tools, rule admin | - -Auto-assigned: `account.group_account_user` → User, `account.group_account_manager` → Admin - -## Controller Endpoints -| Route | Auth | Purpose | +| Sub-module | Phase | Purpose | |---|---|---| -| `/fusion_accounting/session/create` | user | Create new chat session | -| `/fusion_accounting/session/close` | user (ownership check) | Close active session | -| `/fusion_accounting/session/latest` | user (own sessions only) | Load most recent active session + messages | -| `/fusion_accounting/session/history` | user (ownership check, managers see all) | Load specific session messages | -| `/fusion_accounting/chat` | user (ownership check) | Send message, get AI response | -| `/fusion_accounting/approve` | user + manager group check | Approve single Tier 3 action | -| `/fusion_accounting/reject` | user + manager group check | Reject single Tier 3 action | -| `/fusion_accounting/approve_all` | user + manager group check | Batch approve multiple actions | -| `/fusion_accounting/reject_all` | user + manager group check | Batch reject multiple actions | -| `/fusion_accounting/dashboard/data` | user | Get dashboard health card metrics + needs_attention + recent_activity | +| `fusion_accounting_core` | 0 | Security groups, shared schema, Enterprise detection helper | +| `fusion_accounting_ai` | 0 | AI Co-Pilot (Claude/GPT) — was the original `fusion_accounting` code | +| `fusion_accounting_migration` | 0 | Transitional Enterprise->Fusion data migration | -Note: Approve/reject endpoints use `auth='user'` at the decorator level with an imperative `has_group()` check inside the handler (Odoo has no built-in `auth='manager'`). +## Sub-modules (planned) -## Models -| Model | Type | Location | Purpose | -|---|---|---|---| -| `fusion.accounting.session` | Model | models/ | Chat sessions with message JSON storage | -| `fusion.accounting.match.history` | Model | models/ | Every AI tool call + decision (approved/rejected/pending) | -| `fusion.accounting.rule` | Model | models/ | Fusion Rules engine with versioning and auto-promotion | -| `fusion.accounting.tool` | Model | models/ | Tool registry (82 tools seeded from XML) | -| `fusion.accounting.dashboard` | TransientModel | models/ | Computed health metrics (use `.new()` not `.create()`) | -| `res.config.settings` (inherit) | TransientModel | models/ | Settings page (API keys, thresholds, toggles) | -| `account.move` (inherit) | Model | models/ | Post-action audit hook | -| `fusion.accounting.agent` | AbstractModel | services/ | AI orchestrator | -| `fusion.accounting.adapter.claude` | AbstractModel | services/ | Claude tool-calling adapter | -| `fusion.accounting.adapter.openai` | AbstractModel | services/ | OpenAI tool-calling adapter | -| `fusion.accounting.scoring` | AbstractModel | services/ | Confidence scoring | -| `fusion.accounting.rule.wizard` | TransientModel | wizards/ | Quick-create rule from chat suggestion | +Per the roadmap design at `docs/superpowers/specs/2026-04-18-fusion-accounting-enterprise-takeover-roadmap-design.md`: -## AI Models Available -**Claude** (default: claude-sonnet-4-6): -- claude-opus-4-6, claude-sonnet-4-6, claude-haiku-4-5 -- claude-sonnet-4-5, claude-opus-4-5, claude-sonnet-4-0, claude-opus-4-0 +| Sub-module | Phase | Purpose | +|---|---|---| +| `fusion_accounting_bank_rec` | 1 | Native bank reconciliation (replaces account_accountant bank rec) | +| `fusion_accounting_reports` | 2 | Native financial reports engine (replaces account_reports) | +| `fusion_accounting_dashboard` | 3 | Journal kanban + digest | +| `fusion_accounting_followup` | 5 | Customer payment follow-ups | +| `fusion_accounting_assets` | 6 | Asset register + depreciation | +| `fusion_accounting_budget` | 6 | Budget vs actual | -**OpenAI** (default: gpt-5.4-mini): -- gpt-5.4, gpt-5.4-mini, gpt-5.4-nano -- o3, o4-mini -- gpt-4o, gpt-4o-mini (legacy) +## Roadmap and plans -## Theme / Styling Rules -- NO hardcoded colours — use CSS variables (`var(--o-border-color)`, `var(--bs-body-color-rgb)`) and Bootstrap utility classes -- Must work in both light and dark mode -- Box shadows: use `rgba(var(--bs-body-color-rgb), 0.1)` not `rgba(0,0,0,0.1)` -- AI messages use `var(--o-view-background-color)` background + `var(--o-border-color)` border -- Links use `var(--o-action-color)` for theme awareness +- Roadmap design: `docs/superpowers/specs/2026-04-18-fusion-accounting-enterprise-takeover-roadmap-design.md` +- Phase 0 plan: `docs/superpowers/plans/2026-04-18-phase-0-foundation-plan.md` +- Empirical uninstall test results: `docs/superpowers/specs/2026-04-18-empirical-uninstall-test-results.md` (produced in Task 18 of Phase 0) -### HST Filing Workflow (4-Phase AI-Driven) -- Phase 1: AI runs all HST reports (tax report, missing ITCs, compliance audit, HST balance) -- Phase 2: AI sweeps ALL bank accounts for unreconciled expense payments -- Phase 3: Per-line processing — check for existing bills, check history for coding patterns, ask about HST, create bills, register payments -- Phase 4: Re-run reports to verify updated HST position -- New tools added: `search_partners` (Tier 1), `find_similar_bank_lines` (Tier 1), `get_bank_line_details` (Tier 1), `create_vendor_bill` (Tier 3), `register_bill_payment` (Tier 3), `create_expense_entry` (Tier 3) -- Two paths for recording expenses: (a) formal vendor bill + payment, or (b) direct GL entry in MISC journal with optional HST split -- The `create_expense_entry` tool posts directly to the Miscellaneous Operations journal — debit expense + debit HST ITC (2006) + credit bank -- Domain prompt (`hst_management` in domain_prompts.py) includes bank journal IDs and the full 4-phase workflow instructions +## Tooling -## Known Issues / Future Work -- `read_group()` deprecation warnings in `accounting_dashboard.py` — migrate to `_read_group()` when the new API format is stable -- `generate_t4`, `generate_roe` are stubs pointing to fusion_payroll (by design — Phase 2) -- `get_payroll_schedule`, `verify_source_deductions`, `verify_payroll_deductions` are stubs (Phase 2 — fusion_payroll integration) -- `answer_financial_question` is a stub (returns message to use other tools instead) -- Batch approval "Approve All" / "Reject All" buttons are in the chat panel but not yet in the match history list view -- "Needs Attention" panel shows placeholder text in the dashboard — the data is computed and returned by the API but the frontend rendering needs to be connected -- Consider switching OpenAI adapter from Chat Completions API to Responses API for better tool handling with newer models -- `o1` model does not support tool calling — no guard in place (o3/o4-mini do support it) -- Multi-company record rule missing on `fusion.accounting.session` — add if multi-company usage is needed +- `tools/check_odoo_diff.sh` — annual upgrade ritual: diff Enterprise source between Odoo versions + +## Per-sub-module CLAUDE.md + +Each sub-module has its own `CLAUDE.md` with feature-specific context. Read them when working on that sub-module. + +## Workspace-wide conventions + +`/Users/gurpreet/Github/Odoo-Modules/CLAUDE.md` — common Odoo 19 rules (search views, OWL components, SCSS, asset bundle cache busting, dark mode, etc.). Apply to every sub-module. diff --git a/fusion_accounting/README.md b/fusion_accounting/README.md new file mode 100644 index 00000000..ff69a1de --- /dev/null +++ b/fusion_accounting/README.md @@ -0,0 +1,38 @@ +# Fusion Accounting (meta-module) + +One-click install of the entire Fusion Accounting suite for Odoo 19. + +## What it installs + +- AI Co-Pilot for accounting (Claude / GPT) +- Native foundation (security, schema preservation) +- Transitional Enterprise -> Fusion migration helper + +As later sub-modules ship (bank rec, reports, follow-ups, assets, budgets), +they're added to the meta-module's `depends` and installed automatically when +the client upgrades fusion_accounting. + +## Install + + docker exec odoo-dev-app odoo -d -i fusion_accounting --stop-after-init + +## Uninstall + +Uninstalling the meta-module does NOT uninstall its sub-modules (Odoo +behavior). To fully remove Fusion Accounting: + + docker exec odoo-dev-app odoo-shell -d --no-http <_via_fusion`, `_via_enterprise`, `_via_community` +- Adapter `_select_mode()` picks fusion if model loaded, else enterprise if module installed, else community + +--- + +## Architecture +``` +fusion_accounting_ai/ +├── models/ 7 files (5 new models + 2 inherits: account.move, res.config.settings) +├── services/ +│ ├── agent.py AI orchestrator (prompt assembly, tool dispatch loop) +│ ├── adapters/ Claude + OpenAI adapters with native tool-calling +│ ├── data_adapters/ Tri-mode domain routers (fusion / enterprise / community) +│ ├── tools/ 93 tool functions across 11 domain files +│ ├── prompts/ System prompt builder + 12 domain-specific prompts +│ └── scoring.py Confidence scoring + tier promotion logic +├── controllers/ 10 JSON-RPC endpoints +├── wizards/ Rule creation wizard +├── static/src/ OWL dashboard + chat panel + approval cards +├── views/ List/form/search views, menus, settings +├── security/ ACLs + record rules (groups themselves live in fusion_accounting_core) +├── data/ 88 tool definitions, 2 default rules, 2 crons, 1 sequence +├── tests/ API integration tests +└── report/ Audit report QWeb template +``` + +## Key Design Decisions + +### AI Provider Integration +- Uses `fusion.api.service` (from fusion_api module) for API key resolution with fallback to `ir.config_parameter` — NO hard dependency on fusion_api +- Claude adapter: native `tool_use` blocks, extended thinking enabled (8K budget) for all Claude 4.x models +- OpenAI adapter: Chat Completions API with o-series reasoning model support (`developer` role, `max_completion_tokens`, `reasoning_effort`) +- API keys stored in `ir.config_parameter` with `fusion_accounting.` prefix +- API key fields in Settings use `password="True"` widget — labels include "(Fusion AI)" suffix to avoid conflicts with other modules' key fields +- **Provider pinning**: Sessions remember which provider was used. If the global provider changes mid-session, the session continues with its original provider to prevent cross-adapter message format contamination. + +### Tool Tiering +- **Tier 1** (Free): Read-only, execute immediately — 60+ tools +- **Tier 2** (Auto-approved): Low-risk writes, logged — ~10 tools +- **Tier 3** (Requires approval): Financial writes, user must approve — ~15 tools +- Auto-promotion: Tier 3 → Tier 2 at 95% accuracy over 30+ decisions (atomic SQL counters on `fusion.accounting.rule._record_decision`) +- Tool descriptions include tier labels (e.g., `[Tier 3: Requires user approval]`) so the AI knows which tools need approval +- When a Tier 3 tool is encountered during the chat loop, the loop short-circuits: a final text response is forced so the AI can present approval cards to the user + +### Tier 3 Approval Flow +- When a Tier 3 action is approved/rejected, the session's `message_ids_json` is updated to replace the `pending_approval` placeholder with the actual tool result — this prevents dangling `tool_use` blocks that would cause API errors on the next chat turn +- After approval, `scoring.check_promotions()` is called to check if any rules should be promoted + +### Menu Location +- **Parent**: `accountant.menu_accounting` (NOT `account.menu_finance` — that's Community Edition only) +- Enterprise uses `accountant.menu_accounting` (ID 1663) as the visible menu root +- `account.menu_finance` (ID 180) exists but has NO visible children in Enterprise — it's the Community root + +### Session Persistence +- Chat sessions stored in `fusion.accounting.session` with `message_ids_json` (JSON text field) +- On page load, chat panel calls `/session/latest` to restore the most recent active session +- Empty assistant messages (tool-call-only responses with no text) are filtered out by the controller +- "New Chat" button closes current session and creates a fresh one +- Session name (e.g., FAS/2026/00001) shown in the chat header +- **Session ownership**: Controllers verify the current user owns the session (managers can access any session) + +### Rich Text Chat Output +- AI responses are rendered as rich HTML, not plain text +- Markdown-to-HTML conversion happens client-side in `chat_panel.js` via `mdToHtml()` function +- HTML is injected via `innerHTML` on `onMounted` + `onPatched` (NOT via OWL's `markup()` / `t-out` — those proved unreliable in Odoo 19) +- The `_renderRichMessages()` method finds `.fusion_rich_slot[data-idx]` divs and sets their innerHTML +- Supported: headers (# through #####), **bold**, *italic*, `code`, tables, bullet/numbered lists, horizontal rules, [links](url) +- System prompt instructs AI to use markdown formatting and include Odoo record links like `[INV/2026/00123](/odoo/accounting/123)` + +### Interactive Tables (fusion-table) +- AI can return `fusion-table` fenced code blocks instead of Markdown tables for actionable results +- `mdToHtml()` detects these blocks, extracts JSON, and renders `FusionInteractiveTable` OWL components via `mount()` +- **Interactive mode**: checkbox column + data columns + AI Recommendation column (colour-coded badge) + Your Input column (text field per row) + bottom bulk action bar +- **Read-only mode**: styled table, no inputs/actions +- Actions: Apply Recommendations, Flag Selected, Create Rules, Dismiss Selected, Submit All Notes to AI +- Action button clicks format a `[TABLE_ACTION]` structured message and send it back through the chat endpoint +- The AI decides per-response whether to use interactive or Markdown tables based on whether the data is actionable +- Used for: `find_missing_itc_bills`, `find_duplicate_bills`, `get_overdue_invoices`, `find_draft_entries`, `get_unreconciled_bank_lines`, etc. +- NOT used for: `get_profit_loss`, `get_balance_sheet`, `get_trial_balance` (informational, read-only) +- All styles use Odoo CSS variables — dark/light mode handled automatically + +### Dashboard Layout +- Health cards row at top (6 cards: Bank Recon, AR, AP, HST, Audit Score, Month-End) +- Below: side-by-side layout — "Needs Attention" panel (flex-grow) + Chat panel (720px fixed width) +- Chat panel is 720px (80% larger than original 400px design) +- Dashboard endpoint returns `needs_attention` and `recent_activity` JSON arrays alongside health card metrics + +### HST Filing Workflow (4-Phase AI-Driven) +- Phase 1: AI runs all HST reports (tax report, missing ITCs, compliance audit, HST balance) +- Phase 2: AI sweeps ALL bank accounts for unreconciled expense payments +- Phase 3: Per-line processing — check for existing bills, check history for coding patterns, ask about HST, create bills, register payments +- Phase 4: Re-run reports to verify updated HST position +- New tools added: `search_partners` (Tier 1), `find_similar_bank_lines` (Tier 1), `get_bank_line_details` (Tier 1), `create_vendor_bill` (Tier 3), `register_bill_payment` (Tier 3), `create_expense_entry` (Tier 3) +- Two paths for recording expenses: (a) formal vendor bill + payment, or (b) direct GL entry in MISC journal with optional HST split +- The `create_expense_entry` tool posts directly to the Miscellaneous Operations journal — debit expense + debit HST ITC (2006) + credit bank +- Domain prompt (`hst_management` in domain_prompts.py) includes bank journal IDs and the full 4-phase workflow instructions + +## Odoo 19 Gotchas (Learned the Hard Way) + +### Search Views +- NO `string` attribute on `` element +- NO `string` attribute on `` element inside search views +- Group-by filters MUST have `domain="[]"` attribute +- Add `` before `` in search views + +### OWL Client Actions +- Components registered as client actions receive props: `action`, `actionId`, `updateActionState`, `className` +- Must use `static props = ["*"]` (accept any) — NOT `static props = []` (accept none) + +### OWL Rich HTML Rendering +- `markup()` from `@odoo/owl` + `t-out` is UNRELIABLE in Odoo 19 for rendering HTML in OWL components +- Use `onMounted` + `onPatched` hooks to find DOM elements and set `innerHTML` directly +- Pattern: render a placeholder `
`, then in the hook find it and set `.innerHTML` +- Always use BOTH `onMounted` AND `onPatched` — `onPatched` alone misses the first render + +### Cron Safe Eval +- NO `import` statements (forbidden opcode `IMPORT_NAME`) +- `datetime` module available as `datetime` (use `datetime.datetime.now()`, `datetime.timedelta()`) +- NO `from datetime import X` pattern + +### read_group Deprecated +- `read_group()` is deprecated in Odoo 19 — use `_read_group()` instead +- Still works but throws DeprecationWarning +- Dashboard `accounting_dashboard.py` still uses `read_group()` — migrate to `_read_group()` when the new API is stable + +### Config Parameter Values +- When changing a Selection field's options, the stored DB value in `ir_config_parameter` must match one of the new options or Settings page will crash with `ValueError: Wrong value` +- Fix: UPDATE the value in DB after changing selection options: + ```sql + UPDATE ir_config_parameter SET value = 'new_value' WHERE key = 'fusion_accounting.field_name'; + ``` + +### Field Label Conflicts +- Odoo warns if two fields on the same model have the same `string` label +- Our `display_name_field` conflicted with built-in `display_name` — renamed string to "Tool Label" +- API key fields use "(Fusion AI)" suffix to avoid label conflicts with other modules +- Tool model uses `domain` (not `domain_name`) and `parameters_schema` (not `parameters`) as field names + +### Group Assignment +- `implied_ids` on groups only applies to NEWLY added users, not existing ones +- After installing, manually add existing users to groups via SQL: + ```sql + INSERT INTO res_groups_users_rel (gid, uid) + SELECT , gu.uid FROM res_groups_users_rel gu + JOIN ir_model_data imd ON imd.res_id = gu.gid AND imd.model = 'res.groups' + WHERE imd.module = 'account' AND imd.name = 'group_account_manager' + ON CONFLICT DO NOTHING; + ``` + +### TransientModel in Controllers +- Use `.new({...})` NOT `.create({...})` for TransientModels in controller endpoints +- `.create()` writes a DB row on every request; `.new()` is in-memory only +- Dashboard controller uses `.new()` to compute health metrics without DB writes + +## Server Details +- **Server**: odoo-westin (192.168.1.40, SSH via `ssh odoo-westin`) +- **Container**: odoo-dev-app (Odoo), odoo-dev-db (PostgreSQL) +- **Database**: westin-v19 +- **Module path**: `/mnt/extra-addons/fusion_accounting_ai/` +- **Python deps**: anthropic (v0.88.0), openai (v2.30.0) — installed with `--break-system-packages` +- **URL**: erp.westinhealthcare.ca + +## Deployment Commands +```bash +# Full deploy cycle (clean + copy + upgrade + restart) +ssh odoo-westin "docker exec -u 0 odoo-dev-app rm -rf /mnt/extra-addons/fusion_accounting_ai" +scp -r "K:\Github\Odoo-Modules\fusion_accounting_ai" odoo-westin:/tmp/fusion_accounting_ai +ssh odoo-westin "docker cp /tmp/fusion_accounting_ai odoo-dev-app:/mnt/extra-addons/fusion_accounting_ai && rm -rf /tmp/fusion_accounting_ai" +ssh odoo-westin "docker exec odoo-dev-app odoo -d westin-v19 -u fusion_accounting_ai --stop-after-init --http-port=8099 -c /etc/odoo/odoo.conf" +ssh odoo-westin "docker restart odoo-dev-app" + +# Check logs +ssh odoo-westin "docker logs odoo-dev-app --tail 100" + +# Quick DB queries +ssh odoo-westin "docker exec odoo-dev-db psql -U odoo -d westin-v19 -t -c \"\"" + +# Check module state +ssh odoo-westin "docker exec odoo-dev-db psql -U odoo -d westin-v19 -t -c \"SELECT name, state, latest_version FROM ir_module_module WHERE name = 'fusion_accounting_ai';\"" +``` + +## Security Groups +(The three groups themselves are now defined in `fusion_accounting_core`. This +module's `security/ir.model.access.csv` grants access on AI-specific models +using those group XML-ids.) + +| XML ID (in fusion_accounting_core) | Name | Access in AI module | +|---|---|---| +| `group_fusion_accounting_user` | User | Dashboard, chat (read-only tools) | +| `group_fusion_accounting_manager` | Manager | + Approve/reject, Tier 2 tools, rules | +| `group_fusion_accounting_admin` | Administrator | + Config, all tools, rule admin | + +Auto-assigned (configured in _core): `account.group_account_user` → User, +`account.group_account_manager` → Admin + +## Controller Endpoints +| Route | Auth | Purpose | +|---|---|---| +| `/fusion_accounting/session/create` | user | Create new chat session | +| `/fusion_accounting/session/close` | user (ownership check) | Close active session | +| `/fusion_accounting/session/latest` | user (own sessions only) | Load most recent active session + messages | +| `/fusion_accounting/session/history` | user (ownership check, managers see all) | Load specific session messages | +| `/fusion_accounting/chat` | user (ownership check) | Send message, get AI response | +| `/fusion_accounting/approve` | user + manager group check | Approve single Tier 3 action | +| `/fusion_accounting/reject` | user + manager group check | Reject single Tier 3 action | +| `/fusion_accounting/approve_all` | user + manager group check | Batch approve multiple actions | +| `/fusion_accounting/reject_all` | user + manager group check | Batch reject multiple actions | +| `/fusion_accounting/dashboard/data` | user | Get dashboard health card metrics + needs_attention + recent_activity | + +Note: Approve/reject endpoints use `auth='user'` at the decorator level with an imperative `has_group()` check inside the handler (Odoo has no built-in `auth='manager'`). + +## Models +| Model | Type | Location | Purpose | +|---|---|---|---| +| `fusion.accounting.session` | Model | models/ | Chat sessions with message JSON storage | +| `fusion.accounting.match.history` | Model | models/ | Every AI tool call + decision (approved/rejected/pending) | +| `fusion.accounting.rule` | Model | models/ | Fusion Rules engine with versioning and auto-promotion | +| `fusion.accounting.tool` | Model | models/ | Tool registry (82 tools seeded from XML) | +| `fusion.accounting.dashboard` | TransientModel | models/ | Computed health metrics (use `.new()` not `.create()`) | +| `res.config.settings` (inherit) | TransientModel | models/ | Settings page (API keys, thresholds, toggles) | +| `account.move` (inherit) | Model | models/ | Post-action audit hook | +| `fusion.accounting.agent` | AbstractModel | services/ | AI orchestrator | +| `fusion.accounting.adapter.claude` | AbstractModel | services/ | Claude tool-calling adapter | +| `fusion.accounting.adapter.openai` | AbstractModel | services/ | OpenAI tool-calling adapter | +| `fusion.accounting.scoring` | AbstractModel | services/ | Confidence scoring | +| `fusion.accounting.rule.wizard` | TransientModel | wizards/ | Quick-create rule from chat suggestion | + +## AI Models Available +**Claude** (default: claude-sonnet-4-6): +- claude-opus-4-6, claude-sonnet-4-6, claude-haiku-4-5 +- claude-sonnet-4-5, claude-opus-4-5, claude-sonnet-4-0, claude-opus-4-0 + +**OpenAI** (default: gpt-5.4-mini): +- gpt-5.4, gpt-5.4-mini, gpt-5.4-nano +- o3, o4-mini +- gpt-4o, gpt-4o-mini (legacy) + +## Theme / Styling Rules +- NO hardcoded colours — use CSS variables (`var(--o-border-color)`, `var(--bs-body-color-rgb)`) and Bootstrap utility classes +- Must work in both light and dark mode +- Box shadows: use `rgba(var(--bs-body-color-rgb), 0.1)` not `rgba(0,0,0,0.1)` +- AI messages use `var(--o-view-background-color)` background + `var(--o-border-color)` border +- Links use `var(--o-action-color)` for theme awareness + +## Known Issues / Future Work +- `read_group()` deprecation warnings in `accounting_dashboard.py` — migrate to `_read_group()` when the new API format is stable +- `generate_t4`, `generate_roe` are stubs pointing to fusion_payroll (by design — Phase 2) +- `get_payroll_schedule`, `verify_source_deductions`, `verify_payroll_deductions` are stubs (Phase 2 — fusion_payroll integration) +- `answer_financial_question` is a stub (returns message to use other tools instead) +- Batch approval "Approve All" / "Reject All" buttons are in the chat panel but not yet in the match history list view +- "Needs Attention" panel shows placeholder text in the dashboard — the data is computed and returned by the API but the frontend rendering needs to be connected +- Consider switching OpenAI adapter from Chat Completions API to Responses API for better tool handling with newer models +- `o1` model does not support tool calling — no guard in place (o3/o4-mini do support it) +- Multi-company record rule on `fusion.accounting.session` — added in Phase 0 split-out (see UPGRADE_NOTES.md) diff --git a/fusion_accounting_ai/README.md b/fusion_accounting_ai/README.md new file mode 100644 index 00000000..9b5e6f24 --- /dev/null +++ b/fusion_accounting_ai/README.md @@ -0,0 +1,31 @@ +# Fusion Accounting AI + +Conversational AI co-pilot for Odoo Accounting using Claude or GPT. + +## What it does + +Embeds an AI agent in the Odoo Accounting menu. Users chat with the AI, which +calls into Odoo via tool-functions (read journal entries, find unreconciled +bank lines, draft follow-ups, generate audit reports, etc.). Tier 3 actions +(financial writes) require user approval via in-chat approval cards. + +## Install profiles + +This module works on three install profiles: + +1. **Pure Community + this module** — AI uses pure Community searches via the + data-adapter `_via_community` paths. Reduced functionality (no rich reports, + no Enterprise bank-rec features) but all read tools work. +2. **Community + this module + fusion native sub-modules** (recommended target) — + adapters route to fusion bank rec / fusion reports / etc. Full functionality. +3. **Community + Enterprise + this module** (legacy) — adapters route to Enterprise + APIs. Most functionality available; some Enterprise-specific UI integration + (e.g. live cursor in bank-rec widget) not supported. + +## Configuration + +Settings -> Fusion Accounting AI -> set API keys for Claude (default) and/or OpenAI. + +## Troubleshooting + +See `CLAUDE.md` in this module for known Odoo 19 gotchas. diff --git a/fusion_accounting_ai/UPGRADE_NOTES.md b/fusion_accounting_ai/UPGRADE_NOTES.md new file mode 100644 index 00000000..ef6f6500 --- /dev/null +++ b/fusion_accounting_ai/UPGRADE_NOTES.md @@ -0,0 +1,22 @@ +# UPGRADE_NOTES — fusion_accounting_ai + +## V19.0.1.0.0 (initial — Phase 0 split-out) + +### Origin +Code originally lived in `fusion_accounting/` (the original AI module). Split out +into this sub-module during Phase 0 of the Enterprise Takeover Roadmap. + +### Additions in this version +- `services/data_adapters/` — DataAdapter base + 4 adapters (bank_rec, reports, followup, assets) +- `services/tools/*.py` — every tool that called Enterprise-specific APIs refactored through adapters +- `migrations/19.0.1.0.0/post-migration.py` — reassigns ir_model_data ownership from old module name +- Multi-company record rule on `fusion.accounting.session` (was missing pre-Phase-0 per CLAUDE.md Known Issues) + +### Removed from manifest deps +- `account_accountant` (was hard dep) +- `account_reports` (was hard dep) +- `account_followup` (was hard dep) +- `mail` (now inherited via `fusion_accounting_core`) + +Replaced with: `fusion_accounting_core` (Community-only). Runtime detection of +Enterprise modules via the data adapter pattern. diff --git a/fusion_accounting_core/CLAUDE.md b/fusion_accounting_core/CLAUDE.md new file mode 100644 index 00000000..be2c8e44 --- /dev/null +++ b/fusion_accounting_core/CLAUDE.md @@ -0,0 +1,25 @@ +# fusion_accounting_core — Cursor / Claude Context + +## Purpose +Foundation for the Fusion Accounting sub-module suite. Owns: +- Three security groups (User / Manager / Admin) shared across all sub-modules +- Shared-field-ownership declarations on `account.move` and `account.reconcile.model` +- Runtime Enterprise-detection helper: `env['ir.module.module']._fusion_is_enterprise_accounting_installed()` + +## What lives here +- `models/account_move.py` — declares Enterprise-extension fields with identical + schemas / relation tables. Pure schema-preservation; no business logic. +- `models/account_reconcile_model.py` — same pattern for `created_automatically` +- `models/ir_module_module.py` — Enterprise-detection helpers +- `security/fusion_accounting_security.xml` — privilege + 3 groups + auto-assignment + +## Critical rules +- NEVER add business logic to the shared-field models (account_move.py here). + Logic belongs in the feature sub-module that owns it (e.g. fusion_accounting_bank_rec). +- NEVER rename the relation tables for shared M2Ms. They must match Enterprise verbatim + for the dual-ownership pattern to work. +- Shared fields here have NO defaults beyond what Enterprise sets. The point is preservation. + +## Cross-references +- Parent design: `fusion_accounting/docs/superpowers/specs/2026-04-18-fusion-accounting-enterprise-takeover-roadmap-design.md` (Section 3) +- Workspace conventions: `/Users/gurpreet/Github/Odoo-Modules/CLAUDE.md` diff --git a/fusion_accounting_core/README.md b/fusion_accounting_core/README.md new file mode 100644 index 00000000..d58ad06a --- /dev/null +++ b/fusion_accounting_core/README.md @@ -0,0 +1,39 @@ +# Fusion Accounting Core + +Foundation module for the Fusion Accounting suite. + +## What it does + +- Defines three security groups: Fusion Accounting User / Manager / Administrator +- Auto-promotes Odoo `account.group_account_user` -> Fusion User and + `account.group_account_manager` -> Fusion Admin +- Declares schema-preservation fields on `account.move` and `account.reconcile.model` + so that Enterprise extension fields (deferred revenue links, signing user, etc.) + survive an Enterprise uninstall +- Exposes the helper `env['ir.module.module']._fusion_is_enterprise_accounting_installed()` + +## Install + +This module never installs alone. Install `fusion_accounting` (the meta-module) +or any of the feature sub-modules — they all depend on `fusion_accounting_core`. + +## Uninstall + +Uninstalling `fusion_accounting_core` will remove the security groups and the +schema-preservation fields. If Enterprise is also installed, uninstalling +`fusion_accounting_core` will cause Odoo to consider the deferred / signing +fields owned only by Enterprise — which is the original Enterprise-only state +(no data loss, just back to Enterprise-controlled schema). + +## Troubleshooting + +If users are missing the "Fusion Accounting" privilege section in user settings +after install, the `implied_ids` mechanism only fires for newly-added users. +Backfill existing users via SQL: + + INSERT INTO res_groups_users_rel (gid, uid) + SELECT g.res_id, gu.uid + FROM res_groups_users_rel gu + JOIN ir_model_data g ON g.module = 'fusion_accounting_core' AND g.name = 'group_fusion_accounting_user' + JOIN ir_model_data ag ON ag.module = 'account' AND ag.name = 'group_account_user' AND gu.gid = ag.res_id + ON CONFLICT DO NOTHING; diff --git a/fusion_accounting_core/UPGRADE_NOTES.md b/fusion_accounting_core/UPGRADE_NOTES.md new file mode 100644 index 00000000..d761c8ec --- /dev/null +++ b/fusion_accounting_core/UPGRADE_NOTES.md @@ -0,0 +1,28 @@ +# UPGRADE_NOTES — fusion_accounting_core + +## V19.0.1.0.0 (initial — Phase 0) + +### Reference sources +- `RePackaged-Odoo/accounting/account_accountant/models/account_move.py` (Enterprise extension fields read for schema match) +- `RePackaged-Odoo/accounting/account_accountant/models/account_reconcile_model.py` (same) + +### Mirror-zone files (none in _core — _core has no Mirror zone) + +### Abstract-zone files (all of _core is abstract) +- `models/account_move.py` +- `models/account_reconcile_model.py` +- `models/ir_module_module.py` + +### Intentional deltas from Odoo +- Shared-field declarations have NO compute methods, NO @api decorators beyond + basic field types. Enterprise's account_move.py adds compute methods and + business logic; we deliberately do not duplicate them. When Enterprise is + installed, its compute methods run; when it's not, the fields are simply + unused (until a fusion sub-module decides to own that behavior). + +### Migrations +- `migrations/19.0.1.0.0/pre-migration.py` — rehome fusion security xml-ids + from module='fusion_accounting' to module='fusion_accounting_core' BEFORE + data-load (avoids unique-constraint crash on upgrade from pre-Phase-0) +- `migrations/19.0.1.0.0/post-migration.py` — idempotent safety-net for the + same rehome (zero-op if pre-migration already ran) diff --git a/fusion_accounting_migration/CLAUDE.md b/fusion_accounting_migration/CLAUDE.md new file mode 100644 index 00000000..069e3b04 --- /dev/null +++ b/fusion_accounting_migration/CLAUDE.md @@ -0,0 +1,20 @@ +# fusion_accounting_migration — Cursor / Claude Context + +## Purpose +Transitional sub-module that helps clients move from Odoo Enterprise accounting +to Odoo Community + Fusion Accounting without losing data. + +## What it does +- Safety guard: blocks uninstall of Enterprise accounting modules until the + migration wizard has run (per-module flag in ir.config_parameter) +- Migration wizard: shell that other fusion sub-modules extend with per-feature + migration logic (Phase 0 ships only the shell) + +## Critical +- The safety guard overrides `button_immediate_uninstall` AND `module_uninstall` + on `ir.module.module`. Both paths must be guarded — UI uninstall, CLI uninstall, + and API uninstall all go through one or the other. +- Each fusion feature sub-module that replaces an Enterprise feature MUST extend + the migration wizard's `action_run_migration` to add its own migration step + AND set the corresponding `fusion_accounting.migration..completed` + flag to True after running. diff --git a/fusion_accounting_migration/README.md b/fusion_accounting_migration/README.md new file mode 100644 index 00000000..3cd02237 --- /dev/null +++ b/fusion_accounting_migration/README.md @@ -0,0 +1,22 @@ +# Fusion Accounting Migration + +Transitional helper for moving clients from Odoo Enterprise to Community + Fusion. + +## When to use + +Install this module ONCE per client during the Enterprise->Fusion switchover. +After the switchover is complete and the client is comfortable on Community, +this module can be uninstalled. + +## How it works + +1. Install fusion_accounting (the meta-module) — pulls in this module +2. Open Fusion Accounting -> Migrate from Enterprise (top-level menu) +3. Wizard shows which Enterprise modules are detected and what migrations are available +4. Run migration; wizard reports counts and warnings +5. Uninstall Enterprise modules in dep-safe order (the safety guard prevents premature uninstall) + +## Override the safety guard + +If you need to uninstall an Enterprise module WITHOUT migrating (data will be lost), +set `fusion_accounting.migration..completed` to True in System Parameters. diff --git a/fusion_accounting_migration/UPGRADE_NOTES.md b/fusion_accounting_migration/UPGRADE_NOTES.md new file mode 100644 index 00000000..eafcafa7 --- /dev/null +++ b/fusion_accounting_migration/UPGRADE_NOTES.md @@ -0,0 +1,10 @@ +# UPGRADE_NOTES — fusion_accounting_migration + +## V19.0.1.0.0 (initial — Phase 0) + +Skeleton: safety guard + wizard shell. No per-feature migration logic registered yet. + +Added by future phases: +- Phase 1: bank-rec migration (verifies account.partial.reconcile rows are intact; sets `account_accountant.completed` flag) +- Phase 5: account_followup migration +- Phase 6: account_asset, account_budget migration