Assist_Design/docs/integrations/salesforce/opportunity-lifecycle.md
barsa a61c2dd68b Enhance Opportunity Management and Eligibility Handling
- Updated SalesforceOpportunityService to allow filtering by stages during opportunity retrieval, improving flexibility in eligibility checks.
- Integrated DistributedLockService into InternetCatalogService and OrderOrchestrator to prevent race conditions when creating or reusing opportunities.
- Refactored opportunity matching logic to ensure proper handling of stages during eligibility requests and order placements.
- Improved documentation to clarify the opportunity lifecycle and eligibility verification processes, ensuring better understanding for future development.
2025-12-23 16:44:45 +09:00

33 KiB

Opportunity Lifecycle Management Guide

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

Table of Contents

  1. Overview
  2. Existing Field Architecture
  3. Opportunity Matching Rules
  4. Internet Eligibility Flow
  5. ID Verification Flow
  6. Order Placement Flow
  7. Service Activation Flow
  8. Cancellation Flow
  9. 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/catalog/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/submit                             │
│     └─ eKYC service processes documents                                  │
│                                                                          │
│  3. UPDATE ACCOUNT                                                       │
│     └─ Id_Verification_Status__c = "Pending"                             │
│     └─ Id_Verification_Submitted_Date_Time__c = now()                    │
│                                                                          │
│  4. IF eKYC AUTO-APPROVED                                                │
│     └─ Id_Verification_Status__c = "Verified"                            │
│     └─ Id_Verification_Verified_Date_Time__c = now()                     │
│     └─ Customer can proceed to order immediately                         │
│                                                                          │
│  5. IF MANUAL REVIEW NEEDED                                              │
│     └─ Create Case for CS review                                         │
│     └─ Case.Type = "ID Verification"                                     │
│     └─ Case.OpportunityId = linked Opportunity (if exists)               │
│     └─ CS reviews and updates Account                                    │
│                                                                          │
│  6. IF REJECTED                                                          │
│     └─ Id_Verification_Status__c = "Rejected"                            │
│     └─ Id_Verification_Rejection_Message__c = reason                     │
│     └─ Customer must resubmit                                            │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

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):

  • Opportunity Stage picklist
  • CommodityType field
  • Application_Stage__c
  • CancellationNotice__c
  • LineReturn__c
  • ScheduledCancellationDateAndTime__c
  • Account Internet eligibility fields
  • Account ID verification fields
  • Case.OpportunityId (standard lookup)
  • 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:

  • 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 new Post Processing)
  • Fulfillment passes OpportunityId to WHMCS and links WHMCS_Service_ID__c back to the Opportunity
  • 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
  • Cancellation forms (Internet + SIM) with 25th deadline logic