# Portal – Data Model & Mappings This document consolidates all required/used fields across Salesforce and WHMCS, plus how the portal consumes them. ## Quick Reference: Required Fields Summary ### Product2 (7 core + service-specific fields) **Core**: `StockKeepingUnit`, `Product2Categories1__c`, `Portal_Catalog__c`, `Portal_Accessible__c`, `Item_Class__c`, `Billing_Cycle__c`, `WH_Product_ID__c`, `WH_Product_Name__c` **Internet**: `Internet_Plan_Tier__c`, `Internet_Offering_Type__c`, `Internet_Monthly_Price__c` **SIM**: `SIM_Data_Size__c`, `SIM_Plan_Type__c`, `SIM_Has_Family_Discount__c` **Edge Cases**: `Is_Default__c`, `Display_Order__c`, `Auto_Add__c`, `VPN_Region__c` *Note: For complete product setup details, see `SALESFORCE-PRODUCTS.md`* ### Order (6 core + service-specific independent fields) **Core**: `AccountId`, `EffectiveDate`, `Status`, `Pricebook2Id`, `Order_Type__c` **Activation**: `Activation_Type__c`, `Activation_Scheduled_At__c`, `Activation_Status__c` **Internet**: `Internet_Plan_Tier__c`, `Installation_Type__c`, `Weekend_Install__c`, `Access_Mode__c`, `Hikari_Denwa__c` **VPN**: `VPN_Region__c` **SIM**: `SIM_Type__c`, `EID__c`, `SIM_Voice_Mail__c`, `SIM_Call_Waiting__c` + MNP fields **WHMCS**: `WHMCS_Order_ID__c` ### OrderItem (2 core + 2 integration fields) **Core**: `OrderId`, `PricebookEntryId`, `Quantity`, `UnitPrice` **WHMCS**: `WHMCS_Service_ID__c`, `Billing_Cycle__c` ## Order with Add-ons Example When a customer orders "Internet Gold + Hikari Denwa + Weekend Installation": ```sql -- Order Header Order_Type__c: "Internet" Internet_Plan_Tier__c: "Gold" Installation_Type__c: "Single" Weekend_Install__c: true -- Synced from add-on OrderItem -- OrderItems (5 separate items) 1. Internet Gold Service → SKU: "INTERNET-GOLD" → WH: 188 (monthly) 2. Internet Installation → SKU: "INTERNET-INSTALL-SINGLE" → WH: 242 (one-time) 3. Weekend Installation Fee → SKU: "INTERNET-INSTALL-WEEKEND" → WH: 245 (one-time ¥3000) 4. Hikari Denwa Service → SKU: "INTERNET-ADDON-HOME-PHONE" → WH: 246 (monthly ¥450) 5. Hikari Denwa Installation → SKU: "INTERNET-ADDON-DENWA-INSTALL" → WH: 247 (one-time) ``` **Complete Product Structure Needed:** ```sql -- Main Services (Portal_Catalog = true) "Internet Gold Plan" → Item_Class: "Service" → Monthly billing "Single Installation" → Item_Class: "Installation" → One-time billing -- Add-on Services (Portal_Catalog = false, shown as checkboxes) "Hikari Denwa (Home Phone)" → Item_Class: "Add-on" → Monthly billing "Weekend Installation" → Item_Class: "Add-on" → One-time billing "Hikari Denwa Installation" → Item_Class: "Add-on" → One-time billing (auto-added when Denwa selected) ``` **Key Points:** - **5 OrderItems total** - each service/fee is separate - **Automatic dependencies** - selecting Hikari Denwa auto-adds its installation fee - **Mixed billing cycles** - services are monthly, installations are one-time - **Independent WHMCS products** - each OrderItem maps to different WHMCS Product ID ## Object inventory (what's standard vs custom) - Salesforce standard - `Product2` (catalog) - `Pricebook2` / `PricebookEntry` (pricing) - `Order` (header) - `OrderItem` (line) - `Account` (customer profile and eligibility) - WHMCS - Use native User, Client, Order, Invoice, Product (Service). No custom tables. ## Salesforce fields (by object) ### Account (eligibility and mapping) - Eligibility (used for personalized Internet catalog) - `Internet_Eligibility__c` (Picklist) - Values (Label = API Name): - `Home 1G` - `Home 10G` - `Apartment 1G` - `Apartment 100M` - Default behavior: if the field is missing/blank/invalid, the portal treats eligibility as `Home 1G`. - Environment variable mapping (so code doesn’t hardcode field API names): - `ELIGIBILITY_INTERNET_FIELD` (default: `Internet_Eligibility__c`) - Mapping key - `SF_Account_No__c` (Text) used to link portal user ↔ Account (via WHMCS link/signup) ### Product2 Field Reference *For complete Product2 field definitions, custom fields, and edge case handling, see `SALESFORCE-PRODUCTS.md`* #### Core Fields Summary - `StockKeepingUnit` - Product identifier (detailed format: `INTERNET-SILVER-APT-1G`) - `Product2Categories1__c` - "Internet", "SIM", "VPN", "Other" - `Portal_Catalog__c` - Show in main catalog (GET /catalog) - `Portal_Accessible__c` - Can be used in orders/OrderItems - `Item_Class__c` - "Service", "Installation", "Add-on", "Activation" - `WH_Product_ID__c` - WHMCS Product ID #### Pricing (from PricebookEntry) ```sql -- Pricing comes from PricebookEntry.UnitPrice where Pricebook2Id = '01sTL000008eLVlYAM' (Portal Price Book) -- Portal Price Book ID is configurable via PORTAL_PRICEBOOK_ID environment variable UnitPrice Currency -- Display price (¥4800, ¥4900, ¥5300) ``` ## Add-on Products Add-ons are separate products that customers can select alongside main services. They use the existing product structure with `Item_Class__c = "Add-on"`, `Portal_Catalog__c = false` (hidden from catalog), and `Portal_Accessible__c = true` (can be ordered). ### How Add-ons Work: 1. **Customer Selection**: Optional add-ons appear as checkboxes (e.g., Hikari Denwa) 2. **Frontend Logic**: Auto-add dependencies (e.g., Hikari Denwa adds installation fee) 3. **Salesforce Logic**: Auto-detect conditions and add OrderItems (e.g., weekend installation dates) 4. **Order Creation**: Each selected/detected add-on creates a separate OrderItem 5. **Provisioning**: Each add-on provisions as separate WHMCS product 6. **Billing**: Add-ons have their own pricing and billing cycles ### Internet Add-ons: - **Weekend Installation**: Salesforce auto-adds (¥3000) - `INTERNET-INSTALL-WEEKEND` (when installation date is weekend) - **Hikari Denwa Service**: Customer checkbox (¥450/month) - `INTERNET-ADDON-HOME-PHONE` - **Hikari Denwa Installation**: Frontend auto-adds (one-time) - `INTERNET-ADDON-DENWA-INSTALL` ### SIM Add-ons: - **Voice Mail**: Monthly service - `SIM-ADDON-VOICE-MAIL` - **Call Waiting**: Monthly service - `SIM-ADDON-CALL-WAITING` - **Unlimited Calling**: Monthly service - `SIM-ADDON-UNLIMITED-CALLING` **Product Structure Examples:** *For complete product definitions with all SKUs, fields, and WHMCS IDs, see `SALESFORCE-PRODUCTS.md`* ```sql -- Main Service Plans (Portal_Catalog = true, Portal_Accessible = true) "Internet Silver Plan (Apartment 1G)" → SKU: "INTERNET-SILVER-APT-1G" → WH_Product_ID__c: 184 "Internet Gold Plan (Apartment 1G)" → SKU: "INTERNET-GOLD-APT-1G" → WH_Product_ID__c: 185 "Internet Platinum Plan (Apartment 1G)" → SKU: "INTERNET-PLATINUM-APT-1G" → WH_Product_ID__c: 186 -- Add-on Services (Portal_Catalog = false, Portal_Accessible = true) "Weekend Installation" → SKU: "INTERNET-INSTALL-WEEKEND" → WH_Product_ID__c: 245 "Hikari Denwa (Home Phone)" → SKU: "INTERNET-ADDON-HOME-PHONE" → WH_Product_ID__c: 246 -- Customer sees clear messaging: Silver: Standard pricing, customer configures access mode Gold: Premium pricing, operator reviews and may adjust configuration Platinum: Premium pricing + additional fees, operator reviews and may adjust configuration ``` ### Order (clean field structure) #### Core Fields (Required for all orders) ```sql -- Salesforce Standard Fields AccountId Lookup(Account) -- Customer account EffectiveDate Date -- Order date Status Picklist -- "Pending Review", "Approved", "Completed", "Cancelled" Pricebook2Id Lookup(Pricebook2) -- Portal pricebook OpportunityId Lookup(Opportunity) -- Optional, created by BFF -- Order Classification Order_Type__c Picklist -- "Internet", "SIM", "VPN", "Other" ``` #### Activation Fields (All order types) ```sql Activation_Type__c Picklist -- "Immediate", "Scheduled" Activation_Scheduled_At__c Datetime -- When Status = Scheduled Activation_Status__c Picklist -- "Not Started", "Activating", "Activated", "Failed" ``` #### Internet Order Fields (Independent fields synced with OrderItems) ```sql -- Customer/Operator Selections (Independent fields) Internet_Plan_Tier__c Picklist -- "Silver", "Gold", "Platinum" (syncs with OrderItems) Installation_Type__c Picklist -- "Single", "12-Month", "24-Month" (syncs with OrderItems) Weekend_Install__c Checkbox -- Weekend installation add-on (syncs with OrderItems) Access_Mode__c Picklist -- "IPoE-BYOR", "IPoE-HGW", "PPPoE" (operator configures) -- Add-on Selections (Customer choices, syncs with OrderItems) Hikari_Denwa__c Checkbox -- Customer selected Hikari Denwa service ``` #### VPN Order Fields (Customer selections) ```sql -- Customer Selections VPN_Region__c Picklist -- "USA-SF", "UK-London" (customer chooses region) ``` #### SIM Order Fields (Customer choices + MNP data) ```sql -- SIM Configuration (plan type and data size come from selected Product2) SIM_Type__c Picklist -- "eSIM", "Physical SIM" (customer choice) EID__c Text(255) -- Required when SIM_Type__c = "eSIM" -- Add-on Selections (Customer choices, syncs with OrderItems) SIM_Voice_Mail__c Checkbox -- Customer selected Voice Mail add-on SIM_Call_Waiting__c Checkbox -- Customer selected Call Waiting add-on -- MNP/Porting (when MNP_Application__c = true) MNP_Application__c Checkbox -- Customer wants to port number MNP_Reservation_Number__c Text(10) -- 10 digits numeric MNP_Expiry_Date__c Date -- MNP expiry date MNP_Phone_Number__c Text(11) -- 11 digits, no hyphens MVNO_Account_Number__c Text(255) -- Optional Porting_LastName__c Text(255) -- Kanji/Alphabet Porting_FirstName__c Text(255) -- Kanji/Alphabet Porting_LastName_Katakana__c Text(255) -- Katakana Porting_FirstName_Katakana__c Text(255) -- Katakana Porting_Gender__c Picklist -- "Male", "Female", "Corporate/Other" Porting_DateOfBirth__c Date -- YYYY/MM/DD ``` #### WHMCS Integration Fields ```sql WHMCS_Order_ID__c Number(18,0) -- Set after provisioning ``` #### Billing/Shipping Snapshot (Set on create, not synced) ```sql -- Billing Address BillToContactId Lookup(Contact) BillToStreet Text(255) BillToCity Text(40) BillToState Text(80) BillToPostalCode Text(20) BillToCountry Text(80) -- Shipping Address ShipToContactId Lookup(Contact) ShipToStreet Text(255) ShipToCity Text(40) ShipToState Text(80) ShipToPostalCode Text(20) ShipToCountry Text(80) ``` ### OrderItem (clean field structure) #### Core Fields (Required for all order items) ```sql -- Salesforce Standard Fields OrderId Lookup(Order) -- Parent order PricebookEntryId Lookup(PricebookEntry) -- Product + pricing Quantity Number(18,2) -- Usually 1 UnitPrice Currency -- Price at time of order ``` #### WHMCS Integration Fields ```sql -- Provisioning WHMCS_Service_ID__c Number(18,0) -- Set after provisioning Billing_Cycle__c Picklist -- "Monthly", "Annually", "One-time" ``` **Note**: `WH_Product_ID__c` is stored on Product2, but operator can override it on OrderItem during review for Gold/Platinum plans. ## Salesforce Setup Checklist ### Custom Fields to Create #### Product2 Object ```sql -- Core Fields (All Products) StockKeepingUnit Text(255) External ID, Unique (standard Salesforce field) Product2Categories1__c Picklist Values: Internet, SIM, VPN, Other Portal_Catalog__c Checkbox Default: false Portal_Accessible__c Checkbox Default: true Item_Class__c Picklist Values: Service, Installation, Add-on, Activation WH_Product_ID__c Number(18,0) -- Internet Fields Internet_Plan_Tier__c Picklist Values: Silver, Gold, Platinum Internet_Offering_Type__c Picklist Values: Home 1G, Home 10G, Apartment 1G, Apartment 100M Internet_Monthly_Price__c Currency -- SIM Fields SIM_Data_Size__c Text(50) SIM_Plan_Type__c Picklist Values: DataOnly, DataSmsVoice, VoiceOnly SIM_Has_Family_Discount__c Checkbox Default: false -- VPN Fields ``` #### Order Object ```sql -- Core Fields Order_Type__c Picklist Values: Internet, SIM, VPN, Other -- Activation Fields Activation_Type__c Picklist Values: Immediate, Scheduled Activation_Scheduled_At__c Datetime Activation_Status__c Picklist Values: Not Started, Activating, Activated, Failed -- Internet Fields (Independent fields synced with OrderItems) Internet_Plan_Tier__c Picklist Values: Silver, Gold, Platinum Installation_Type__c Picklist Values: Single, 12-Month, 24-Month Weekend_Install__c Checkbox Weekend installation add-on Access_Mode__c Picklist Values: IPoE-BYOR, IPoE-HGW, PPPoE (operator sets) -- VPN Fields (Independent fields) VPN_Region__c Picklist Values: USA-SF, UK-London (customer chooses) -- SIM Fields (Customer choices only - plan details come from Product2) SIM_Type__c Picklist Values: eSIM, Physical SIM (customer chooses format) EID__c Text(255) Required for eSIM activation -- MNP Fields (when applicable) MNP_Application__c Checkbox MNP_Reservation_Number__c Text(10) MNP_Expiry_Date__c Date MNP_Phone_Number__c Text(11) MVNO_Account_Number__c Text(255) Porting_LastName__c Text(255) Porting_FirstName__c Text(255) Porting_LastName_Katakana__c Text(255) Porting_FirstName_Katakana__c Text(255) Porting_Gender__c Picklist Values: Male, Female, Corporate/Other Porting_DateOfBirth__c Date -- WHMCS Integration WHMCS_Order_ID__c Number(18,0) ``` #### OrderItem Object ```sql -- WHMCS Integration Fields WHMCS_Service_ID__c Number(18,0) Set after provisioning Billing_Cycle__c Picklist Values: Monthly, Annually, One-time ``` #### Account Object ```sql -- Portal Integration Fields Internet_Eligibility__c Picklist Values: Home 1G, Home 10G, Apartment 1G, Apartment 100M SF_Account_No__c Text(255) External ID, Unique (Customer Number) ``` ## Salesforce Sync Flow: Order Fields ↔ OrderItems ### Overview Independent fields on Order are synced with OrderItems using Salesforce Flow. When operators change Order fields, Flow automatically adds/removes/updates OrderItems. When OrderItems change, Flow updates Order fields. ### Weekend Installation Auto-Detection ```apex // Trigger: After Insert, After Update on Order // When: Installation_Scheduled_Date__c changes Date installDate = order.Installation_Scheduled_Date__c; String dayOfWeek = installDate.format('E'); // 'Sat' or 'Sun' for weekends if (dayOfWeek == 'Sat' || dayOfWeek == 'Sun') { // Add weekend installation fee if not exists List existing = [SELECT Id FROM OrderItem WHERE OrderId = :order.Id AND Product2.SKU__c = 'INTERNET-INSTALL-WEEKEND']; if (existing.isEmpty()) { Product2 product = [SELECT Id FROM Product2 WHERE SKU__c = 'INTERNET-INSTALL-WEEKEND']; PricebookEntry pbe = [SELECT Id, UnitPrice FROM PricebookEntry WHERE Product2Id = :product.Id AND Pricebook2.Name = 'Portal']; INSERT new OrderItem( OrderId = order.Id, PricebookEntryId = pbe.Id, Quantity = 1, UnitPrice = pbe.UnitPrice ); order.Weekend_Install__c = true; UPDATE order; } } else { // Remove weekend fee if date changed to weekday DELETE [SELECT Id FROM OrderItem WHERE OrderId = :order.Id AND Product2.SKU__c = 'INTERNET-INSTALL-WEEKEND']; order.Weekend_Install__c = false; UPDATE order; } ``` ### Flow 1: Order Fields → OrderItems (When operator changes Order fields) #### Internet Plan Tier Change ```apex // Trigger: After Update on Order // When: Internet_Plan_Tier__c changes // 1. Remove old Internet service OrderItem DELETE [SELECT Id FROM OrderItem WHERE OrderId = :order.Id AND Product2.Product2Categories1__c = 'Internet' AND Product2.Item_Class__c = 'Service']; // 2. Add new Internet service OrderItem String newSku = 'INTERNET-' + order.Internet_Plan_Tier__c.toUpperCase(); Product2 newProduct = [SELECT Id FROM Product2 WHERE SKU__c = :newSku LIMIT 1]; PricebookEntry pbe = [SELECT Id, UnitPrice FROM PricebookEntry WHERE Product2Id = :newProduct.Id AND Pricebook2.Name = 'Portal' LIMIT 1]; OrderItem newItem = new OrderItem( OrderId = order.Id, PricebookEntryId = pbe.Id, Quantity = 1, UnitPrice = pbe.UnitPrice ); INSERT newItem; ``` #### Installation Type Change ```apex // When: Installation_Type__c changes // 1. Remove old installation OrderItem DELETE [SELECT Id FROM OrderItem WHERE OrderId = :order.Id AND Product2.Product2Categories1__c = 'Internet' AND Product2.Item_Class__c = 'Install']; // 2. Add new installation OrderItem if not null if (order.Installation_Type__c != null) { String installSku = 'INTERNET-INSTALL-' + (order.Installation_Type__c == 'Single' ? 'SINGLE' : order.Installation_Type__c == '12-Month' ? '12M' : '24M'); // Create new installation OrderItem with installSku } ``` #### Weekend Install Toggle ```apex // When: Weekend_Install__c changes if (order.Weekend_Install__c == true && oldOrder.Weekend_Install__c == false) { // Add weekend installation OrderItem // SKU: 'INTERNET-INSTALL-WEEKEND' } else if (order.Weekend_Install__c == false && oldOrder.Weekend_Install__c == true) { // Remove weekend installation OrderItem DELETE [SELECT Id FROM OrderItem WHERE OrderId = :order.Id AND Product2.SKU__c = 'INTERNET-INSTALL-WEEKEND']; } ``` #### Add-on Field Changes ```apex // When: Hikari_Denwa__c changes if (order.Hikari_Denwa__c == true && oldOrder.Hikari_Denwa__c == false) { // Add Hikari Denwa service and installation OrderItems INSERT new OrderItem(Product2.SKU__c = 'INTERNET-ADDON-HOME-PHONE'); INSERT new OrderItem(Product2.SKU__c = 'INTERNET-ADDON-DENWA-INSTALL'); } else if (order.Hikari_Denwa__c == false && oldOrder.Hikari_Denwa__c == true) { // Remove Hikari Denwa OrderItems DELETE [SELECT Id FROM OrderItem WHERE OrderId = :order.Id AND Product2.SKU__c IN ('INTERNET-ADDON-HOME-PHONE', 'INTERNET-ADDON-DENWA-INSTALL')]; } // When: SIM add-on fields change if (order.SIM_Voice_Mail__c != oldOrder.SIM_Voice_Mail__c) { if (order.SIM_Voice_Mail__c) { INSERT new OrderItem(Product2.SKU__c = 'SIM-ADDON-VOICE-MAIL'); } else { DELETE [SELECT Id FROM OrderItem WHERE OrderId = :order.Id AND Product2.SKU__c = 'SIM-ADDON-VOICE-MAIL']; } } // Similar logic for SIM_Call_Waiting__c ``` ### Flow 2: OrderItems → Order Fields (When OrderItems change) #### Sync Order Fields from OrderItems ```apex // Trigger: After Insert, After Update, After Delete on OrderItem // Purpose: Keep Order fields in sync with OrderItems // Get all OrderItems for this Order List items = [SELECT Product2.Internet_Plan_Tier__c, Product2.SKU__c, Product2.Item_Class__c, Product2.Product2Categories1__c FROM OrderItem WHERE OrderId = :orderId]; Order orderToUpdate = new Order(Id = orderId); // Sync Internet fields for (OrderItem item : items) { if (item.Product2.Product2Categories1__c == 'Internet' && item.Product2.Item_Class__c == 'Service') { orderToUpdate.Internet_Plan_Tier__c = item.Product2.Internet_Plan_Tier__c; } if (item.Product2.Product2Categories1__c == 'Internet' && item.Product2.Item_Class__c == 'Installation') { if (item.Product2.SKU__c.contains('SINGLE')) orderToUpdate.Installation_Type__c = 'Single'; else if (item.Product2.SKU__c.contains('12M')) orderToUpdate.Installation_Type__c = '12-Month'; else if (item.Product2.SKU__c.contains('24M')) orderToUpdate.Installation_Type__c = '24-Month'; } if (item.Product2.SKU__c == 'INTERNET-INSTALL-WEEKEND') { orderToUpdate.Weekend_Install__c = true; } if (item.Product2.SKU__c == 'INTERNET-ADDON-HOME-PHONE') { orderToUpdate.Hikari_Denwa__c = true; } // SIM add-on sync if (item.Product2.SKU__c == 'SIM-ADDON-VOICE-MAIL') { orderToUpdate.SIM_Voice_Mail__c = true; } if (item.Product2.SKU__c == 'SIM-ADDON-CALL-WAITING') { orderToUpdate.SIM_Call_Waiting__c = true; } // VPN fields: Only VPN_Region__c is stored on Order (customer selection) } // Set defaults if no items found if (noInternetService) orderToUpdate.Internet_Plan_Tier__c = null; if (noInstallation) orderToUpdate.Installation_Type__c = null; if (noWeekendInstall) orderToUpdate.Weekend_Install__c = false; if (noHikariDenwa) orderToUpdate.Hikari_Denwa__c = false; if (noSIMVoiceMail) orderToUpdate.SIM_Voice_Mail__c = false; if (noSIMCallWaiting) orderToUpdate.SIM_Call_Waiting__c = false; UPDATE orderToUpdate; ``` ## WHMCS fields/IDs used - Users & Clients (created/linked at signup) - User: `firstname`, `lastname`, `email`, `password` - Client: `firstname`, `lastname`, `email`, optional `phonenumber`, `companyname` - Address: `address1`, `address2`, `city`, `state`, `postcode`, `country` - Custom field: `CustomerNumber` (maps to Salesforce Account `SF_Account_No__c`) - Link: User ↔ Client recorded in portal mapping table - Invoices & PayMethods - GetInvoices (by `clientid`) surfaced in portal - GetPayMethods used to gate checkout (must have a method on file) - SSO links used to open invoice/payment-methods UI in WHMCS - Client linking in portal - Portal mapping stores: `userId`, `whmcsClientId`, `sfAccountId` - Product IDs (pid) used in AddOrder: - Internet (by dwelling/speed): Home 1G SILV=181, GOLD=182, PLAT=183; APT 1G SILV=184, GOLD=185, PLAT=186; APT 100M SILV=187, GOLD=188, PLAT=189 - Installation: Single=242, 12M=243, 24M=244 - VPN: USA=33, UK=54, Activation=37 - SIM/eSIM: see mapping table above (e.g., Data-only 5GB=97, Data+Voice 10GB=216, Voice-only=142) ### AddOrder request fields (reference) - `clientid` (Number) – required – resolved from `Order.AccountId` via portal mapping to WHMCS client - `pid[]` (Array) – required – service and install Product2 → `WH_Product_ID__c` - `billingcycle` (derived) – sent to WHMCS based on the SKU type (Onetime for activation/install SKUs; Monthly for service SKUs) - `promocode` (Text) – optional - `notes` (Text) – should include `sfOrderId=` (templated via `Product2.WHMCS_Notes_Template__c`) - `noinvoice` (Boolean) – optional - `noemail` (Boolean) – optional ## Catalog requirements *For complete catalog architecture, API endpoints, and frontend implementation, see `PRODUCT-CATALOG-ARCHITECTURE.md`* - Pricebook: create a `Portal` price book; ensure active `PricebookEntry` for each visible Product2 - API dependencies: `GET /catalog` queries `Product2` fields: Id, Name, StockKeepingUnit, Product2Categories1__c, Portal_Catalog__c, Portal_Accessible__c - New endpoints: `/catalog/sim/activation-fees`, `/catalog/vpn/activation-fees`, `/catalog/sim/addons`, etc. - Personalized catalog: `GET /catalog/personalized` - Filters by consolidated offering: include Product2 where `Internet_Offering_Type__c` equals `Account.Internet_Eligibility__c` (fallback to Home 1G when Account field missing/invalid) ## Enablement & permissions (Salesforce) - Enable Products and Price Books in the org (Setup → Product Settings) - Integration user (Named Credential/Connected App user) minimum permissions: - Objects (Read): `Product2`, `Pricebook2`, `PricebookEntry`, `Order`, `OrderItem`, `Account` - Fields (Readable): - `Product2.StockKeepingUnit`, `Product2.Portal_Catalog__c`, `Product2.Portal_Accessible__c`, `Product2.Product2Categories1__c` - Optional display: `Product2.Portal_Description__c`, `Product2.Portal_Feature_Bullets__c`, `Product2.Portal_Hero_Image_URL__c`, `Product2.Portal_Tags__c`, `Product2.Portal_Sort_Order__c`, `Product2.Portal_Valid_From__c`, `Product2.Portal_Valid_Until__c` - Optional eligibility: `Product2.Portal_Eligibility_Dwelling__c`, `Product2.Portal_Eligibility_Speed__c`, `Product2.Portal_Eligibility_Region__c` - Account eligibility fields per env: dwelling/speed - Order custom fields listed above (Type/Activation/Internet/SIM/MNP) - OrderItem custom fields: `ConfigOptions_JSON__c` ## Portal field mapping reference (source-of-truth → target) Activation (Portal → Salesforce) - `activationType` (Immediate | Scheduled) → `Order.Activation_Type__c` - `activationScheduledAt` (Datetime) → `Order.Activation_Scheduled_At__c` Address (Portal → WHMCS/Order snapshot) - `street` → `WHMCS.Client.address1`; `Order.BillToStreet` - `addressLine2` → `WHMCS.Client.address2` - `city` → `WHMCS.Client.city`; `Order.BillToCity` - `state` → `WHMCS.Client.state`; `Order.BillToState` - `postalCode` → `WHMCS.Client.postcode`; `Order.BillToPostalCode` - `country` (ISO 2) → `WHMCS.Client.country`; `Order.BillToCountry` Checkout (Portal → Salesforce/WHMCS) - `billingCycle` (UI concept only) → derived server-side from SKU class; WHMCS `billingcycle` - `productSKU` (Text) → `Product2.StockKeepingUnit` (PBE lookup) - `promoCode` (Text) → WHMCS `promocode` - `quantity` (Number) → `OrderItem.Quantity` (validated against `Product2.Portal_Max_Quantity__c`) Internet (Portal → Salesforce) - `accessMode` (IPoE‑HGW | IPoE‑BYOR | PPPoE) → `Order.Access_Mode__c` - `installmentPlan` (One-time | 12-Month | 24-Month) → `Order.Installment_Plan__c` - `planTier` (Platinum | Gold | Silver) → `Order.Internet_Plan_Tier__c` (derived from SKU) - `serviceSpeed` (Text) → `Order.Service_Speed__c` - `weekendInstall` (Checkbox) → `Order.Weekend_Install__c` - `internetOffering` (Picklist) → `Order.Internet_Offering_Type__c` (defaults from Account when not provided) - Also set from product on selection: `Order.Internet_Offering_Type__c = Product2.Portal_Internet_Offering_Type__c` VPN (Portal → Salesforce) - `vpnRegion` (Picklist) → `Order.VPN_Region__c` - (optional) `vpnType` (Picklist) → `Order.VPN_Type__c` SIM / eSIM (Portal → Salesforce) - `simType` (eSIM | Physical SIM) → `Order.SIM_Type__c` - `eid` (Text) → `Order.EID__c` (required when `SIM_Type__c = eSIM`) - Porting (all optional unless flow requires): - `mnpApplication` → `Order.MNP_Application__c` - `mnpReservationNumber` → `Order.MNP_Reservation_Number__c` - `mnpExpiryDate` → `Order.MNP_Expiry_Date__c` - `mnpPhoneNumber` → `Order.MNP_Phone_Number__c` - `mvnoAccountNumber` → `Order.MVNO_Account_Number__c` - `portingDob` → `Order.Porting_DateOfBirth__c` - `portingFirstNameKanji` → `Order.Porting_FirstName_Kanji__c` - `portingLastNameKanji` → `Order.Porting_LastName_Kanji__c` - `portingFirstNameKatakana` → `Order.Porting_FirstName_Katakana__c` - `portingLastNameKatakana` → `Order.Porting_LastName_Katakana__c` - `portingGender` (Male | Female | Corporate/Other) → `Order.Porting_Gender__c` Signup (Portal → WHMCS; plus SF mapping) - `email`, `firstName`, `lastName`, `password` → WHMCS User + Client - `company` → `WHMCS.Client.companyname` (optional) - `phone` → `WHMCS.Client.phonenumber` (optional) - `customerNumber` (Salesforce Account number) → `WHMCS.Client.customfields.CustomerNumber` → BFF links to Salesforce `Account.SF_Account_No__c` ## Notes - BFF resolves pricing by `Product2.StockKeepingUnit` → `PricebookEntry` and provisioning by `Product2.WH_Product_ID__c` → WHMCS `pid[]` - Use `SIM` as the sole Product2 category for both eSIM and Physical SIM; `Order_Type__c` retains SIM vs eSIM - All Product2 visible in portal must have `StockKeepingUnit`, `Portal_Catalog__c = true`, and a `PricebookEntry`