# fusion_accounting — AI Accounting Co-Pilot ## 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. ## Architecture ``` fusion_accounting/ ├── models/ 7 models (6 new + 1 inherit on account.move) ├── services/ │ ├── agent.py AI orchestrator (prompt assembly, tool dispatch loop) │ ├── adapters/ Claude + OpenAI adapters with native tool-calling │ ├── tools/ 85 tool functions across 11 domain files │ ├── prompts/ System prompt builder + 12 domain-specific prompts │ └── scoring.py Confidence scoring + tier promotion logic ├── controllers/ 8 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/ 82 tool definitions, 2 default rules, 2 crons └── 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 4.5+ 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 ### 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) ### 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 ### 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 - "New Chat" button closes current session and creates a fresh one ## 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) ### 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 ### 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 ### 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" ### 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; ``` ## 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` ## Deployment Commands ```bash # Deploy module to server 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" # Upgrade module (use alt port to avoid conflict with running instance) 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" # Restart container ssh odoo-westin "docker restart odoo-dev-app" # Check logs ssh odoo-westin "docker logs odoo-dev-app --tail 100" ``` ## 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 ## Models | Model | Type | Purpose | |---|---|---| | `fusion.accounting.session` | Model | Chat sessions with message JSON storage | | `fusion.accounting.match.history` | Model | Every AI tool call + decision (approved/rejected/pending) | | `fusion.accounting.rule` | Model | Fusion Rules engine with versioning and auto-promotion | | `fusion.accounting.tool` | Model | Tool registry (82 tools seeded from XML) | | `fusion.accounting.dashboard` | TransientModel | Computed health metrics (use `.new()` not `.create()`) | | `fusion.accounting.agent` | AbstractModel | AI orchestrator | | `fusion.accounting.adapter.claude` | AbstractModel | Claude tool-calling adapter | | `fusion.accounting.adapter.openai` | AbstractModel | OpenAI tool-calling adapter | | `fusion.accounting.scoring` | AbstractModel | Confidence scoring | | `account.move` (inherit) | Model | Post-action audit hook | ## 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)` ## Known Issues / Future Work - `read_group()` deprecation warnings — migrate to `_read_group()` when format is documented - `verify_source_deductions`, `generate_t4`, `generate_roe` are stubs pointing to fusion_payroll (by design — Phase 2) - `account.return` model used in HST tools may not exist in all Odoo 19 setups — needs try/except guard - Batch approval "Approve All" / "Reject All" buttons are in the chat panel but not yet in the match history list view