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

6.1 KiB

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:

PO Received

Only a received Purchase Order can move an enquiry to:

closed_won

Quotation approval or quotation acceptance alone does not create a Won outcome.


Closed Lost

Definition:

Sales opportunity permanently lost

Lost requires:

Lost Reason

Optional:

Lost Competitor
Lost Remark

Scope D4.1 Outcome Schema

Extend:

crm_enquiries

Add:

closedWonAt
closedLostAt

closedByUserId

poNumber
poDate
poAmount
poCurrency

lostReason
lostCompetitor
lostRemark

Rules:

Won
OR
Lost

never both.


Scope D4.2 Lost Reason Master Data

Add master option category:

crm_lost_reason

Seed:

competitor_won
budget_not_approved
project_cancelled
customer_no_response
technical_requirement_mismatch
price_too_high
timeline_not_fit
other

Rules:

Lost Reason

required when closing lost.


Scope D4.3 Outcome Service Layer

Create centralized lifecycle service.

Supported actions:

markWon()
markLost()
reopenOpportunity()

All outcome transitions must go through service layer.

No direct status updates.


Scope D4.4 Mark Won Workflow

Action:

Mark Won

Available only when:

pipelineStage = enquiry

Required fields:

PO Number
PO Date

Optional:

PO Amount
PO Currency
PO Attachment
Remark

System behavior:

pipelineStage = closed_won

closedWonAt = now
closedByUserId = current user

Audit:

crm_enquiry

action = mark_won

Scope D4.5 PO Attachment

Add attachment support.

Allowed:

PDF
Image
Excel

Relationship:

crm_enquiry

Attachment category:

purchase_order

UI:

Upload PO
View PO
Download PO

Scope D4.6 Mark Lost Workflow

Action:

Mark Lost

Available only when:

pipelineStage = enquiry

Required:

Lost Reason

Optional:

Lost Competitor
Lost Remark

System behavior:

pipelineStage = closed_lost

closedLostAt = now
closedByUserId = current user

Audit:

crm_enquiry

action = mark_lost

Scope D4.7 Reopen Opportunity

Allow controlled reopen.

Available for:

closed_lost

Only.

Not allowed for:

closed_won

Reason:

Won must remain final once PO exists.

Required:

Reopen Reason

Audit:

action = reopen

Scope D4.8 UI Changes

Enquiry Detail

Add:

Outcome

Section.

Display:

Won
Lost
Open

Fields:

PO Information
Lost Information
Closed By
Closed Date

Actions:

Mark Won
Mark Lost
Reopen

Scope D4.9 Dashboard KPI Migration

Replace temporary logic.

Current:

quotation accepted

Proxy.

New:

pipelineStage = closed_won

Official.


Update:

Won Count

closed_won

Lost Count

closed_lost

Win Rate

Won / (Won + Lost)

Lost Analysis

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:

PO Amount

If available.

Fallback:

Quotation Total

if PO Amount is empty.


Lost Revenue:

Quotation Total

for reporting opportunity loss.


Scope D4.11 Report Foundation

Prepare data for Task K.

Add reusable reporting helpers:

getWonRevenue()
getLostRevenue()

getLostByReason()

getLostByCompetitor()

getWinRate()

Organization scoped.


Scope D4.12 Permissions

Add:

crm.enquiry.mark_won
crm.enquiry.mark_lost
crm.enquiry.reopen

Default:

sales
sales_manager
department_manager
crm_admin

Not granted:

marketing

Scope D4.13 Security

Marketing may see:

Won
Lost
Outcome Summary

Marketing may not see:

PO Amount
Revenue
Quotation Pricing

Enforce server-side.


Scope D4.14 Audit

Entity:

crm_enquiry

Actions:

mark_won
mark_lost
reopen
upload_po

Payload:

poNumber
poAmount

lostReason
lostCompetitor

closedBy
closedAt

Scope D4.15 Verification

Scenario 1

Enquiry
→ Mark Won

Expected:

closed_won
PO stored
audit created

Scenario 2

Enquiry
→ Mark Lost

Expected:

closed_lost
lost reason stored
audit created

Scenario 3

Lost
→ Reopen

Expected:

enquiry
reopen audit created

Scenario 4

Won
→ Reopen

Expected:

blocked

Scenario 5

Marketing User

Expected:

see outcome
cannot see PO amount

Documentation

Create:

docs/adr/0016-won-lost-lifecycle-governance.md
docs/implementation/task-d4-won-lost-lifecycle-governance.md

Freeze:

PO = Won
Lost Reason required
Won cannot reopen
Lost can reopen
Revenue attribution rule
Win/Loss KPI rule

Explicit Non-Scope

Do NOT implement:

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:

Lead
→ Enquiry
→ Quotation
→ Closed Won (PO)

Lead
→ Enquiry
→ Quotation
→ Closed Lost

becomes the official CRM business outcome lifecycle.