# 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 | `Internet_Eligibility_Notes__c` | Agent notes | | Case ID | `Internet_Eligibility_Case_Id__c` | Linked Case for request | **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) | 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) | | Cancellation Notice | `CancellationNotice__c` | 有, 未, 不要, 移転 | | Scheduled Cancellation | `ScheduledCancellationDateAndTime__c` | End of cancellation month | | Line Return Status | `LineReturn__c` | NotYet, SentKit, Returned, etc. | ### New Opportunity Fields (To Create) | Field | API Name | Type | Purpose | | ---------------- | --------------------- | -------- | -------------------------------- | | Portal Source | `Portal_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 │ │ │ │ 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 │ │ │ │ - Portal_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() │ │ └─ Internet_Eligibility_Case_Id__c = Case.Id │ │ │ │ 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() │ │ └─ Updates Opportunity: │ │ - Eligible → Stage: Ready │ │ - Not Eligible → Stage: Void │ │ │ │ 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 ### Always Create Case For **every** cancellation request, create a Case (notification to CS): ``` ┌─────────────────────────────────────────────────────────────────────────┐ │ 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 - {Product}" │ │ │ │ Case.Description = ALL form data: │ │ │ │ - WHMCS Service ID │ │ │ │ - Cancellation month │ │ │ │ - Alternative email (if provided) │ │ │ │ - Customer comments (if provided) │ │ │ │ Case.OpportunityId = linked Opportunity (if found) │ │ │ │ Case.Status = "New" │ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ │ 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. IF NOT LINKED (Legacy) │ │ └─ Case contains all info for CS to process │ │ └─ CS will manually find and update correct Opportunity │ │ │ │ 5. CUSTOMER SEES │ │ └─ If linked: Cancellation status from Opportunity │ │ └─ If not linked: "Request received, we'll confirm by email" │ │ │ └─────────────────────────────────────────────────────────────────────────┘ ``` ### 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 | Data | Where It Goes | Why | | ----------------- | -------------------- | ------------------------- | | Scheduled date | Opportunity | Lifecycle tracking | | Notice status | Opportunity | Lifecycle tracking | | Return status | Opportunity | Lifecycle tracking | | Customer comments | **Case** | Not needed on Opp | | Alternative email | **Case** | Not needed on Opp | | WHMCS Service ID | Case (for reference) | 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] CancellationNotice\_\_c - [x] LineReturn\_\_c - [x] ScheduledCancellationDateAndTime\_\_c - [x] Account Internet eligibility fields - [x] Account ID verification fields - [x] Case.OpportunityId (standard lookup) - [x] Order.OpportunityId (standard lookup) **Opportunity Fields Required (Portal writes these):** - [ ] `Portal_Source__c` picklist (used to track how the Opportunity was created) - [ ] `WHMCS_Service_ID__c` number field (used to link WHMCS service → Salesforce Opportunity 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)