# Order Fulfillment - Complete Implementation Guide

_This document provides the complete, up-to-date specification for order creation and fulfillment workflow._

## 🏗️ Architecture Overview

### System Components

- **Portal Frontend**: Next.js customer interface
- **Portal BFF**: NestJS backend orchestrating all integrations
- **Salesforce**: Order management, catalog, CS review/approval
- **WHMCS**: Billing, payment methods, service provisioning

### Data Flow

```
Customer → Portal → BFF → Salesforce (Order Creation)
CS Team → Salesforce (Platform Event) → BFF (Subscriber) → WHMCS (Order Fulfillment)
```

## 🛍️ Complete Customer Journey

### Phase 1: Order Creation

#### 1. Customer Signup

```typescript
// Required fields
{
  email: "customer@example.com",
  password: "secure_password",
  firstName: "John",
  lastName: "Doe",
  customerNumber: "SF123456" // Salesforce Account Number
}

// Portal creates:
├── WHMCS Client (with Customer Number in custom field)
├── Portal User account
└── Mapping: userId ↔ whmcsClientId ↔ sfAccountId
```

#### 2. Payment Method Setup (Required Gate)

```typescript
// Portal checks payment method before checkout
GET / billing / payment - methods / summary;
Response: {
  hasPaymentMethod: true / false;
}

// If false, redirect to WHMCS SSO
POST / auth / sso - link;
Response: {
  ssoUrl: "https://whmcs.com/index.php?rp=/account/paymentmethods&token=...";
}
```

#### 3. Browse Catalog

```typescript
// Personalized catalog based on eligibility
GET /catalog/personalized
Headers: { Authorization: "Bearer jwt_token" }

// BFF queries Salesforce
SELECT Id, Name, StockKeepingUnit, WH_Product_ID__c, Billing_Cycle__c
FROM Product2
WHERE Portal_Catalog__c = true
  AND Internet_Offering_Type__c = :accountEligibility
```

#### 4. Place Order

```typescript
// Customer checkout
POST /orders
{
  "items": [
    { "sku": "INTERNET-GOLD-APT-1G", "quantity": 1 },
    { "sku": "INTERNET-INSTALL-SINGLE", "quantity": 1 },
    { "sku": "INTERNET-ADDON-HOME-PHONE", "quantity": 1 }
  ],
  "activationType": "Scheduled",
  "activationScheduledAt": "2024-01-20T09:00:00Z"
}

// BFF creates in Salesforce:
Order {
  AccountId: "001xx000004TmiQAAS",
  Status: "Pending Review",
  Order_Type__c: "Internet",
  Activation_Type__c: "Scheduled",
  Activation_Scheduled_At__c: "2024-01-20T09:00:00Z"
}

OrderItems [
  { Product2.SKU: "INTERNET-GOLD-APT-1G", Quantity: 1 },
  { Product2.SKU: "INTERNET-INSTALL-SINGLE", Quantity: 1 },
  { Product2.SKU: "INTERNET-ADDON-HOME-PHONE", Quantity: 1 }
]
```

### Phase 2: CS Review & Approval

#### 5. Order Review (Salesforce)

```sql
-- CS Team views order with all details
SELECT Id, OrderNumber, Status, TotalAmount, Account.Name,
       Order_Type__c, Activation_Type__c, Activation_Scheduled_At__c,
       (SELECT Product2.Name, Product2.WH_Product_ID__c, Quantity, UnitPrice
        FROM OrderItems)
FROM Order
WHERE Id = '8014x000000ABCDXYZ'
```

#### 6. Provision Trigger (Platform Events)

```text
Salesforce Record‑Triggered Flow publishes Platform Event: OrderProvisionRequested__e
Fields:
- OrderId__c (Text 18)
- IdemKey__c (Text 80, optional)
```

The portal subscribes to this event, enqueues a job, and performs provisioning.

### Phase 3: Order Fulfillment

#### 7. Order Fulfillment Service (Modular Architecture)

##### OrderFulfillmentValidator

```typescript
class OrderFulfillmentValidator {
  async validateFulfillmentRequest(sfOrderId: string, idempotencyKey: string) {
    // 1. Validate Salesforce order exists
    const sfOrder = await this.salesforceService.getOrder(sfOrderId);

    // 2. Check idempotency (already provisioned?)
    if (sfOrder.WHMCS_Order_ID__c) {
      return { isAlreadyProvisioned: true, whmcsOrderId: sfOrder.WHMCS_Order_ID__c };
    }

    // 3. Get WHMCS client ID from mapping
    const clientId = await this.mappingsService.findBySfAccountId(sfOrder.Account.Id);

    // 4. Validate payment method exists
    const hasPaymentMethod = await this.whmcsOrderService.hasPaymentMethod(clientId);
    if (!hasPaymentMethod) {
      throw new ConflictException(
        "Payment method missing - client must add payment method before fulfillment"
      );
    }

    return { sfOrder, clientId, isAlreadyProvisioned: false };
  }
}
```

##### OrderWhmcsMapper

```typescript
class OrderWhmcsMapper {
  mapOrderItemsToWhmcs(orderItems: any[]): WhmcsOrderItem[] {
    return orderItems.map(item => ({
      productId: item.product.whmcsProductId, // From WH_Product_ID__c
      billingCycle: item.product.billingCycle.toLowerCase(), // From Billing_Cycle__c
      quantity: item.quantity,
    }));
  }
}
```

##### OrderFulfillmentOrchestrator

```typescript
class OrderFulfillmentOrchestrator {
  async executeFulfillment(sfOrderId: string, payload: any, idempotencyKey: string) {
    const context = { sfOrderId, idempotencyKey, steps: [] };

    // Step 1: Validate request
    context.validation = await this.validator.validateFulfillmentRequest(sfOrderId, idempotencyKey);

    if (context.validation.isAlreadyProvisioned) {
      return { success: true, status: "Already Fulfilled" };
    }

    // Step 2: Update SF status to "Activating"
    await this.salesforceService.updateOrder({
      Id: sfOrderId,
      Status: "Activating",
      Provisioning_Status__c: "In Progress",
    });

    // Step 3: Get full order details
    context.orderDetails = await this.orderOrchestrator.getOrder(sfOrderId);

    // Step 4: Map to WHMCS format
    context.mappingResult = await this.mapper.mapOrderItemsToWhmcs(context.orderDetails.items);

    // Step 5: Create WHMCS order
    context.whmcsResult = await this.whmcsOrderService.addOrder({
      clientId: context.validation.clientId,
      items: context.mappingResult.whmcsItems,
      paymentMethod: "mailin",
      noinvoice: true,
      noemail: true,
    });

    // Step 6: Accept/provision WHMCS order
    await this.whmcsOrderService.acceptOrder(context.whmcsResult.orderId);

    // Step 7: Update SF with success
    await this.salesforceService.updateOrder({
      Id: sfOrderId,
      Status: "Activated",
      Provisioning_Status__c: "Fulfilled",
      WHMCS_Order_ID__c: context.whmcsResult.orderId,
    });

    return { success: true, status: "Fulfilled", whmcsOrderId: context.whmcsResult.orderId };
  }
}
```

## 📊 Complete Data Mapping Reference

### Salesforce to WHMCS Mapping

#### Order Header Mapping

| Source            | Target                 | Example                        | Notes                                    |
| ----------------- | ---------------------- | ------------------------------ | ---------------------------------------- |
| `Order.AccountId` | Resolved to `clientid` | `1`                            | Via portal mapping table                 |
| `Order.Id`        | Added to order notes   | `sfOrderId=8014x000000ABCDXYZ` | For tracking                             |
| N/A               | `paymentmethod`        | `"mailin"`                     | Required by WHMCS API                    |
| N/A               | `noinvoice`            | `true`                         | Don't create invoice during provisioning |
| N/A               | `noemail`              | `true`                         | Don't send emails during provisioning    |

#### OrderItem Array Mapping

| Salesforce Field            | WHMCS Parameter  | Example Value                       | Format       |
| --------------------------- | ---------------- | ----------------------------------- | ------------ |
| `Product2.WH_Product_ID__c` | `pid[]`          | `["185", "242", "246"]`             | String array |
| `Product2.Billing_Cycle__c` | `billingcycle[]` | `["monthly", "onetime", "monthly"]` | String array |
| `OrderItem.Quantity`        | `qty[]`          | `[1, 1, 1]`                         | Number array |

#### Product ID Mapping Examples

| Product Name                 | Salesforce SKU                 | WH_Product_ID\_\_c | WHMCS pid | Billing Cycle |
| ---------------------------- | ------------------------------ | ------------------ | --------- | ------------- |
| Internet Gold (Apartment 1G) | `INTERNET-GOLD-APT-1G`         | 185                | "185"     | "monthly"     |
| Single Installation          | `INTERNET-INSTALL-SINGLE`      | 242                | "242"     | "onetime"     |
| Hikari Denwa Service         | `INTERNET-ADDON-HOME-PHONE`    | 246                | "246"     | "monthly"     |
| Hikari Denwa Installation    | `INTERNET-ADDON-DENWA-INSTALL` | 247                | "247"     | "onetime"     |
| Weekend Installation Fee     | `INTERNET-INSTALL-WEEKEND`     | 245                | "245"     | "onetime"     |

### WHMCS API Request/Response Format

#### AddOrder Request

```json
{
  "action": "AddOrder",
  "clientid": 1,
  "paymentmethod": "mailin",
  "pid": ["185", "242", "246", "247"],
  "billingcycle": ["monthly", "onetime", "monthly", "onetime"],
  "qty": [1, 1, 1, 1],
  "noinvoice": true,
  "noemail": true,
  "promocode": "",
  "configoptions": ["", "", "", ""],
  "customfields": ["", "", "", ""]
}
```

#### AddOrder Response

```json
{
  "result": "success",
  "orderid": 12345,
  "serviceids": "67890,67891,67892,67893",
  "addonids": "",
  "domainids": "",
  "invoiceid": 0
}
```

#### AcceptOrder Request

```json
{
  "action": "AcceptOrder",
  "orderid": 12345
}
```

#### AcceptOrder Response

```json
{
  "result": "success"
}
```

### Status Update Mapping

#### Success Flow

| Step                 | Salesforce Order.Status | Provisioning_Status\_\_c | WHMCS_Order_ID\_\_c |
| -------------------- | ----------------------- | ------------------------ | ------------------- |
| Initial              | "Pending Review"        | null                     | null                |
| CS Approval          | "Activating"            | "In Progress"            | null                |
| WHMCS Created        | "Activating"            | "In Progress"            | "12345"             |
| Services Provisioned | "Activated"             | "Fulfilled"              | "12345"             |

#### Failure Flow

| Step        | Salesforce Order.Status | Provisioning_Status\_\_c | Error Fields                    |
| ----------- | ----------------------- | ------------------------ | ------------------------------- |
| Initial     | "Pending Review"        | null                     | null                            |
| CS Approval | "Activating"            | "In Progress"            | null                            |
| Failure     | "Draft"                 | "Failed"                 | Error_Code**c, Error_Message**c |

## 🔒 Security Implementation

- No inbound Salesforce webhooks are used; provisioning is triggered via Platform Events.
- Portal authenticates to Salesforce via JWT (Connected App) and requires Platform Event permissions.

### Error Codes

```typescript
enum FulfillmentErrorCode {
  PAYMENT_METHOD_MISSING = "PAYMENT_METHOD_MISSING",
  ORDER_NOT_FOUND = "ORDER_NOT_FOUND",
  WHMCS_ERROR = "WHMCS_ERROR",
  MAPPING_ERROR = "MAPPING_ERROR",
  FULFILLMENT_ERROR = "FULFILLMENT_ERROR",
}
```

## ⚡ Performance Metrics

### Typical Timeline

```
10:30:00.000 - CS approves Order
10:30:00.050 - Platform Event published (OrderProvisionRequested__e)
10:30:00.080 - BFF subscriber enqueues provisioning job
10:30:00.200 - Salesforce order updated to "Activating"
10:30:00.500 - Order details retrieved and mapped
10:30:01.000 - WHMCS AddOrder API call
10:30:01.500 - WHMCS AcceptOrder API call
10:30:02.000 - Services provisioned in WHMCS
10:30:02.200 - Salesforce updated to "Activated"

Total fulfillment time: ~2.2 seconds (asynchronous trigger) ⚡
```

### API Call Performance

- **Salesforce getOrder**: ~200ms
- **WHMCS AddOrder**: ~400ms
- **WHMCS AcceptOrder**: ~300ms
- **Salesforce updateOrder**: ~150ms

## 🔧 Configuration Requirements

### Salesforce Setup

```
1) Platform Event: OrderProvisionRequested__e (fields: OrderId__c [Text 18], IdemKey__c [Text 80, optional])
2) Permission Set: grant Platform Event permissions and PE object read to the portal integration user
3) Flow (Record‑Triggered): On Order Status = Approved → Create OrderProvisionRequested__e with OrderId__c
```

### Environment Variables

```bash
# BFF Configuration (Salesforce Platform Events)
SF_EVENTS_ENABLED=true
SF_PROVISION_EVENT_CHANNEL=/event/OrderProvisionRequested__e
SF_EVENTS_REPLAY=LATEST

# WHMCS API
WHMCS_API_IDENTIFIER=your_whmcs_api_id
WHMCS_API_SECRET=your_whmcs_api_secret
WHMCS_API_URL=https://your-whmcs.com/includes/api.php

# Database
DATABASE_URL=postgresql://user:pass@host:5432/portal
```

This comprehensive guide ensures consistent implementation across all teams and provides the complete picture of the order fulfillment workflow.
