# Opportunity Lifecycle Management Guide

This guide documents the Salesforce Opportunity integration for service lifecycle tracking.

## Table of Contents

1. [Overview](#overview)
2. [Existing Field Architecture](#existing-field-architecture)
3. [Opportunity Matching Rules](#opportunity-matching-rules)
4. [Internet Eligibility Flow](#internet-eligibility-flow)
5. [ID Verification Flow](#id-verification-flow)
6. [Order Placement Flow](#order-placement-flow)
7. [Service Activation Flow](#service-activation-flow)
8. [Cancellation Flow](#cancellation-flow)
9. [Implementation Checklist](#implementation-checklist)

---

## Overview

### What Customers See vs What's Internal

```
┌─────────────────────────────────────────────────────────────────────────┐
│                    INTERNAL ONLY (Sales/CS Pipeline)                     │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  Opportunity Stages: Introduction, Ready (before Order placed)          │
│  Application Stages: INTRO-1, UNRESPONSIVE-1, etc. (CS workflow)        │
│  Eligibility fields on Account (eligibility status)                     │
│  ID Verification fields on Account (verification status)                │
│                                                                          │
│  These are for internal tracking - customer sees results, not fields.   │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────┐
│                    CUSTOMER-FACING (Portal)                              │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  1. ELIGIBILITY STATUS (from Account field)                              │
│     └─ "Checking...", "Eligible", "Not Eligible"                         │
│                                                                          │
│  2. ID VERIFICATION STATUS (from Account field)                          │
│     └─ "Pending", "Verified", "Rejected"                                 │
│                                                                          │
│  3. ORDER TRACKING (from Salesforce Order)                               │
│     └─ After order placed, before WHMCS activates                        │
│                                                                          │
│  4. SERVICE PAGE (from WHMCS)                                            │
│     └─ After service is active                                           │
│                                                                          │
│  5. CANCELLATION STATUS (from Opportunity)                               │
│     └─ End date, return deadline, kit status                             │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘
```

---

## Existing Field Architecture

### Account Fields (Already Exist)

**Internet Eligibility Fields:**

| Field             | API Name                                    | Purpose                       |
| ----------------- | ------------------------------------------- | ----------------------------- |
| Eligibility Value | `Internet_Eligibility__c`                   | The actual eligibility result |
| Status            | `Internet_Eligibility_Status__c`            | Pending, Checked              |
| Requested At      | `Internet_Eligibility_Request_Date_Time__c` | When request was made         |
| Checked At        | `Internet_Eligibility_Checked_Date_Time__c` | When eligibility was checked  |

**Notes:**

- The portal does **not** store the Case ID or agent notes on the Account in our current Salesforce environment.
- Agent notes should live inside the **Case Description** / Case activity history.

**ID Verification Fields:**

| Field             | API Name                                 | Purpose                     |
| ----------------- | ---------------------------------------- | --------------------------- |
| Status            | `Id_Verification_Status__c`              | Pending, Verified, Rejected |
| Submitted At      | `Id_Verification_Submitted_Date_Time__c` | When submitted              |
| Verified At       | `Id_Verification_Verified_Date_Time__c`  | When verified               |
| Notes             | `Id_Verification_Note__c`                | Agent notes                 |
| Rejection Message | `Id_Verification_Rejection_Message__c`   | Reason for rejection        |

### Opportunity Fields (Existing)

**Core Fields:**

| Field             | API Name               | Purpose                                                         |
| ----------------- | ---------------------- | --------------------------------------------------------------- |
| Stage             | `StageName`            | Introduction, Ready, Post Processing, Active, △Cancelling, etc. |
| Commodity Type    | `CommodityType`        | Personal SonixNet Home Internet, SIM, VPN                       |
| Application Stage | `Application_Stage__c` | INTRO-1 (for portal)                                            |

**Internet Cancellation Fields:**

| Field                  | API Name                              | Purpose                        |
| ---------------------- | ------------------------------------- | ------------------------------ |
| Cancellation Notice    | `CancellationNotice__c`               | 有, 未, 不要, 移転             |
| Scheduled Cancellation | `ScheduledCancellationDateAndTime__c` | End of cancellation month      |
| Line Return Status     | `LineReturn__c`                       | NotYet, SentKit, Returned, etc |

**SIM Cancellation Fields:**

| Field                      | API Name                                 | Purpose                 |
| -------------------------- | ---------------------------------------- | ----------------------- |
| SIM Cancellation Notice    | `SIMCancellationNotice__c`               | 有, 未, 不要            |
| SIM Scheduled Cancellation | `SIMScheduledCancellationDateAndTime__c` | End of SIM cancellation |

_Note: SIM customer comments are stored on the Cancellation Case, same as Internet._

### Portal Integration Fields (Existing)

| Field              | API Name                | Type     | Purpose                          |
| ------------------ | ----------------------- | -------- | -------------------------------- |
| Opportunity Source | `Opportunity_Source__c` | Picklist | How Opportunity was created      |
| WHMCS Service ID   | `WHMCS_Service_ID__c`   | Number   | Link to WHMCS after provisioning |

### Order Fields (Existing)

| Field       | API Name        | Purpose                    |
| ----------- | --------------- | -------------------------- |
| Opportunity | `OpportunityId` | Links Order TO Opportunity |

### Case Fields (Existing)

| Field       | API Name        | Purpose                   |
| ----------- | --------------- | ------------------------- |
| Opportunity | `OpportunityId` | Links Case TO Opportunity |

**Key Principle:** Cases and Orders link TO Opportunity, not vice versa.

---

## Opportunity Matching Rules

### When to Find vs Create Opportunity

```
┌─────────────────────────────────────────────────────────────────────────┐
│                     OPPORTUNITY MATCHING RULES                           │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  INTERNET ELIGIBILITY REQUEST:                                           │
│  ─────────────────────────────                                           │
│  1. Check if Account already has eligibility status set                  │
│     └─ If already "Eligible" or "Not Eligible" → Don't create Opp       │
│                                                                          │
│  2. Find existing Opportunity:                                           │
│     └─ WHERE AccountId = ?                                               │
│       AND CommodityType IN ('Personal SonixNet Home Internet',          │
│                              'Corporate SonixNet Home Internet')         │
│       AND StageName = 'Introduction'                                     │
│       AND IsClosed = false                                               │
│                                                                          │
│  3. If found → Use existing, create Case linked to it                    │
│     If not → Create new Opportunity, then create Case linked to it       │
│                                                                          │
│  ORDER PLACEMENT:                                                        │
│  ─────────────────                                                       │
│  1. Find existing Opportunity:                                           │
│     └─ WHERE AccountId = ?                                               │
│       AND CommodityType = ? (based on order type)                        │
│       AND StageName IN ('Introduction', 'Ready')                         │
│       AND IsClosed = false                                               │
│                                                                          │
│  2. If found → Use existing, update stage to 'Post Processing'           │
│     If not → Create new Opportunity with stage 'Post Processing'         │
│                                                                          │
│  3. Create Order with Order.OpportunityId = Opportunity.Id               │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘
```

### Stage Matching by Flow

| Flow                 | Match Stages        | If Not Found                    |
| -------------------- | ------------------- | ------------------------------- |
| Internet Eligibility | Introduction only   | Create with Introduction        |
| Order Placement      | Introduction, Ready | Create with Post Processing     |
| Provisioning         | Post Processing     | Error (should exist from order) |
| Cancellation         | Active              | Error (service must be active)  |

---

## Internet Eligibility Flow

### Complete Flow Diagram

```
┌─────────────────────────────────────────────────────────────────────────┐
│                     INTERNET ELIGIBILITY FLOW                            │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  1. CUSTOMER ENTERS ADDRESS                                              │
│     └─ Portal: POST /api/services/internet/eligibility-request           │
│        Portal UI: shows confirmation at /account/services/internet/request-submitted │
│                                                                          │
│  2. CHECK IF ELIGIBILITY ALREADY KNOWN                                   │
│     └─ Query: SELECT Internet_Eligibility__c FROM Account                │
│     └─ If already set → Return cached result, no action needed           │
│                                                                          │
│  3. FIND OR CREATE OPPORTUNITY                                           │
│     ┌─────────────────────────────────────────────────────────────────┐ │
│     │ Query:                                                          │ │
│     │   SELECT Id FROM Opportunity                                    │ │
│     │   WHERE AccountId = ?                                           │ │
│     │     AND CommodityType IN ('Personal SonixNet Home Internet',    │ │
│     │                           'Corporate SonixNet Home Internet')   │ │
│     │     AND StageName = 'Introduction'                              │ │
│     │     AND IsClosed = false                                        │ │
│     │                                                                 │ │
│     │ If found → Use existing                                         │ │
│     │ If not → Create new:                                            │ │
│     │   - Stage: Introduction                                         │ │
│     │   - CommodityType: Personal SonixNet Home Internet              │ │
│     │   - Opportunity_Source__c: Portal - Internet Eligibility Request│ │
│     │   - Application_Stage__c: INTRO-1                               │ │
│     └─────────────────────────────────────────────────────────────────┘ │
│                                                                          │
│  4. CREATE CASE (linked to Opportunity)                                  │
│     ┌─────────────────────────────────────────────────────────────────┐ │
│     │ Case.Type = "Eligibility Check"                                 │ │
│     │ Case.AccountId = customer's account                             │ │
│     │ Case.OpportunityId = opportunity from step 3 ←── THE LINK       │ │
│     │ Case.Subject = "Internet Eligibility - {Address}"               │ │
│     │ Case.Description = address details, postal code, etc.           │ │
│     │ Case.Status = "New"                                             │ │
│     └─────────────────────────────────────────────────────────────────┘ │
│                                                                          │
│  5. UPDATE ACCOUNT                                                       │
│     └─ Internet_Eligibility_Status__c = "Pending"                        │
│     └─ Internet_Eligibility_Request_Date_Time__c = now()                 │
│                                                                          │
│  6. CS PROCESSES CASE                                                    │
│     └─ Checks with NTT / provider                                        │
│     └─ Updates Account:                                                  │
│         - Internet_Eligibility__c = result                               │
│         - Internet_Eligibility_Status__c = "Checked"                     │
│         - Internet_Eligibility_Checked_Date_Time__c = now()              │
│     └─ Opportunity stages are not automatically moved to "Ready" by      │
│        the eligibility check. CS updates stages later as part of the     │
│        order review / lifecycle workflow.                                │
│                                                                          │
│  7. PORTAL DETECTS CHANGE (via CDC or polling)                           │
│     └─ Shows eligibility result to customer                              │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘
```

### What Customer Sees

| Account Field State          | Portal Shows                           |
| ---------------------------- | -------------------------------------- |
| Status = null                | Address input form                     |
| Status = "Pending"           | "Checking your address..." spinner     |
| Eligibility = eligible value | Plan selection enabled                 |
| Eligibility = not eligible   | "Sorry, service not available" message |

---

## ID Verification Flow

### Complete Flow Diagram

```
┌─────────────────────────────────────────────────────────────────────────┐
│                     ID VERIFICATION (eKYC) FLOW                          │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  WHEN: During checkout registration or before order placement            │
│                                                                          │
│  1. CHECK IF ALREADY VERIFIED                                            │
│     └─ Query: SELECT Id_Verification_Status__c FROM Account              │
│     └─ If "Verified" → Skip verification, proceed to checkout            │
│                                                                          │
│  2. CUSTOMER UPLOADS ID DOCUMENTS                                        │
│     └─ Portal: POST /api/verification/residence-card                     │
│     └─ BFF uploads file to Salesforce Files (ContentVersion)             │
│                                                                          │
│  3. UPDATE ACCOUNT                                                       │
│     └─ Id_Verification_Status__c = "Submitted" (portal maps to pending)  │
│     └─ Id_Verification_Submitted_Date_Time__c = now()                    │
│                                                                          │
│  4. CS REVIEWS IN SALESFORCE                                              │
│     └─ Id_Verification_Status__c = "Verified" or "Rejected"              │
│     └─ Id_Verification_Verified_Date_Time__c = now() (on verify)          │
│     └─ Id_Verification_Rejection_Message__c = reason (on reject)          │
│                                                                          │
│  5. CUSTOMER RESUBMITS IF NEEDED                                          │
│     └─ Portal shows feedback + upload UI                                 │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘
```

### What Customer Sees

| Account Field State | Portal Shows                    |
| ------------------- | ------------------------------- |
| Status = null       | ID upload form                  |
| Status = "Pending"  | "Verifying your identity..."    |
| Status = "Verified" | Proceed to checkout enabled     |
| Status = "Rejected" | Error message + resubmit option |

---

## Order Placement Flow

```
┌─────────────────────────────────────────────────────────────────────────┐
│                     ORDER PLACEMENT FLOW                                 │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  PREREQUISITES:                                                          │
│  - Internet: Eligibility = Eligible (Account field)                      │
│  - SIM: ID Verification = Verified (Account field)                       │
│                                                                          │
│  1. CUSTOMER SUBMITS ORDER                                               │
│     └─ Portal: POST /api/orders                                          │
│                                                                          │
│  2. FIND OR CREATE OPPORTUNITY                                           │
│     └─ Query for existing (Introduction or Ready stage)                  │
│     └─ If found → Use existing                                           │
│     └─ If not → Create with stage 'Post Processing'                      │
│                                                                          │
│  3. CREATE SALESFORCE ORDER                                              │
│     └─ Order.OpportunityId = Opportunity.Id ←── THE LINK                 │
│     └─ Order.Status = "Pending Review"                                   │
│                                                                          │
│  4. UPDATE OPPORTUNITY                                                   │
│     └─ Stage = "Post Processing"                                         │
│                                                                          │
│  5. CUSTOMER SEES ORDER TRACKING                                         │
│     └─ "Your order is being processed"                                   │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘
```

---

## Service Activation Flow

```
┌─────────────────────────────────────────────────────────────────────────┐
│                     SERVICE ACTIVATION FLOW                              │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  1. AGENT APPROVES ORDER                                                 │
│     └─ Order.Status = "Approved" (triggers CDC)                          │
│                                                                          │
│  2. BFF PROVISIONS TO WHMCS                                              │
│     └─ Calls WHMCS AddOrder API                                          │
│     └─ Passes OpportunityId as custom field ←── WHMCS GETS OPP ID       │
│     └─ WHMCS returns serviceId                                           │
│                                                                          │
│  3. UPDATE OPPORTUNITY                                                   │
│     └─ WHMCS_Service_ID__c = serviceId ←── OPP GETS WHMCS ID            │
│     └─ Stage = "Active"                                                  │
│                                                                          │
│  4. BIDIRECTIONAL LINK COMPLETE                                          │
│     └─ Opportunity.WHMCS_Service_ID__c → WHMCS Service                   │
│     └─ WHMCS Service.OpportunityId → Opportunity (optional; helpful for ops/debugging) │
│                                                                          │
│  5. CUSTOMER SEES SERVICE PAGE                                           │
│     └─ Portal shows active services from WHMCS; lifecycle/cancellation tracking lives on the linked Salesforce Opportunity (via WHMCS_Service_ID__c) │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘
```

---

## Cancellation Flow

The cancellation process differs between Internet and SIM services:

### Internet Cancellation Flow

For Internet cancellation, we create a Case for CS workflow and update the Opportunity:

```
┌─────────────────────────────────────────────────────────────────────────┐
│                     INTERNET CANCELLATION FLOW                           │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  1. CUSTOMER SUBMITS CANCELLATION FORM                                   │
│     └─ Selects month (25th rule applies)                                 │
│     └─ Enters comments, alternative email (optional)                     │
│                                                                          │
│  2. CREATE CANCELLATION CASE                                             │
│     ┌─────────────────────────────────────────────────────────────────┐ │
│     │ Case.Type = "Cancellation Request"                              │ │
│     │ Case.AccountId = customer's account                             │ │
│     │ Case.Subject = "Cancellation Request - Internet ({month})"      │ │
│     │ Case.Description = ALL form data                                │ │
│     │ Case.OpportunityId = linked Opportunity (if found)              │ │
│     └─────────────────────────────────────────────────────────────────┘ │
│                                                                          │
│  3. IF OPPORTUNITY IS LINKED (via WHMCS_Service_ID__c)                   │
│     └─ Update Opportunity:                                               │
│         - Stage = "△Cancelling"                                          │
│         - ScheduledCancellationDateAndTime__c = end of month             │
│         - CancellationNotice__c = "有"                                   │
│         - LineReturn__c = "NotYet"                                       │
│                                                                          │
│  4. SEND CONFIRMATION EMAIL                                              │
│     └─ Customer receives confirmation with cancellation details          │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘
```

### SIM Cancellation Flow

For SIM cancellation, we call Freebit API, create a Case, and update the Opportunity:

```
┌─────────────────────────────────────────────────────────────────────────┐
│                     SIM CANCELLATION FLOW                                │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  1. CUSTOMER SUBMITS CANCELLATION FORM                                   │
│     └─ Selects month (25th rule applies)                                 │
│     └─ Sees minimum contract warning if applicable                       │
│                                                                          │
│  2. CALL FREEBIT PA02-04 API                                             │
│     └─ Cancel account with runDate = 1st of next month                   │
│     └─ Cancellation takes effect at end of selected month                │
│                                                                          │
│  3. CREATE CANCELLATION CASE (same as Internet)                          │
│     ┌─────────────────────────────────────────────────────────────────┐ │
│     │ Case.Type = "Cancellation Request"                              │ │
│     │ Case.AccountId = customer's account                             │ │
│     │ Case.Subject = "Cancellation Request - SIM ({month})"           │ │
│     │ Case.Description = ALL form data (SIM #, comments, etc.)        │ │
│     │ Case.OpportunityId = linked Opportunity (if found)              │ │
│     └─────────────────────────────────────────────────────────────────┘ │
│                                                                          │
│  4. IF OPPORTUNITY IS LINKED (via WHMCS_Service_ID__c)                   │
│     └─ Update Opportunity:                                               │
│         - Stage = "△Cancelling"                                          │
│         - SIMScheduledCancellationDateAndTime__c = end of month          │
│         - SIMCancellationNotice__c = "有"                                │
│                                                                          │
│  5. SEND CONFIRMATION EMAILS                                             │
│     └─ Admin notification with API results                               │
│     └─ Customer confirmation with cancellation details                   │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘
```

### The 25th Rule

```
TODAY'S DATE              AVAILABLE CANCELLATION MONTHS
─────────────────────────────────────────────────────────
Before 25th of month  →   Can select THIS month or later
On/After 25th         →   Must select NEXT month or later
```

### Cancellation Data Location

**Internet Cancellation:**

| Data              | Where It Goes | Why                      |
| ----------------- | ------------- | ------------------------ |
| Scheduled date    | Opportunity   | Lifecycle tracking       |
| Notice status     | Opportunity   | Lifecycle tracking       |
| Return status     | Opportunity   | Equipment tracking (ONU) |
| Customer comments | **Case**      | Details for CS           |
| WHMCS Service ID  | Case          | Service identification   |

**SIM Cancellation:**

| Data              | Where It Goes | Why                       |
| ----------------- | ------------- | ------------------------- |
| Scheduled date    | Opportunity   | Lifecycle tracking        |
| Notice status     | Opportunity   | Lifecycle tracking        |
| Customer comments | **Case**      | Same as Internet          |
| SIM #, Serial #   | Case          | Service identification    |
| WHMCS Service ID  | Case          | Helps CS identify service |

---

## Implementation Checklist

### Salesforce Admin Tasks

**Existing Fields (No Changes):**

- [x] Opportunity Stage picklist
- [x] CommodityType field
- [x] Application_Stage\_\_c
- [x] Account Internet eligibility fields
- [x] Account ID verification fields
- [x] Case.OpportunityId (standard lookup)
- [x] Order.OpportunityId (standard lookup)

**Internet Cancellation Fields (Existing):**

- [x] `CancellationNotice__c` - Internet cancellation notice status
- [x] `LineReturn__c` - Equipment return status
- [x] `ScheduledCancellationDateAndTime__c` - Internet cancellation date

**SIM Cancellation Fields (Existing):**

- [x] `SIMCancellationNotice__c` - SIM cancellation notice status
- [x] `SIMScheduledCancellationDateAndTime__c` - SIM cancellation date
- _Note: Customer comments stored on Case, same as Internet_

**Portal Integration Fields (Existing):**

- [x] `Opportunity_Source__c` picklist (tracks how Opportunity was created)
- [x] `WHMCS_Service_ID__c` number field (links WHMCS → Salesforce for cancellations)

### WHMCS Admin Tasks

- [ ] Create an `OpportunityId` custom field on Services/Hosting (optional but recommended for ops/debugging)
- [ ] Confirm whether your WHMCS expects `customfields[]` keys by **name** (`OpportunityId`) or by **numeric field id**, and configure accordingly

### BFF Development Tasks

**Completed:**

- [x] Domain types in `packages/domain/opportunity/`
- [x] Field map configuration
- [x] `SalesforceOpportunityService`
- [x] Cancellation deadline helpers (25th rule)
- [x] Eligibility request creates/reuses Opportunity (Stage `Introduction`) and links Case (`Case.OpportunityId`)
- [x] Order placement reuses Opportunity in `Introduction`/`Ready` (otherwise creates new `Post Processing`)
- [x] Fulfillment passes `OpportunityId` to WHMCS and links `WHMCS_Service_ID__c` back to the Opportunity
- [x] Cancellation Case creation + Opportunity cancellation field updates (Internet cancellations)

**Optional / Future:**

- [ ] Read `OpportunityId` back from WHMCS service custom fields (portal currently relies on Salesforce `WHMCS_Service_ID__c` for linking)

### Frontend Tasks

- [ ] Order tracking page (from Salesforce Order)
- [ ] Service page with cancellation status
- [x] Cancellation forms (Internet + SIM) with 25th deadline logic

---

## Related Documentation

- [Salesforce-WHMCS Mapping](./SALESFORCE-WHMCS-MAPPING-REFERENCE.md)
- [Order Provisioning](../orders/PORTAL-ORDERING-PROVISIONING.md)
