- Updated opportunity field mappings to replace deprecated fields with new ones for SIM and Internet cancellations, enhancing clarity and consistency. - Introduced separate data structures for Internet and SIM cancellation data, improving type safety and validation. - Refactored SalesforceOpportunityService to handle updates for both Internet and SIM cancellations, ensuring accurate data handling. - Enhanced cancellation query fields to support new SIM cancellation requirements, improving the overall cancellation process. - Cleaned up the portal integration to reflect changes in opportunity source fields, promoting better data integrity and tracking.
37 KiB
Opportunity Lifecycle Management Guide
This guide documents the Salesforce Opportunity integration for service lifecycle tracking.
Table of Contents
- Overview
- Existing Field Architecture
- Opportunity Matching Rules
- Internet Eligibility Flow
- ID Verification Flow
- Order Placement Flow
- Service Activation Flow
- Cancellation Flow
- 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):
- Opportunity Stage picklist
- CommodityType field
- Application_Stage__c
- Account Internet eligibility fields
- Account ID verification fields
- Case.OpportunityId (standard lookup)
- Order.OpportunityId (standard lookup)
Internet Cancellation Fields (Existing):
CancellationNotice__c- Internet cancellation notice statusLineReturn__c- Equipment return statusScheduledCancellationDateAndTime__c- Internet cancellation date
SIM Cancellation Fields (Existing):
SIMCancellationNotice__c- SIM cancellation notice statusSIMScheduledCancellationDateAndTime__c- SIM cancellation date- Note: Customer comments stored on Case, same as Internet
Portal Integration Fields (Existing):
Opportunity_Source__cpicklist (tracks how Opportunity was created)WHMCS_Service_ID__cnumber field (links WHMCS → Salesforce for cancellations)
WHMCS Admin Tasks
- Create an
OpportunityIdcustom 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:
- Domain types in
packages/domain/opportunity/ - Field map configuration
SalesforceOpportunityService- Cancellation deadline helpers (25th rule)
- Eligibility request creates/reuses Opportunity (Stage
Introduction) and links Case (Case.OpportunityId) - Order placement reuses Opportunity in
Introduction/Ready(otherwise creates newPost Processing) - Fulfillment passes
OpportunityIdto WHMCS and linksWHMCS_Service_ID__cback to the Opportunity - Cancellation Case creation + Opportunity cancellation field updates (Internet cancellations)
Optional / Future:
- Read
OpportunityIdback from WHMCS service custom fields (portal currently relies on SalesforceWHMCS_Service_ID__cfor linking)
Frontend Tasks
- Order tracking page (from Salesforce Order)
- Service page with cancellation status
- Cancellation forms (Internet + SIM) with 25th deadline logic