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