diff --git a/fusion_accounting/PHASE_4_PLAN.md b/fusion_accounting/PHASE_4_PLAN.md new file mode 100644 index 00000000..17b66285 --- /dev/null +++ b/fusion_accounting/PHASE_4_PLAN.md @@ -0,0 +1,140 @@ +# Phase 4 — Fusion Accounting Follow-up Implementation Plan + +**Module:** `fusion_accounting_followup` +**Branch:** `fusion_accounting/phase-4-followup` +**Pre-phase tag:** `fusion_accounting/pre-phase-4` +**Estimated tasks:** ~35 +**Reference:** `/Users/gurpreet/Github/RePackaged-Odoo/accounting/account_followup/` (~1318 LOC Python) + +## Goal + +Replace Enterprise's `account_followup` module — multi-level dunning sequences for unpaid invoices, with AI augmentation: contextually-appropriate follow-up text generation + payment-risk scoring + tone adjustment based on customer history. Coexists with Enterprise. + +## Architecture (HYBRID engine, Phases 1-3 pattern) + +``` +fusion.followup.engine (AbstractModel) ← shared primitives +├── compute_followup_level(partner) +├── get_overdue_for_partner(partner) +├── send_followup_email(partner, level=None) +├── escalate_to_next_level(partner) +├── pause_followup(partner, until_date) +├── reset_followup(partner) +└── snapshot_followup_history(partner) ← audit/history + +services/ ← pure-Python +├── overdue_aging.py → bucket overdue lines (current/30/60/90/120+) +├── level_resolver.py → match aging buckets to follow-up levels +├── risk_scorer.py → payment-history risk score (0-100) +├── tone_selector.py → gentle/firm/legal based on level + risk +├── followup_text_generator.py → LLM-generated follow-up text +└── followup_text_prompt.py → provider-agnostic LLM prompt + +models/ +├── fusion_followup_level.py → level definition (delay days, template, action) +├── fusion_followup_run.py → execution record (per-partner per-level) +├── fusion_followup_text_cache.py → LLM-generated text cache (cost-saving) +├── fusion_followup_engine.py → AbstractModel orchestrator +├── res_partner.py (inherit) → fusion_followup_status, fusion_followup_paused_until +└── account_move_line.py (inherit) → followup_level_id (which level last contacted at) + +controllers/followup_controller.py ← 6 JSON-RPC endpoints +├── /fusion/followup/list_overdue → list partners with overdue +├── /fusion/followup/get_partner_detail → single partner with aging + history +├── /fusion/followup/generate_text → AI-generate follow-up text +├── /fusion/followup/send → send a follow-up email +├── /fusion/followup/pause → pause follow-ups for a partner +└── /fusion/followup/reset → reset follow-up state + +static/src/ +├── scss/ ← follow-up design tokens +├── services/followup_service.js ← reactive state + RPC wrappers +├── views/followup_dashboard/ ← top-level OWL controller +└── components/ ← partner_card, aging_bucket_strip, ai_text_panel, + followup_history_table, risk_badge +``` + +## Coexistence + +`group_fusion_show_when_enterprise_absent`. Follow-up menu visible only when `account_followup` NOT installed. + +## Tasks (~35 total) + +### Group 1: Foundation (1-2) +1. Safety net (DONE) +2. Plan doc + module skeleton + +### Group 2: Pure-Python services TDD (3-7) +3. `services/overdue_aging.py` (TDD: bucket lines into 0/30/60/90/120+) +4. `services/level_resolver.py` (TDD: match aging to level) +5. `services/risk_scorer.py` (TDD: payment-history risk 0-100) +6. `services/tone_selector.py` (TDD: gentle/firm/legal) +7. `services/followup_text_generator.py` + `followup_text_prompt.py` (LLM) + +### Group 3: Persisted models (8-12) +8. `models/fusion_followup_level.py` (level definition) +9. `models/fusion_followup_run.py` (execution record) +10. `models/fusion_followup_text_cache.py` (LLM cache) +11. `models/res_partner.py` (inherit: fusion_followup_status, paused_until) +12. `models/account_move_line.py` (inherit: followup_level_id) + +### Group 4: Engine + integration tests (13-14) +13. `models/fusion_followup_engine.py` (7-method API) +14. Engine integration tests + +### Group 5: Backend wiring (15-18) +15. JSON-RPC controller (6 endpoints) +16. FollowupAdapter wiring `_via_fusion` paths +17. 4 new AI tools (list_overdue, generate_text, send_followup, get_risk_score) +18. Cron — daily scan + escalate + +### Group 6: Tests + perf (19-21) +19. Property-based tests (Hypothesis: aging buckets sum to total) +20. Integration tests (full follow-up flow: scan → escalate → send → reset) +21. Performance benchmarks (P95: scan < 500ms, generate_text < 5s incl. LLM) + +### Group 7: Frontend (22-26) +22. SCSS tokens + main stylesheet +23. `followup_service.js` +24. `followup_dashboard` (top-level) +25. `partner_card` + `aging_bucket_strip` + `risk_badge` +26. `ai_text_panel` (Fusion-only) + `followup_history_table` + +### Group 8: Wizards + data (27-29) +27. Default follow-up levels XML data (7-day reminder, 30-day, 60-day, legal) +28. Default mail templates XML data (3 escalation levels) +29. "Send batch follow-ups" wizard + +### Group 9: Migration + coexistence (30-32) +30. Migration wizard inheritance — backfill from account_followup tables +31. Menu + window action with coexistence group filter +32. Coexistence test + +### Group 10: Final tests + polish (33-37) +33. 5 OWL tour tests +34. Local LLM compat test for text_generator +35. Update meta-module manifest +36. CLAUDE.md, UPGRADE_NOTES.md, README.md +37. End-to-end smoke + tag phase-4-complete + push + +## Performance Targets (P95) + +- `compute_followup_level`: <50ms +- `get_overdue_for_partner`: <100ms +- `send_followup_email` (no LLM): <200ms +- `generate_text` (with LLM): <5s +- Controller `list_overdue` (50 partners): <500ms + +## V19 Conventions (Phases 1-3 lessons) + +- `models.Constraint` not `_sql_constraints` +- No `@api.depends('id')` on stored compute fields +- `@route(type='jsonrpc')` not `type='json'` +- `ir.cron` no `numbercall` field +- `res.groups.user_ids` not `users` +- `ir.ui.menu.group_ids` not `groups_id` +- `from odoo.exceptions import UserError, ValidationError` (NOT `self.env['ir.exceptions'].UserError`) + +## Test Targets + +Match Phases 1-3 test pyramid. Phase 4 target: ~80-100 additional tests → ~510-530 total project tests. diff --git a/fusion_accounting_followup/__init__.py b/fusion_accounting_followup/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/fusion_accounting_followup/__manifest__.py b/fusion_accounting_followup/__manifest__.py new file mode 100644 index 00000000..a3936fac --- /dev/null +++ b/fusion_accounting_followup/__manifest__.py @@ -0,0 +1,45 @@ +{ + 'name': 'Fusion Accounting Follow-up', + 'version': '19.0.1.0.0', + 'category': 'Accounting/Accounting', + 'summary': 'AI-augmented customer follow-ups (dunning) for unpaid invoices.', + 'description': """ +Fusion Accounting Follow-up +=========================== + +A Fusion-native replacement for Odoo Enterprise's account_followup module. + +CORE scope (Phase 4): +- Multi-level dunning sequences (gentle reminder, firm warning, legal) +- Per-partner follow-up state (current level, paused-until, history) +- Automated daily scan + escalation cron +- Mail templates per level + +AI augmentation: +- Contextually-appropriate follow-up text generation (LLM) +- Payment-risk scoring from invoice/payment history +- Tone selection (gentle/firm/legal) based on level + risk + +Coexists with Enterprise: when account_followup is installed, the Fusion +menu hides; the engine + AI tools remain available for the chat. +""", + 'author': 'Fusion Accounting', + 'license': 'LGPL-3', + 'depends': [ + 'fusion_accounting_core', + 'fusion_accounting_ai', + 'account', + 'mail', + ], + 'data': [ + 'security/ir.model.access.csv', + ], + 'assets': { + 'web.assets_backend': [ + ], + }, + 'installable': True, + 'auto_install': False, + 'application': False, + 'icon': '/fusion_accounting_followup/static/description/icon.png', +} diff --git a/fusion_accounting_followup/controllers/__init__.py b/fusion_accounting_followup/controllers/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/fusion_accounting_followup/models/__init__.py b/fusion_accounting_followup/models/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/fusion_accounting_followup/reports/__init__.py b/fusion_accounting_followup/reports/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/fusion_accounting_followup/security/ir.model.access.csv b/fusion_accounting_followup/security/ir.model.access.csv new file mode 100644 index 00000000..97dd8b91 --- /dev/null +++ b/fusion_accounting_followup/security/ir.model.access.csv @@ -0,0 +1 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink diff --git a/fusion_accounting_followup/services/__init__.py b/fusion_accounting_followup/services/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/fusion_accounting_followup/static/description/icon.png b/fusion_accounting_followup/static/description/icon.png new file mode 100644 index 00000000..6773c627 Binary files /dev/null and b/fusion_accounting_followup/static/description/icon.png differ diff --git a/fusion_accounting_followup/tests/__init__.py b/fusion_accounting_followup/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/fusion_accounting_followup/wizards/__init__.py b/fusion_accounting_followup/wizards/__init__.py new file mode 100644 index 00000000..e69de29b