2025-10-03 14:26:55 +09:00
|
|
|
/**
|
2025-12-25 13:59:28 +09:00
|
|
|
* Services Domain - Salesforce Provider Mapper
|
2025-12-25 13:20:45 +09:00
|
|
|
*
|
2025-12-25 13:59:28 +09:00
|
|
|
* Transforms Salesforce Product2 records to normalized services contracts.
|
2025-10-03 14:26:55 +09:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import type {
|
|
|
|
|
CatalogProductBase,
|
|
|
|
|
InternetPlanCatalogItem,
|
|
|
|
|
InternetInstallationCatalogItem,
|
|
|
|
|
InternetAddonCatalogItem,
|
|
|
|
|
SimCatalogProduct,
|
|
|
|
|
SimActivationFeeCatalogItem,
|
|
|
|
|
VpnCatalogProduct,
|
2025-12-10 15:22:10 +09:00
|
|
|
} from "../../contract.js";
|
2025-10-03 14:26:55 +09:00
|
|
|
import type {
|
|
|
|
|
SalesforceProduct2WithPricebookEntries,
|
|
|
|
|
SalesforcePricebookEntryRecord,
|
2025-12-10 15:22:10 +09:00
|
|
|
} from "./raw.types.js";
|
2025-10-20 13:53:35 +09:00
|
|
|
import {
|
|
|
|
|
enrichInternetPlanMetadata,
|
|
|
|
|
inferAddonTypeFromSku,
|
|
|
|
|
inferInstallationTermFromSku,
|
2025-12-10 15:22:10 +09:00
|
|
|
} from "../../utils.js";
|
2025-10-03 14:26:55 +09:00
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// Tier Templates (Hardcoded Product Metadata)
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// Helper Functions
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
function coerceNumber(value: unknown): number | undefined {
|
|
|
|
|
if (typeof value === "number") return value;
|
|
|
|
|
if (typeof value === "string") {
|
|
|
|
|
const parsed = Number.parseFloat(value);
|
|
|
|
|
return Number.isFinite(parsed) ? parsed : undefined;
|
|
|
|
|
}
|
|
|
|
|
return undefined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// Base Product Mapper
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
function baseProduct(
|
|
|
|
|
product: SalesforceProduct2WithPricebookEntries,
|
|
|
|
|
pricebookEntry?: SalesforcePricebookEntryRecord
|
|
|
|
|
): CatalogProductBase {
|
|
|
|
|
const sku = product.StockKeepingUnit ?? "";
|
|
|
|
|
const base: CatalogProductBase = {
|
|
|
|
|
id: product.Id,
|
|
|
|
|
sku,
|
|
|
|
|
name: product.Name ?? sku,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (product.Description) base.description = product.Description;
|
|
|
|
|
if (product.Billing_Cycle__c) base.billingCycle = product.Billing_Cycle__c;
|
|
|
|
|
if (typeof product.Catalog_Order__c === "number") base.displayOrder = product.Catalog_Order__c;
|
|
|
|
|
|
|
|
|
|
// Derive prices
|
|
|
|
|
const billingCycle = product.Billing_Cycle__c?.toLowerCase();
|
2025-10-21 18:24:47 +09:00
|
|
|
const unitPriceFromPricebook = coerceNumber(pricebookEntry?.UnitPrice);
|
|
|
|
|
const priceField = coerceNumber(product.Price__c);
|
|
|
|
|
const monthlyField = coerceNumber(product.Monthly_Price__c);
|
|
|
|
|
const oneTimeField = coerceNumber(product.One_Time_Price__c);
|
|
|
|
|
|
|
|
|
|
if (unitPriceFromPricebook !== undefined) {
|
|
|
|
|
base.unitPrice = unitPriceFromPricebook;
|
|
|
|
|
} else if (priceField !== undefined) {
|
|
|
|
|
base.unitPrice = priceField;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (monthlyField !== undefined) {
|
|
|
|
|
base.monthlyPrice = monthlyField;
|
|
|
|
|
}
|
2025-10-03 14:26:55 +09:00
|
|
|
|
2025-10-21 18:24:47 +09:00
|
|
|
if (oneTimeField !== undefined) {
|
|
|
|
|
base.oneTimePrice = oneTimeField;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-25 13:20:45 +09:00
|
|
|
const primaryPrice = unitPriceFromPricebook ?? monthlyField ?? priceField ?? oneTimeField;
|
2025-10-21 18:24:47 +09:00
|
|
|
|
|
|
|
|
if (primaryPrice !== undefined) {
|
2025-10-03 14:26:55 +09:00
|
|
|
if (billingCycle === "monthly") {
|
2025-10-21 18:24:47 +09:00
|
|
|
base.monthlyPrice = base.monthlyPrice ?? primaryPrice;
|
2025-10-03 14:26:55 +09:00
|
|
|
} else if (billingCycle) {
|
2025-10-21 18:24:47 +09:00
|
|
|
base.oneTimePrice = base.oneTimePrice ?? primaryPrice;
|
|
|
|
|
} else {
|
|
|
|
|
base.monthlyPrice = base.monthlyPrice ?? primaryPrice;
|
2025-10-03 14:26:55 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return base;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// Internet Product Mappers
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
export function mapInternetPlan(
|
|
|
|
|
product: SalesforceProduct2WithPricebookEntries,
|
|
|
|
|
pricebookEntry?: SalesforcePricebookEntryRecord
|
|
|
|
|
): InternetPlanCatalogItem {
|
|
|
|
|
const base = baseProduct(product, pricebookEntry);
|
|
|
|
|
const tier = product.Internet_Plan_Tier__c ?? undefined;
|
|
|
|
|
const offeringType = product.Internet_Offering_Type__c ?? undefined;
|
|
|
|
|
|
2025-10-20 13:53:35 +09:00
|
|
|
return enrichInternetPlanMetadata({
|
2025-10-03 14:26:55 +09:00
|
|
|
...base,
|
|
|
|
|
internetPlanTier: tier,
|
|
|
|
|
internetOfferingType: offeringType,
|
2025-10-20 13:53:35 +09:00
|
|
|
});
|
2025-10-03 14:26:55 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function mapInternetInstallation(
|
|
|
|
|
product: SalesforceProduct2WithPricebookEntries,
|
|
|
|
|
pricebookEntry?: SalesforcePricebookEntryRecord
|
|
|
|
|
): InternetInstallationCatalogItem {
|
|
|
|
|
const base = baseProduct(product, pricebookEntry);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
...base,
|
|
|
|
|
catalogMetadata: {
|
2025-10-20 13:53:35 +09:00
|
|
|
installationTerm: inferInstallationTermFromSku(base.sku),
|
2025-10-03 14:26:55 +09:00
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function mapInternetAddon(
|
|
|
|
|
product: SalesforceProduct2WithPricebookEntries,
|
|
|
|
|
pricebookEntry?: SalesforcePricebookEntryRecord
|
|
|
|
|
): InternetAddonCatalogItem {
|
|
|
|
|
const base = baseProduct(product, pricebookEntry);
|
|
|
|
|
const bundledAddonId = product.Bundled_Addon__c ?? undefined;
|
|
|
|
|
const isBundledAddon = product.Is_Bundled_Addon__c ?? false;
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
...base,
|
|
|
|
|
bundledAddonId,
|
|
|
|
|
isBundledAddon,
|
2025-10-20 13:53:35 +09:00
|
|
|
catalogMetadata: {
|
|
|
|
|
addonType: inferAddonTypeFromSku(base.sku),
|
|
|
|
|
},
|
2025-10-03 14:26:55 +09:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// SIM Product Mappers
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
export function mapSimProduct(
|
|
|
|
|
product: SalesforceProduct2WithPricebookEntries,
|
|
|
|
|
pricebookEntry?: SalesforcePricebookEntryRecord
|
|
|
|
|
): SimCatalogProduct {
|
|
|
|
|
const base = baseProduct(product, pricebookEntry);
|
|
|
|
|
const dataSize = product.SIM_Data_Size__c ?? undefined;
|
|
|
|
|
const planType = product.SIM_Plan_Type__c ?? undefined;
|
|
|
|
|
const hasFamilyDiscount = product.SIM_Has_Family_Discount__c ?? false;
|
|
|
|
|
const bundledAddonId = product.Bundled_Addon__c ?? undefined;
|
|
|
|
|
const isBundledAddon = product.Is_Bundled_Addon__c ?? false;
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
...base,
|
|
|
|
|
simDataSize: dataSize,
|
|
|
|
|
simPlanType: planType,
|
|
|
|
|
simHasFamilyDiscount: hasFamilyDiscount,
|
|
|
|
|
bundledAddonId,
|
|
|
|
|
isBundledAddon,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function mapSimActivationFee(
|
|
|
|
|
product: SalesforceProduct2WithPricebookEntries,
|
|
|
|
|
pricebookEntry?: SalesforcePricebookEntryRecord
|
|
|
|
|
): SimActivationFeeCatalogItem {
|
|
|
|
|
const simProduct = mapSimProduct(product, pricebookEntry);
|
2025-12-25 17:55:44 +09:00
|
|
|
// Auto_Add__c and Is_Default__c are no longer in the query.
|
|
|
|
|
// Default status is handled by SimServicesService fallback if not present.
|
2025-10-03 14:26:55 +09:00
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
...simProduct,
|
|
|
|
|
catalogMetadata: {
|
2025-12-25 17:55:44 +09:00
|
|
|
isDefault: false, // Will be handled by service fallback
|
2025-10-03 14:26:55 +09:00
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// VPN Product Mapper
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
export function mapVpnProduct(
|
|
|
|
|
product: SalesforceProduct2WithPricebookEntries,
|
|
|
|
|
pricebookEntry?: SalesforcePricebookEntryRecord
|
|
|
|
|
): VpnCatalogProduct {
|
|
|
|
|
const base = baseProduct(product, pricebookEntry);
|
|
|
|
|
const vpnRegion = product.VPN_Region__c ?? undefined;
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
...base,
|
|
|
|
|
vpnRegion,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// PricebookEntry Extraction
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
export function extractPricebookEntry(
|
|
|
|
|
record: SalesforceProduct2WithPricebookEntries
|
|
|
|
|
): SalesforcePricebookEntryRecord | undefined {
|
|
|
|
|
const entries = record.PricebookEntries?.records;
|
|
|
|
|
if (!Array.isArray(entries) || entries.length === 0) {
|
|
|
|
|
return undefined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Return first active entry, or first entry if none are active
|
|
|
|
|
const activeEntry = entries.find(e => e.IsActive === true);
|
|
|
|
|
return activeEntry ?? entries[0];
|
|
|
|
|
}
|