# Unified Order System

## Overview

The Unified Order System ensures that orders are created in Odoo FIRST, then synced to the local database with the Odoo ID as an idempotency key. This prevents duplicate orders and maintains a single source of truth.

## Architecture

### Order Creation Flow

```
1. Customer submits order → 
2. Create order in Odoo (get odoo_order_id) → 
3. Create local order with odoo_order_id → 
4. Return success to customer
```

### Key Principles

1. **Odoo First**: Always create orders in Odoo before creating them locally
2. **Idempotency**: Use `odoo_order_id` as the unique identifier to prevent duplicates
3. **Sync on Read**: When fetching orders, sync any missing orders from Odoo to local DB
4. **Single Source of Truth**: Odoo is the authoritative source for order data

## Implementation

### UnifiedOrderService

The `UnifiedOrderService` handles all order creation and synchronization:

```php
// Create a new order
$unifiedOrderService = app(UnifiedOrderService::class);
$salesOrder = $unifiedOrderService->createOrder($customer, $orderData);

// Get or create order from Odoo (idempotent)
$salesOrder = $unifiedOrderService->getOrCreateFromOdoo($odooOrderId, $customer);

// Sync order status from Odoo
$unifiedOrderService->syncOrderStatus($salesOrder);
```

### Database Schema

The `sales_orders` table has a unique constraint on `odoo_order_id`:

```sql
ALTER TABLE sales_orders ADD UNIQUE KEY sales_orders_odoo_order_id_unique (odoo_order_id);
```

This ensures that:
- Each Odoo order can only be synced once to the local database
- Duplicate order creation attempts will fail gracefully
- NULL values are allowed (for orders not yet synced to Odoo)

## Benefits

### 1. No More Duplicates

Before:
- Order created locally → Synced to Odoo → Duplicate entries
- Odoo orders fetched → Portal orders fetched → Shown twice

After:
- Order created in Odoo → Synced to local DB once → Single entry
- All orders have `odoo_order_id` → Easy deduplication

### 2. Consistent Order Numbers

- Odoo generates the authoritative order number
- Local database uses the same order number
- No confusion about which order is which

### 3. Real-time Sync

- Orders are synced from Odoo when listing orders
- Missing orders are automatically pulled from Odoo
- Status updates are synced on demand

### 4. Idempotency

- Multiple sync attempts for the same order are safe
- `getOrCreateFromOdoo()` checks for existing orders first
- Unique constraint prevents accidental duplicates

## Migration Guide

### For Existing Orders

If you have existing orders with duplicates, run this cleanup:

```php
// Find duplicate orders (same odoo_order_id)
$duplicates = DB::table('sales_orders')
    ->select('odoo_order_id', DB::raw('COUNT(*) as count'))
    ->whereNotNull('odoo_order_id')
    ->groupBy('odoo_order_id')
    ->having('count', '>', 1)
    ->get();

// Keep the first order, delete the rest
foreach ($duplicates as $duplicate) {
    $orders = SalesOrder::where('odoo_order_id', $duplicate->odoo_order_id)
        ->orderBy('created_at', 'asc')
        ->get();
    
    // Keep the first one
    $orders->shift();
    
    // Delete the rest
    foreach ($orders as $order) {
        $order->delete();
    }
}
```

### Running the Migration

```bash
php artisan migrate
```

This will add the unique constraint to `odoo_order_id`.

## API Changes

### SalesOrderController

#### Before:
```php
// Created order locally first
$salesOrder = SalesOrder::create([...]);

// Then tried to sync to Odoo (sometimes failed)
if ($customer->odoo_partner_id) {
    $odooOrderId = $odooService->createSaleOrder(...);
    $salesOrder->update(['odoo_order_id' => $odooOrderId]);
}
```

#### After:
```php
// Create in Odoo first, then local
$unifiedOrderService = app(UnifiedOrderService::class);
$salesOrder = $unifiedOrderService->createOrder($customer, $orderData);
```

### Order Listing

#### Before:
```php
// Only showed local orders
$orders = SalesOrder::where('customer_id', $user->id)->get();
```

#### After:
```php
// Syncs from Odoo, then shows all orders
$orders = SalesOrder::where('customer_id', $user->id)->get();

// Sync missing orders from Odoo
if ($user->odoo_partner_id) {
    $odooOrders = $odooService->getCustomerOrders($user->odoo_partner_id);
    foreach ($odooOrders as $odooOrder) {
        $unifiedOrderService->getOrCreateFromOdoo($odooOrder['id'], $user);
    }
}
```

## Error Handling

### Odoo Creation Fails

If order creation in Odoo fails, the entire operation fails:

```php
try {
    $salesOrder = $unifiedOrderService->createOrder($customer, $orderData);
} catch (Exception $e) {
    // Order was NOT created locally
    // Customer can retry
    return response()->json(['error' => 'Failed to create order'], 500);
}
```

### Sync Fails

If syncing from Odoo fails, local orders are still shown:

```php
try {
    // Sync from Odoo
} catch (Exception $e) {
    Log::warning('Failed to sync from Odoo');
    // Continue with local orders
}
```

## Testing

### Test Order Creation

```php
$customer = Customer::factory()->create(['odoo_partner_id' => 123]);
$orderData = ['total_amount' => 100, ...];

$unifiedOrderService = app(UnifiedOrderService::class);
$order = $unifiedOrderService->createOrder($customer, $orderData);

// Assert order was created in Odoo
$this->assertNotNull($order->odoo_order_id);

// Assert order was created locally
$this->assertDatabaseHas('sales_orders', [
    'odoo_order_id' => $order->odoo_order_id,
    'customer_id' => $customer->id
]);
```

### Test Idempotency

```php
$odooOrderId = 456;

// First call creates the order
$order1 = $unifiedOrderService->getOrCreateFromOdoo($odooOrderId, $customer);

// Second call returns the same order
$order2 = $unifiedOrderService->getOrCreateFromOdoo($odooOrderId, $customer);

$this->assertEquals($order1->id, $order2->id);
```

## Monitoring

### Logs to Watch

- `Order created in Odoo first` - Successful Odoo creation
- `Order created in local database` - Successful local creation
- `Order already exists locally` - Idempotency working
- `Failed to create order in Odoo` - Odoo creation failed
- `Failed to sync order from Odoo` - Sync failed

### Metrics to Track

- Orders with `odoo_order_id` vs without
- Failed order creations
- Duplicate order attempts (should be 0)
- Sync latency

## Future Improvements

1. **Webhook Integration**: Listen for Odoo order updates instead of polling
2. **Batch Sync**: Sync multiple orders in one API call
3. **Conflict Resolution**: Handle cases where local and Odoo data diverge
4. **Offline Mode**: Queue orders when Odoo is unavailable
