# Task D.5.6 Opportunity Lifecycle / Won-Lost Alignment

## Summary

- Added explicit opportunity outcome fields in `crm_opportunities`:
  - `outcomeStatus`
  - `closedAt`
  - `lostDetail`
  - `cancelReason`
  - `noQuotationReason`
- Preserved legacy compatibility fields:
  - `pipelineStage`
  - `closedWonAt`
  - `closedLostAt`

## Lifecycle Model

- Working stage continues to use `crm_opportunities.status` for now.
- UI/API now expose `stage` as an alias of `status`.
- Final result is tracked with `outcomeStatus`.

### Closed outcomes

- `won`
- `lost`
- `cancelled`
- `no_quotation`

### Reopen behavior

- Reopen clears final outcome data.
- Reopen returns the opportunity to:
  - `status = quotation_created`
  - `outcomeStatus = open`
  - `pipelineStage = opportunity`

## Master Options

- Added/seeded:
  - `crm_opportunity_stage`
  - `crm_opportunity_outcome_status`
  - `crm_opportunity_lost_reason`
  - `crm_opportunity_cancel_reason`
  - `crm_opportunity_no_quotation_reason`
- Kept legacy compatibility categories:
  - `crm_opportunity_status`
  - `crm_lost_reason`

## API Routes

- `POST /api/crm/opportunities/[id]/mark-won`
- `POST /api/crm/opportunities/[id]/mark-lost`
- `POST /api/crm/opportunities/[id]/mark-cancelled`
- `POST /api/crm/opportunities/[id]/mark-no-quotation`
- `POST /api/crm/opportunities/[id]/reopen`

## Quotation Alignment

- Quotation creation is blocked when linked opportunity has a closed `outcomeStatus`.
- Quotation creation keeps outcome as `open`.
- Opportunity stage sync remains compatibility-first and moves to `quotation_created`.

## Audit Actions

- `mark_opportunity_won`
- `mark_opportunity_lost`
- `mark_opportunity_cancelled`
- `mark_opportunity_no_quotation`
- `reopen_opportunity`

## Encoding Guard

- Verified repository text encoding with `npm run verify:encoding`.
- Repaired mojibake on CRM dashboard components in the sales ranking, follow-up, and hot-project sections.
