Files
Odoo-Modules/fusion_accounting/PHASE_4_PLAN.md
gsinghpal ea2f44287f feat(fusion_accounting_followup): Phase 4 skeleton + plan
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
2026-04-19 20:31:07 -04:00

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)

  1. Safety net (DONE)
  2. Plan doc + module skeleton

Group 2: Pure-Python services TDD (3-7)

  1. services/overdue_aging.py (TDD: bucket lines into 0/30/60/90/120+)
  2. services/level_resolver.py (TDD: match aging to level)
  3. services/risk_scorer.py (TDD: payment-history risk 0-100)
  4. services/tone_selector.py (TDD: gentle/firm/legal)
  5. services/followup_text_generator.py + followup_text_prompt.py (LLM)

Group 3: Persisted models (8-12)

  1. models/fusion_followup_level.py (level definition)
  2. models/fusion_followup_run.py (execution record)
  3. models/fusion_followup_text_cache.py (LLM cache)
  4. models/res_partner.py (inherit: fusion_followup_status, paused_until)
  5. models/account_move_line.py (inherit: followup_level_id)

Group 4: Engine + integration tests (13-14)

  1. models/fusion_followup_engine.py (7-method API)
  2. Engine integration tests

Group 5: Backend wiring (15-18)

  1. JSON-RPC controller (6 endpoints)
  2. FollowupAdapter wiring _via_fusion paths
  3. 4 new AI tools (list_overdue, generate_text, send_followup, get_risk_score)
  4. Cron — daily scan + escalate

Group 6: Tests + perf (19-21)

  1. Property-based tests (Hypothesis: aging buckets sum to total)
  2. Integration tests (full follow-up flow: scan → escalate → send → reset)
  3. Performance benchmarks (P95: scan < 500ms, generate_text < 5s incl. LLM)

Group 7: Frontend (22-26)

  1. SCSS tokens + main stylesheet
  2. followup_service.js
  3. followup_dashboard (top-level)
  4. partner_card + aging_bucket_strip + risk_badge
  5. ai_text_panel (Fusion-only) + followup_history_table

Group 8: Wizards + data (27-29)

  1. Default follow-up levels XML data (7-day reminder, 30-day, 60-day, legal)
  2. Default mail templates XML data (3 escalation levels)
  3. "Send batch follow-ups" wizard

Group 9: Migration + coexistence (30-32)

  1. Migration wizard inheritance — backfill from account_followup tables
  2. Menu + window action with coexistence group filter
  3. Coexistence test

Group 10: Final tests + polish (33-37)

  1. 5 OWL tour tests
  2. Local LLM compat test for text_generator
  3. Update meta-module manifest
  4. CLAUDE.md, UPGRADE_NOTES.md, README.md
  5. 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.