# 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.