feat: trigger Freebit APIs on "Processed" status instead of "Approved"
Some checks failed
Pull Request Checks / Code Quality & Security (push) Has been cancelled
Security Audit / Security Vulnerability Audit (push) Has been cancelled
Security Audit / Dependency Review (push) Has been cancelled
Security Audit / CodeQL Security Analysis (push) Has been cancelled
Security Audit / Check Outdated Dependencies (push) Has been cancelled
Some checks failed
Pull Request Checks / Code Quality & Security (push) Has been cancelled
Security Audit / Security Vulnerability Audit (push) Has been cancelled
Security Audit / Dependency Review (push) Has been cancelled
Security Audit / CodeQL Security Analysis (push) Has been cancelled
Security Audit / Check Outdated Dependencies (push) Has been cancelled
- CDC subscriber now listens for Status="Processed" to fire SIM APIs - On API error, order Status reverts to "Approved" for retry - Provisioning processor validates "Processed" for Physical SIM flow Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e7d1371c48
commit
afc18988cd
@ -49,7 +49,7 @@ const INTERNAL_ORDER_FIELDS = new Set([
|
|||||||
const INTERNAL_ORDER_ITEM_FIELDS = new Set(["WHMCS_Service_ID__c"]);
|
const INTERNAL_ORDER_ITEM_FIELDS = new Set(["WHMCS_Service_ID__c"]);
|
||||||
|
|
||||||
/** Statuses that trigger provisioning */
|
/** Statuses that trigger provisioning */
|
||||||
const PROVISION_TRIGGER_STATUSES = new Set(["Approved", "Reactivate"]);
|
const PROVISION_TRIGGER_STATUSES = new Set(["Processed", "Reactivate"]);
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class OrderCdcSubscriber implements OnModuleInit {
|
export class OrderCdcSubscriber implements OnModuleInit {
|
||||||
@ -134,9 +134,9 @@ export class OrderCdcSubscriber implements OnModuleInit {
|
|||||||
await this.handleActivationStatusChange(payload, orderId);
|
await this.handleActivationStatusChange(payload, orderId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for provisioning trigger (Status change to "Approved")
|
// Check for provisioning trigger (Status change to "Processed")
|
||||||
if (payload && changedFields.has("Status")) {
|
if (payload && changedFields.has("Status")) {
|
||||||
await this.handleStatusApprovedChange(payload, orderId);
|
await this.handleStatusProcessedChange(payload, orderId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache invalidation - only for customer-facing field changes
|
// Cache invalidation - only for customer-facing field changes
|
||||||
@ -222,9 +222,9 @@ export class OrderCdcSubscriber implements OnModuleInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle order status changes to "Approved"
|
* Handle order status changes to "Processed"
|
||||||
*
|
*
|
||||||
* Enqueues a provisioning job when Status changes to "Approved".
|
* Enqueues a provisioning job when Status changes to "Processed".
|
||||||
* The provisioning processor will fetch the full order from Salesforce
|
* The provisioning processor will fetch the full order from Salesforce
|
||||||
* and validate the conditions (SIM_Type__c, Assign_Physical_SIM__c, etc.)
|
* and validate the conditions (SIM_Type__c, Assign_Physical_SIM__c, etc.)
|
||||||
*
|
*
|
||||||
@ -232,27 +232,20 @@ export class OrderCdcSubscriber implements OnModuleInit {
|
|||||||
* because CDC only includes CHANGED fields. If only Status was updated, those fields
|
* because CDC only includes CHANGED fields. If only Status was updated, those fields
|
||||||
* will be null in the payload even though they have values on the record.
|
* will be null in the payload even though they have values on the record.
|
||||||
*
|
*
|
||||||
* The processor handles:
|
* On API failure, the orchestrator reverts the order Status back to "Approved".
|
||||||
* - Physical SIM: Status="Approved" + SIM_Type="Physical SIM" + Assigned_Physical_SIM set
|
|
||||||
* - Standard: Activation_Status__c="Activating"
|
|
||||||
* - Idempotency via WHMCS_Order_ID__c check
|
|
||||||
*/
|
*/
|
||||||
private async handleStatusApprovedChange(
|
private async handleStatusProcessedChange(
|
||||||
payload: Record<string, unknown>,
|
payload: Record<string, unknown>,
|
||||||
orderId: string
|
orderId: string
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const status = extractStringField(payload, ["Status"]);
|
const status = extractStringField(payload, ["Status"]);
|
||||||
|
|
||||||
// Only trigger when status changes to "Approved"
|
// Only trigger when status changes to "Processed"
|
||||||
if (status !== "Approved") {
|
if (status !== "Processed") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: We intentionally do NOT check SIM_Type__c or Assign_Physical_SIM__c here
|
this.logger.log("Enqueuing provisioning job for order status change to Processed", {
|
||||||
// because CDC payloads only contain changed fields. The provisioning processor
|
|
||||||
// will fetch the full order and validate all conditions.
|
|
||||||
|
|
||||||
this.logger.log("Enqueuing provisioning job for order status change to Approved", {
|
|
||||||
orderId,
|
orderId,
|
||||||
status,
|
status,
|
||||||
});
|
});
|
||||||
@ -260,15 +253,15 @@ export class OrderCdcSubscriber implements OnModuleInit {
|
|||||||
try {
|
try {
|
||||||
await this.provisioningQueue.enqueue({
|
await this.provisioningQueue.enqueue({
|
||||||
sfOrderId: orderId,
|
sfOrderId: orderId,
|
||||||
idempotencyKey: `cdc-status-approved-${Date.now()}-${orderId}`,
|
idempotencyKey: `cdc-status-processed-${Date.now()}-${orderId}`,
|
||||||
correlationId: `cdc-status-approved-${orderId}`,
|
correlationId: `cdc-status-processed-${orderId}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.logger.log("Successfully enqueued provisioning job for Approved status", {
|
this.logger.log("Successfully enqueued provisioning job for Processed status", {
|
||||||
orderId,
|
orderId,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error("Failed to enqueue provisioning job for Approved status", {
|
this.logger.error("Failed to enqueue provisioning job for Processed status", {
|
||||||
orderId,
|
orderId,
|
||||||
error: error instanceof Error ? error.message : String(error),
|
error: error instanceof Error ? error.message : String(error),
|
||||||
});
|
});
|
||||||
|
|||||||
@ -34,10 +34,10 @@ export class ProvisioningProcessor extends WorkerHost {
|
|||||||
|
|
||||||
// Guard: Determine if this is a valid provisioning request
|
// Guard: Determine if this is a valid provisioning request
|
||||||
// Case 1: Standard flow - Activation_Status__c = "Activating"
|
// Case 1: Standard flow - Activation_Status__c = "Activating"
|
||||||
// Case 2: Physical SIM flow - Status = "Approved" with SIM_Type__c = "Physical SIM"
|
// Case 2: Physical SIM flow - Status = "Processed" with SIM_Type__c = "Physical SIM"
|
||||||
const isStandardActivation = activationStatus === "Activating";
|
const isStandardActivation = activationStatus === "Activating";
|
||||||
const isPhysicalSimApproval =
|
const isPhysicalSimApproval =
|
||||||
orderStatus === "Approved" && simType === "Physical SIM" && !!assignedPhysicalSim;
|
orderStatus === "Processed" && simType === "Physical SIM" && !!assignedPhysicalSim;
|
||||||
|
|
||||||
if (!isStandardActivation && !isPhysicalSimApproval) {
|
if (!isStandardActivation && !isPhysicalSimApproval) {
|
||||||
this.logger.log("Skipping provisioning job: Order not in activatable state", {
|
this.logger.log("Skipping provisioning job: Order not in activatable state", {
|
||||||
|
|||||||
@ -312,7 +312,7 @@ export class OrderFulfillmentOrchestrator {
|
|||||||
failedStep: context.steps.find(s => s.status === "failed")?.step,
|
failedStep: context.steps.find(s => s.status === "failed")?.step,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update Salesforce with failure status
|
// Update Salesforce with failure status — revert to "Approved" so it can be retried
|
||||||
const errorShortCode = (
|
const errorShortCode = (
|
||||||
this.orderFulfillmentErrorService.getShortCode(error) || String(errorCode)
|
this.orderFulfillmentErrorService.getShortCode(error) || String(errorCode)
|
||||||
)
|
)
|
||||||
@ -321,7 +321,7 @@ export class OrderFulfillmentOrchestrator {
|
|||||||
try {
|
try {
|
||||||
await this.salesforceFacade.updateOrder({
|
await this.salesforceFacade.updateOrder({
|
||||||
Id: sfOrderId,
|
Id: sfOrderId,
|
||||||
Status: "Pending Review",
|
Status: "Approved",
|
||||||
Activation_Status__c: "Failed",
|
Activation_Status__c: "Failed",
|
||||||
Activation_Error_Code__c: errorShortCode,
|
Activation_Error_Code__c: errorShortCode,
|
||||||
Activation_Error_Message__c: userMessage?.slice(0, 255),
|
Activation_Error_Message__c: userMessage?.slice(0, 255),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user