# Caterpoint Integration Guide

## Overview

v1.2.0 introduces the `DataNormalizer` component to transform Caterpoint order data into a backend-compatible format for order creation.

**Order Flow:**
```
Caterpoint API (XML) 
  → DataNormalizer (normalization) 
  → Magento-compatible data format 
  → OrderImporter::newCaterPointOrder() 
  → backend\models\Cartsbe (created in system)
```

The `OrderImporter::newCaterPointOrder()` static method ingests normalized Caterpoint order data and creates backend order records in the system. This component is self-contained and does not depend on the ahs-magento module.

## Usage

### 1. Fetch Raw Orders with Normalization

Add the `normalize=1` parameter to the fetch-raw endpoint:

```
GET /NGauge/caterpoint/order/fetch-raw?format=json&normalize=1
```

**Example Request:**
```bash
curl "https://collinsfresh.dev.portalpeople.co.uk/NGauge/caterpoint/order/fetch-raw?format=json&normalize=1"
```

**Response Structure:**
```json
{
  "success": true,
  "message": "Raw orders fetched from Caterpoint API",
  "sourceFormat": "xml",
  "normalized": true,
  "data": {
    "orders": [
      {
        "payment": {
          "method": "caterpoint_order"
        },
        "order": {
          "updated_at": "2026-02-23T08:20:42Z",
          "id": "0006511986",
          "grand_total": 0
        },
        "customer": {
          "email": "supplier-0006511986@caterpoint.local",
          "firstname": "St",
          "lastname": "Katharines House Care Home"
        },
        "billing_address": {
          "street": "Ship-To Location: 235",
          "city": "Unknown",
          "postcode": "",
          "telephone": ""
        },
        "shipping_address": {
          "street": "Ship-To Location: 235",
          "city": "Unknown",
          "postcode": "",
          "telephone": ""
        },
        "items": [
          {
            "sku": "15002K",
            "name": "Apples Granny Smith",
            "qty_ordered": 1,
            "row_total_incl_tax": 0,
            "discount_amount": 0
          }
        ],
        "shipping_fields": {
          "shipping_date": "2026-02-27",
          "shipping_message": "Caterpoint Order Import"
        },
        "shipping_method": {
          "description": "Caterpoint Delivery"
        },
        "order_source": "caterpoint",
        "sourceFormat": "caterpoint-xml"
      }
    ],
    "nextCursor": null,
    "total": 1
  }
}
```

### 2. Creating Backend Orders with OrderImporter

The normalized data can be passed directly to `OrderImporter::newCaterPointOrder()` to create backend order records:

```php
<?php
// Fetch normalized Caterpoint orders
$response = json_decode(file_get_contents(
    'https://collinsfresh.dev.portalpeople.co.uk/NGauge/caterpoint/order/fetch-raw?format=json&normalize=1'
), true);

// Create backend order records from each order
foreach ($response['data']['orders'] as $orderData) {
    // newCaterPointOrder() creates a backend\models\Cartsbe record
    $cartModel = \PortalPeople\caterpoint\components\OrderImporter::newCaterPointOrder($orderData);
    
    if ($cartModel) {
        echo "Backend order created: {$cartModel->id} (Caterpoint: {$orderData['order']['id']})\n";
    } else {
        echo "Failed to create order from: {$orderData['order']['id']}\n";
    }
}
?>
```

**What happens in newCaterPointOrder:**
- Creates a `backend\models\Cartsbe` model (backend order)
- Parses customer details (splits buyer name into first/last name)
- Creates cart lines for each order item (with product lookup by SKU)
- Sets up shipment information with requested delivery date
- Creates payment response record (status: Paid for Caterpoint orders)
- Returns the saved cart model or null on failure
- Logs all operations via Yii logger (channel: 'caterpoint-import')

## Data Mapping

The normalization transforms Caterpoint fields to the data structure expected by `Carts::NewMagentoOrder()`:

| Caterpoint Field | Magento Field | Backend Usage |
|------------------|---------------|-------|
| `orderIdentification.entityIdentification` | `order.id` | Unique order identifier |
| `creationDateTime` | `order.updated_at` | Order date/time |
| `buyer.organisationDetails.organisationName` | `customer.firstname`, `customer.lastname` | Split on first space; used for contact name |
| `orderLogisticalInformation.shipTo.additionalPartyIdentification` | `billing_address.street`, `shipping_address.street` | Ship-to location identifier |
| `orderLogisticalInformation.orderLogisticalDateInformation.requestedDeliveryDateTime.date` | `shipping_fields.shipping_date` | Creates shipment.shipment_date and delivery_date |
| `orderLineItem[].transactionalTradeItem.additionalTradeItemIdentification` | `items[].sku` | Product lookup key; must match stock_code in backend\models\Productsbe |
| `orderLineItem[].transactionalTradeItem.tradeItemDescription` | `items[].name` | Product description (fallback for display) |
| `orderLineItem[].requestedQuantity` | `items[].qty_ordered` | Order quantity |
| - | `payment.method` | Set to `caterpoint_order` to identify source |
| - | `order_source` | Set to `caterpoint` for audit trail |

## Important Notes & Limitations

### Data Limitations

- **Pricing Data**: Caterpoint's fetch-raw endpoint does not include pricing information. All prices are set to 0. Order importer will create cart lines with zero cost.
- **Email Address**: A placeholder email is generated as `supplier-{orderId}@caterpoint.local` (Caterpoint doesn't provide customer emails)
- **Address Information**: Only ship-to location ID is available; full postal addresses are not provided by Caterpoint API. Both billing and shipping addresses are set to the same ship-to location ID.
- **Original Data**: The `_original` field in normalized responses preserves the raw Caterpoint data for reference and troubleshooting.

### SKU Matching

The `items[].sku` field must match the `stock_code` in your `backend\models\Productsbe` for the order importer to find and link the product. If a SKU doesn't match:
- Order importer skips that line item (warning logged)
- Check your product master data for SKU mapping

### Order Status

Orders created from Caterpoint via OrderImporter will have:
- `status` = "Paid" (Caterpoint orders are pre-approved)
- `billingstatus` = "Paid"
- A payment response record created with `status = 'Paid'`

## API Endpoints

### Query Parameters

- `format`: Response format (`json`, `xml`, or `html` - default: `html`)
- `normalize`: Enable Magento normalization (`0` or `1` - default: `0`)
- `cursor`: Pagination cursor for fetching next batch
- `limit`: Number of orders to fetch per request (default: `1`)

### Examples

**HTML View (default):**
```
GET /NGauge/caterpoint/order/fetch-raw
```

**JSON Raw (original Caterpoint format):**
```
GET /NGauge/caterpoint/order/fetch-raw?format=json
```

**JSON Normalized (Magento format):**
```
GET /NGauge/caterpoint/order/fetch-raw?format=json&normalize=1
```

**XML Normalized:**
```
GET /NGauge/caterpoint/order/fetch-raw?format=xml&normalize=1
```

## Implementation

### v1.2.0
- **New Component**: `PortalPeople\caterpoint\components\DataNormalizer`
- **New Methods**:
  - `DataNormalizer::normalizeOrder(array): array` - Normalize single order
  - `DataNormalizer::normalizeOrders(array): array` - Normalize order batch
  - `DataNormalizer::normalizeFromFetchRawResponse(array): array` - Normalize full response

### v1.2.1
- **New Component**: `PortalPeople\caterpoint\components\OrderImporter`
- **New Methods**:
  - `OrderImporter::newCaterPointOrder(array): ?Cartsbe` - Create backend order from normalized data
- **Independent Implementation**: No dependency on ahs-magento module
- **Updated Endpoints**: 
  - `OrderController::actionFetchRaw()` supports `normalize` parameter
  - Responses include `normalized` flag indicating transformation status

## Troubleshooting

### Orders Not Being Created

1. **Check product SKU availability**:
   - Verify the SKU in Caterpoint matches `backend\models\Productsbe::stock_code`
   - Query: `SELECT id, stock_code FROM productsbe WHERE stock_code IN ('15002K', '18001E', ...)`
   - If no match, NewMagentoOrder silently skips the line item

2. **Verify order was fetched**:
   - Check fetch-raw response has `success: true` and contains order data
   - Review Yii logs (`runtime/logs/app.log`) for format detection issues

3. **Check OrderImporter execution**:
   - Add error handling to catch exceptions:
     ```php
     try {
         $cart = \PortalPeople\caterpoint\components\OrderImporter::newCaterPointOrder($orderData);
         if (!$cart) {
             echo "Order import returned null - check Yii logs\n";
         } else {
             echo "Order created successfully: {$cart->id}\n";
         }
     } catch (\Exception $e) {
         echo "Exception: {$e->getMessage()}\n";
     }
     ```

4. **Validate buyer name parsing**:
   - Check `customer.firstname` and `customer.lastname` in normalized response
   - If only one word, firstname gets the full name and lastname is empty

5. **Review logs for errors**:
   - Check Yii warnings/errors: `tail -f runtime/logs/app.log | grep -i caterpoint`
   - Check payment method compatibility: ensure `caterpoint_order` payment method is recognized

### Debugging Order Creation

**Monitor the caterpoint-import log channel** for detailed debugging information:

```bash
# Tail logs for Caterpoint import events
tail -f runtime/logs/app.log | grep caterpoint-import
```

The OrderImporter automatically logs:
- `INFO`: Successful order creation
- `WARNING`: Skipped items (SKU not found)
- `ERROR`: Cart save failures, exceptions

**Check logs programmatically:**

```php
// In your order import loop
foreach ($response['data']['orders'] as $orderData) {
    $cart = \PortalPeople\caterpoint\components\OrderImporter::newCaterPointOrder($orderData);
    
    if (!$cart) {
        echo "Order import failed - check logs for error details\n";
        continue;
    }
    
    echo "Backend order created: {$cart->id}\n";
}
```

All operation details are automatically logged to the `caterpoint-import` channel by the OrderImporter.
