# Consolidated Type System - Complete Solution

## Problem Analysis

You identified a critical issue: **order items, catalog items, and product types were all trying to encapsulate the same Salesforce Product2 data**, leading to:

1. **Type duplication** across contexts
2. **Inconsistent pricing models** (sometimes `price`, sometimes `monthlyPrice`/`oneTimePrice`)
3. **Enhanced Order Summary** creating its own conflicting `OrderItem` interface
4. **Missing PricebookEntry representation** in the type system

## Solution: Unified Type Architecture

### Core Principle

**One Salesforce Object = One TypeScript Type Structure**

Since both catalog items and order items ultimately represent Salesforce `Product2` objects (with `PricebookEntry` for pricing), we created a unified type system that directly maps to the Salesforce/WHMCS object structure.

## New Type Hierarchy

### 1. Base Product Structure

```typescript
// packages/domain/src/entities/product.ts

// Maps directly to Salesforce Product2 fields
interface BaseProduct {
  // Standard Salesforce fields
  id: string; // Product2.Id
  name: string; // Product2.Name
  sku: string; // Product2.StockKeepingUnit

  // Custom portal fields
  category: ProductCategory; // Product2Categories1__c
  itemClass: ItemClass; // Item_Class__c
  billingCycle: BillingCycle; // Billing_Cycle__c
  portalCatalog: boolean; // Portal_Catalog__c
  portalAccessible: boolean; // Portal_Accessible__c

  // WHMCS integration
  whmcsProductId: number; // WH_Product_ID__c
  whmcsProductName: string; // WH_Product_Name__c
}

// Represents Salesforce PricebookEntry structure
interface PricebookEntry {
  id: string; // PricebookEntry.Id
  name: string; // PricebookEntry.Name
  unitPrice: number; // PricebookEntry.UnitPrice
  pricebook2Id: string; // PricebookEntry.Pricebook2Id
  product2Id: string; // PricebookEntry.Product2Id
  isActive: boolean; // PricebookEntry.IsActive
}

// Product with proper pricing structure
interface ProductWithPricing extends BaseProduct {
  pricebookEntry?: PricebookEntry;
  // Convenience fields derived from pricebookEntry and billingCycle
  unitPrice?: number; // PricebookEntry.UnitPrice
  monthlyPrice?: number; // UnitPrice if billingCycle === "Monthly"
  oneTimePrice?: number; // UnitPrice if billingCycle === "Onetime"
}
```

### 2. Specialized Product Types

```typescript
// Category-specific extensions
interface InternetProduct extends ProductWithPricing {
  category: "Internet";
  internetPlanTier?: "Silver" | "Gold" | "Platinum";
  internetOfferingType?: string;
  internetMonthlyPrice?: number;
}

interface SimProduct extends ProductWithPricing {
  category: "SIM";
  simDataSize?: string;
  simPlanType?: "DataOnly" | "DataSmsVoice" | "VoiceOnly";
  simHasFamilyDiscount?: boolean;
}

// Union type for all products
type Product = InternetProduct | SimProduct | VpnProduct | ProductWithPricing;
```

### 3. Order Item Types

```typescript
// For new orders (before Salesforce creation)
interface OrderItemRequest extends ProductWithPricing {
  quantity: number;
  autoAdded?: boolean;
}

// Actual Salesforce OrderItem structure
interface SalesforceOrderItem {
  id: string; // OrderItem.Id
  orderId: string; // OrderItem.OrderId
  quantity: number; // OrderItem.Quantity
  unitPrice: number; // OrderItem.UnitPrice
  totalPrice: number; // OrderItem.TotalPrice
  pricebookEntry: PricebookEntry & {
    product2: Product; // Full product data
  };
  whmcsServiceId?: string; // WHMCS_Service_ID__c
  billingCycle?: BillingCycle; // Derived from Product2
}
```

### 4. Order Types

```typescript
// Salesforce Order structure
interface SalesforceOrder {
  id: string; // Order.Id
  orderNumber: string; // Order.OrderNumber
  status: string; // Order.Status
  type: string; // Order.Type
  effectiveDate: string; // Order.EffectiveDate
  totalAmount: number; // Order.TotalAmount
  accountId: string; // Order.AccountId
  pricebook2Id: string; // Order.Pricebook2Id

  // Custom fields
  orderType?: string; // Order_Type__c
  activationStatus?: string; // Activation_Status__c
  whmcsOrderId?: string; // WHMCS_Order_ID__c

  orderItems?: SalesforceOrderItem[];
}

// WHMCS Order structure (separate for existing orders)
interface WhmcsOrder extends WhmcsEntity {
  orderNumber: string;
  status: OrderStatus;
  items: WhmcsOrderItem[];
}

// Union types for different contexts
type Order = WhmcsOrder | SalesforceOrder;
type OrderItem = WhmcsOrderItem | OrderItemRequest | SalesforceOrderItem;
```

## Key Benefits

### 1. **Single Source of Truth**

- All types map directly to Salesforce object structure
- No more guessing which type to use in which context
- Consistent field names across the application

### 2. **Proper PricebookEntry Representation**

- Pricing is now properly modeled as PricebookEntry structure
- Convenience fields (`monthlyPrice`, `oneTimePrice`) derived from `unitPrice` and `billingCycle`
- No more confusion between `price` vs `monthlyPrice` vs `unitPrice`

### 3. **Type Safety**

- TypeScript discrimination based on `category` field
- Proper type guards for business logic
- Compile-time validation of field access

### 4. **Maintainability**

- Changes to Salesforce fields only need updates in one place
- Clear transformation functions between Salesforce API and TypeScript types
- Backward compatibility through type aliases

## Transformation Functions

```typescript
// Transform Salesforce Product2 + PricebookEntry to unified Product
function fromSalesforceProduct2(sfProduct: any, pricebookEntry?: any): Product {
  const billingCycle = sfProduct.Billing_Cycle__c || "Onetime";
  const unitPrice = pricebookEntry?.UnitPrice;

  return {
    id: sfProduct.Id,
    name: sfProduct.Name,
    sku: sfProduct.StockKeepingUnit,
    category: sfProduct.Product2Categories1__c,
    itemClass: sfProduct.Item_Class__c,
    billingCycle: billingCycle,
    portalCatalog: sfProduct.Portal_Catalog__c,
    portalAccessible: sfProduct.Portal_Accessible__c,
    whmcsProductId: sfProduct.WH_Product_ID__c,
    whmcsProductName: sfProduct.WH_Product_Name__c,

    // PricebookEntry structure
    pricebookEntry: pricebookEntry
      ? {
          id: pricebookEntry.Id,
          name: pricebookEntry.Name,
          unitPrice: pricebookEntry.UnitPrice,
          pricebook2Id: pricebookEntry.Pricebook2Id,
          product2Id: sfProduct.Id,
          isActive: pricebookEntry.IsActive !== false,
        }
      : undefined,

    // Convenience pricing fields
    unitPrice: unitPrice,
    monthlyPrice: billingCycle === "Monthly" ? unitPrice : undefined,
    oneTimePrice: billingCycle === "Onetime" ? unitPrice : undefined,

    // Include all other fields for dynamic access
    ...sfProduct,
  };
}

// Transform Salesforce OrderItem to unified structure
function fromSalesforceOrderItem(sfOrderItem: any): SalesforceOrderItem {
  const product = fromSalesforceProduct2(
    sfOrderItem.PricebookEntry?.Product2,
    sfOrderItem.PricebookEntry
  );

  return {
    id: sfOrderItem.Id,
    orderId: sfOrderItem.OrderId,
    quantity: sfOrderItem.Quantity,
    unitPrice: sfOrderItem.UnitPrice,
    totalPrice: sfOrderItem.TotalPrice,
    pricebookEntry: {
      ...product.pricebookEntry!,
      product2: product,
    },
    whmcsServiceId: sfOrderItem.WHMCS_Service_ID__c,
    billingCycle: product.billingCycle,
    ...sfOrderItem,
  };
}
```

## Usage Examples

### Catalog Context

```typescript
const product = fromSalesforceProduct2(salesforceProduct, pricebookEntry);

if (isCatalogVisible(product)) {
  displayInCatalog(product);
}
```

### Order Context

```typescript
const orderItem: OrderItemRequest = {
  ...product,
  quantity: 1,
  autoAdded: false,
};
```

### Enhanced Order Summary

```typescript
// Now uses consistent unified types
interface OrderItem extends OrderItemRequest {
  id?: string;
  description?: string;
}

// Access unified pricing fields
const price =
  item.billingCycle === "Monthly"
    ? item.monthlyPrice || item.unitPrice || 0
    : item.oneTimePrice || item.unitPrice || 0;
```

## Migration Strategy

1. **Backward Compatibility**: Legacy type aliases maintained

   ```typescript
   export type InternetPlan = InternetProduct;
   export type CatalogItem = Product;
   ```

2. **Gradual Adoption**: Existing code continues to work while new code uses unified types

3. **Clear Documentation**: This document explains the new structure and migration path

## Files Modified

- `packages/domain/src/entities/product.ts` - New unified product types
- `packages/domain/src/entities/order.ts` - Updated order types
- `packages/domain/src/entities/catalog.ts` - Re-exports with legacy aliases
- `apps/portal/src/features/catalog/components/base/EnhancedOrderSummary.tsx` - Updated to use unified types

## Enhanced Order Summary

The **Enhanced Order Summary** was a UI component that created its own `OrderItem` interface, leading to type conflicts. It has been updated to:

1. **Extend unified types**: Now extends `OrderItemRequest` instead of creating conflicting interfaces
2. **Use consistent pricing**: Uses `monthlyPrice`/`oneTimePrice`/`unitPrice` from unified structure
3. **Proper field access**: Uses `itemClass`, `billingCycle` from unified product structure

This eliminates the confusion about "which OrderItem type should I use?" because there's now a clear hierarchy:

- `OrderItemRequest` - for new orders in the portal
- `SalesforceOrderItem` - for existing orders from Salesforce
- `WhmcsOrderItem` - for existing orders from WHMCS

The Enhanced Order Summary now properly represents the unified product structure while adding UI-specific fields like `description` and optional `id`.

## Conclusion

This consolidated type system eliminates the original problem of multiple types trying to represent the same Salesforce data. Now there's **one unified type system** that properly maps to your Salesforce/WHMCS object structure, with clear transformation functions and proper PricebookEntry representation.

The solution maintains backward compatibility while providing a clear path forward for consistent type usage across your entire application.
