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
This commit is contained in:
@@ -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 `<search>` element
|
||||
- NO `string` attribute on `<group>` element inside search views
|
||||
- Group-by filters MUST have `domain="[]"` attribute
|
||||
- Add `<separator/>` before `<group>` 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 `<div class="slot" t-att-data-idx="index"/>`, 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 <group_id>, 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 \"<SQL>\""
|
||||
|
||||
# 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.
|
||||
|
||||
38
fusion_accounting/README.md
Normal file
38
fusion_accounting/README.md
Normal file
@@ -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 <db> -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 <db> --no-http <<EOF
|
||||
env['ir.module.module'].search([
|
||||
('name', 'in', [
|
||||
'fusion_accounting',
|
||||
'fusion_accounting_ai',
|
||||
'fusion_accounting_migration',
|
||||
'fusion_accounting_core',
|
||||
]),
|
||||
('state', '=', 'installed'),
|
||||
]).button_immediate_uninstall()
|
||||
EOF
|
||||
|
||||
## Documentation
|
||||
|
||||
See `docs/superpowers/specs/` for the design and `docs/superpowers/plans/` for implementation plans.
|
||||
272
fusion_accounting_ai/CLAUDE.md
Normal file
272
fusion_accounting_ai/CLAUDE.md
Normal file
@@ -0,0 +1,272 @@
|
||||
# fusion_accounting_ai — Cursor / Claude Context
|
||||
|
||||
## Purpose
|
||||
Conversational AI co-pilot for Odoo Accounting using Claude or GPT with native
|
||||
tool-calling. Embeds in any Odoo install via the data-adapter pattern (works on
|
||||
Community-only, Community + fusion native sub-modules, or Community + Enterprise).
|
||||
|
||||
## Sub-module relationships
|
||||
- `fusion_accounting_core`: hard dep, provides security groups + Enterprise detection
|
||||
- `fusion_accounting_bank_rec` (Phase 1): adapter routes to it when present
|
||||
- `fusion_accounting_reports` (Phase 2): same
|
||||
- `fusion_accounting_followup` (Phase 5): same
|
||||
- Odoo Enterprise modules: detected at runtime, AI tools route through them via adapters
|
||||
|
||||
## Data-adapter pattern (Phase 0 addition)
|
||||
- `services/data_adapters/base.py` — `DataAdapter` + `AdapterMode`
|
||||
- `services/data_adapters/_registry.py` — `get_adapter(env, name)` + `register_adapter`
|
||||
- One adapter file per domain: `bank_rec.py`, `reports.py`, `followup.py`, `assets.py`
|
||||
- Each adapter implements `<method>_via_fusion`, `<method>_via_enterprise`, `<method>_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 `<search>` element
|
||||
- NO `string` attribute on `<group>` element inside search views
|
||||
- Group-by filters MUST have `domain="[]"` attribute
|
||||
- Add `<separator/>` before `<group>` 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 `<div class="slot" t-att-data-idx="index"/>`, 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 <group_id>, 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 \"<SQL>\""
|
||||
|
||||
# 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)
|
||||
31
fusion_accounting_ai/README.md
Normal file
31
fusion_accounting_ai/README.md
Normal file
@@ -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.
|
||||
22
fusion_accounting_ai/UPGRADE_NOTES.md
Normal file
22
fusion_accounting_ai/UPGRADE_NOTES.md
Normal file
@@ -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.
|
||||
25
fusion_accounting_core/CLAUDE.md
Normal file
25
fusion_accounting_core/CLAUDE.md
Normal file
@@ -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`
|
||||
39
fusion_accounting_core/README.md
Normal file
39
fusion_accounting_core/README.md
Normal file
@@ -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;
|
||||
28
fusion_accounting_core/UPGRADE_NOTES.md
Normal file
28
fusion_accounting_core/UPGRADE_NOTES.md
Normal file
@@ -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)
|
||||
20
fusion_accounting_migration/CLAUDE.md
Normal file
20
fusion_accounting_migration/CLAUDE.md
Normal file
@@ -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.<module>.completed`
|
||||
flag to True after running.
|
||||
22
fusion_accounting_migration/README.md
Normal file
22
fusion_accounting_migration/README.md
Normal file
@@ -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.<module>.completed` to True in System Parameters.
|
||||
10
fusion_accounting_migration/UPGRADE_NOTES.md
Normal file
10
fusion_accounting_migration/UPGRADE_NOTES.md
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user