Files
alla-allaos-fullstack/plans/task-d.4.md
phaichayon ffa5de8311 task-d.4
2026-06-22 16:57:02 +07:00

711 lines
6.1 KiB
Markdown

# Task D.4: Won / Lost Lifecycle Governance
## Objective
Formalize CRM opportunity outcomes by introducing production-grade:
* Closed Won lifecycle
* Closed Lost lifecycle
* PO tracking
* Lost reason governance
* Outcome analytics foundation
This task replaces temporary dashboard assumptions and becomes the official business outcome model for CRM.
---
# Background
Business Discovery 3.0 has frozen:
## Closed Won
Definition:
```txt
PO Received
```
Only a received Purchase Order can move an enquiry to:
```txt
closed_won
```
Quotation approval or quotation acceptance alone does not create a Won outcome.
---
## Closed Lost
Definition:
```txt
Sales opportunity permanently lost
```
Lost requires:
```txt
Lost Reason
```
Optional:
```txt
Lost Competitor
Lost Remark
```
---
# Scope D4.1 Outcome Schema
Extend:
```txt
crm_enquiries
```
Add:
```txt
closedWonAt
closedLostAt
closedByUserId
poNumber
poDate
poAmount
poCurrency
lostReason
lostCompetitor
lostRemark
```
Rules:
```txt
Won
OR
Lost
```
never both.
---
# Scope D4.2 Lost Reason Master Data
Add master option category:
```txt
crm_lost_reason
```
Seed:
```txt
competitor_won
budget_not_approved
project_cancelled
customer_no_response
technical_requirement_mismatch
price_too_high
timeline_not_fit
other
```
Rules:
```txt
Lost Reason
```
required when closing lost.
---
# Scope D4.3 Outcome Service Layer
Create centralized lifecycle service.
Supported actions:
```txt
markWon()
markLost()
reopenOpportunity()
```
All outcome transitions must go through service layer.
No direct status updates.
---
# Scope D4.4 Mark Won Workflow
Action:
```txt
Mark Won
```
Available only when:
```txt
pipelineStage = enquiry
```
Required fields:
```txt
PO Number
PO Date
```
Optional:
```txt
PO Amount
PO Currency
PO Attachment
Remark
```
System behavior:
```txt
pipelineStage = closed_won
closedWonAt = now
closedByUserId = current user
```
Audit:
```txt
crm_enquiry
action = mark_won
```
---
# Scope D4.5 PO Attachment
Add attachment support.
Allowed:
```txt
PDF
Image
Excel
```
Relationship:
```txt
crm_enquiry
```
Attachment category:
```txt
purchase_order
```
UI:
```txt
Upload PO
View PO
Download PO
```
---
# Scope D4.6 Mark Lost Workflow
Action:
```txt
Mark Lost
```
Available only when:
```txt
pipelineStage = enquiry
```
Required:
```txt
Lost Reason
```
Optional:
```txt
Lost Competitor
Lost Remark
```
System behavior:
```txt
pipelineStage = closed_lost
closedLostAt = now
closedByUserId = current user
```
Audit:
```txt
crm_enquiry
action = mark_lost
```
---
# Scope D4.7 Reopen Opportunity
Allow controlled reopen.
Available for:
```txt
closed_lost
```
Only.
Not allowed for:
```txt
closed_won
```
Reason:
```txt
Won must remain final once PO exists.
```
Required:
```txt
Reopen Reason
```
Audit:
```txt
action = reopen
```
---
# Scope D4.8 UI Changes
Enquiry Detail
Add:
```txt
Outcome
```
Section.
Display:
```txt
Won
Lost
Open
```
Fields:
```txt
PO Information
Lost Information
Closed By
Closed Date
```
Actions:
```txt
Mark Won
Mark Lost
Reopen
```
---
# Scope D4.9 Dashboard KPI Migration
Replace temporary logic.
Current:
```txt
quotation accepted
```
Proxy.
New:
```txt
pipelineStage = closed_won
```
Official.
---
Update:
## Won Count
```txt
closed_won
```
---
## Lost Count
```txt
closed_lost
```
---
## Win Rate
```txt
Won / (Won + Lost)
```
---
## Lost Analysis
```txt
Lost By Reason
Lost By Competitor
Lost By Sales
Lost By Product Type
Lost By Branch
```
---
# Scope D4.10 Revenue Rules
Freeze:
## Won Revenue
Priority:
```txt
PO Amount
```
If available.
Fallback:
```txt
Quotation Total
```
if PO Amount is empty.
---
Lost Revenue:
```txt
Quotation Total
```
for reporting opportunity loss.
---
# Scope D4.11 Report Foundation
Prepare data for Task K.
Add reusable reporting helpers:
```txt
getWonRevenue()
getLostRevenue()
getLostByReason()
getLostByCompetitor()
getWinRate()
```
Organization scoped.
---
# Scope D4.12 Permissions
Add:
```txt
crm.enquiry.mark_won
crm.enquiry.mark_lost
crm.enquiry.reopen
```
Default:
```txt
sales
sales_manager
department_manager
crm_admin
```
Not granted:
```txt
marketing
```
---
# Scope D4.13 Security
Marketing may see:
```txt
Won
Lost
Outcome Summary
```
Marketing may not see:
```txt
PO Amount
Revenue
Quotation Pricing
```
Enforce server-side.
---
# Scope D4.14 Audit
Entity:
```txt
crm_enquiry
```
Actions:
```txt
mark_won
mark_lost
reopen
upload_po
```
Payload:
```txt
poNumber
poAmount
lostReason
lostCompetitor
closedBy
closedAt
```
---
# Scope D4.15 Verification
Scenario 1
```txt
Enquiry
→ Mark Won
```
Expected:
```txt
closed_won
PO stored
audit created
```
---
Scenario 2
```txt
Enquiry
→ Mark Lost
```
Expected:
```txt
closed_lost
lost reason stored
audit created
```
---
Scenario 3
```txt
Lost
→ Reopen
```
Expected:
```txt
enquiry
reopen audit created
```
---
Scenario 4
```txt
Won
→ Reopen
```
Expected:
```txt
blocked
```
---
Scenario 5
Marketing User
Expected:
```txt
see outcome
cannot see PO amount
```
---
# Documentation
Create:
```txt
docs/adr/0016-won-lost-lifecycle-governance.md
docs/implementation/task-d4-won-lost-lifecycle-governance.md
```
Freeze:
```txt
PO = Won
Lost Reason required
Won cannot reopen
Lost can reopen
Revenue attribution rule
Win/Loss KPI rule
```
---
# Explicit Non-Scope
Do NOT implement:
```txt
Sales Order
Project Handover
Invoice
Delivery Note
ERP Integration
Forecasting
Probability Scoring
```
---
# Deliverables
1. Won Lifecycle
2. Lost Lifecycle
3. PO Tracking
4. Lost Reason Governance
5. Outcome Analytics Foundation
6. Dashboard KPI Migration
7. Security Rules
8. Audit Coverage
9. ADR 0016
---
# Definition of Done
Task D.4 is complete when:
* PO creates Closed Won
* Lost requires Lost Reason
* Won/Lost KPIs use pipelineStage
* Lost analytics exist
* Revenue rules are frozen
* Marketing visibility restrictions are enforced
* Audit logs exist
* ADR 0016 exists
Result:
```txt
Lead
→ Enquiry
→ Quotation
→ Closed Won (PO)
Lead
→ Enquiry
→ Quotation
→ Closed Lost
```
becomes the official CRM business outcome lifecycle.