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
6.2 KiB
6.2 KiB
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)
- Safety net (DONE)
- Plan doc + module skeleton
Group 2: Pure-Python services TDD (3-7)
services/overdue_aging.py(TDD: bucket lines into 0/30/60/90/120+)services/level_resolver.py(TDD: match aging to level)services/risk_scorer.py(TDD: payment-history risk 0-100)services/tone_selector.py(TDD: gentle/firm/legal)services/followup_text_generator.py+followup_text_prompt.py(LLM)
Group 3: Persisted models (8-12)
models/fusion_followup_level.py(level definition)models/fusion_followup_run.py(execution record)models/fusion_followup_text_cache.py(LLM cache)models/res_partner.py(inherit: fusion_followup_status, paused_until)models/account_move_line.py(inherit: followup_level_id)
Group 4: Engine + integration tests (13-14)
models/fusion_followup_engine.py(7-method API)- Engine integration tests
Group 5: Backend wiring (15-18)
- JSON-RPC controller (6 endpoints)
- FollowupAdapter wiring
_via_fusionpaths - 4 new AI tools (list_overdue, generate_text, send_followup, get_risk_score)
- Cron — daily scan + escalate
Group 6: Tests + perf (19-21)
- Property-based tests (Hypothesis: aging buckets sum to total)
- Integration tests (full follow-up flow: scan → escalate → send → reset)
- Performance benchmarks (P95: scan < 500ms, generate_text < 5s incl. LLM)
Group 7: Frontend (22-26)
- SCSS tokens + main stylesheet
followup_service.jsfollowup_dashboard(top-level)partner_card+aging_bucket_strip+risk_badgeai_text_panel(Fusion-only) +followup_history_table
Group 8: Wizards + data (27-29)
- Default follow-up levels XML data (7-day reminder, 30-day, 60-day, legal)
- Default mail templates XML data (3 escalation levels)
- "Send batch follow-ups" wizard
Group 9: Migration + coexistence (30-32)
- Migration wizard inheritance — backfill from account_followup tables
- Menu + window action with coexistence group filter
- Coexistence test
Group 10: Final tests + polish (33-37)
- 5 OWL tour tests
- Local LLM compat test for text_generator
- Update meta-module manifest
- CLAUDE.md, UPGRADE_NOTES.md, README.md
- End-to-end smoke + tag phase-4-complete + push
Performance Targets (P95)
compute_followup_level: <50msget_overdue_for_partner: <100mssend_followup_email(no LLM): <200msgenerate_text(with LLM): <5s- Controller
list_overdue(50 partners): <500ms
V19 Conventions (Phases 1-3 lessons)
models.Constraintnot_sql_constraints- No
@api.depends('id')on stored compute fields @route(type='jsonrpc')nottype='json'ir.cronnonumbercallfieldres.groups.user_idsnotusersir.ui.menu.group_idsnotgroups_idfrom odoo.exceptions import UserError, ValidationError(NOTself.env['ir.exceptions'].UserError)
Test Targets
Match Phases 1-3 test pyramid. Phase 4 target: ~80-100 additional tests → ~510-530 total project tests.