35-task plan to replace Enterprise account_followup module: - Multi-level dunning (gentle reminder -> firm warning -> legal) - AI augmentation: contextual follow-up text generation + payment risk scoring + tone selection - HYBRID engine: shared primitives + persisted level/run/cache models - Per-partner state: current level, paused-until, history - Coexists with Enterprise (group_fusion_show_when_enterprise_absent) - Same V19 conventions + test pyramid + perf-budget discipline as Phases 1-3 Made-with: Cursor
141 lines
6.2 KiB
Markdown
141 lines
6.2 KiB
Markdown
# 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.
|