# Product Update API Guide

This guide covers how to update products using the simplified pricing architecture where prices are set at product/variant/channel level only.

## Key Points for Updates

✅ **Update Behavior:**

- Updates are **destructive** for pricing - all existing prices are deleted and recreated
- Variants and options are also deleted and recreated
- **Inventory records are updated (not deleted)** - preserves inventory_id and stock history
- Other fields (codes, description, media, etc.) use `updateOrCreate` pattern

✅ **What Gets Preserved:**

- Product ID remains the same
- **Inventory IDs remain the same** - stock history is never lost
- Activity logs are maintained
- **Complete stock change history** is tracked in `stock_update` table

✅ **What Gets Replaced:**

- All pricing records
- All variant and option records

---

## Base Configuration

**Base URL:** `{{BASE_URL}}/products/{product_id}`

**Method:** `PUT` or `PATCH`

**Headers:**

```
Content-Type: application/json
Accept: application/json
Authorization: Bearer {{TOKEN}}
```

---

## Update Scenarios

### Scenario 1: Update Simple Product - Standard Pricing (channel = false)

**Endpoint:** `PUT /products/{id}`

**Use Case:** Update product name, price, and inventory

**Request:**

```json
{
    "name": "Updated Product Name",
    "slug": "updated-product-name",
    "sku": "SP-001-UPDATED",
    "category_id": 1,
    "brand_id": 1,
    "unit_id": 1,
    "type": "goods",
    "status": 1,
    "is_featured": true,

    "has_codes": true,
    "track_inventory": true,
    "has_variants": false,
    "channel": false,

    "description": "Updated product description",
    "short_description": "Updated short description",

    "product_code": "PC-001-UPDATED",
    "barcode": "123456789012",

    "inventory": [
        {
            "branch_id": 1,
            "stock_qty": 150,
            "cost_price": 50
        }
    ],

    "price": {
        "retail": 120,
        "wholesale": 95
    }
}
```

**Result:**

- Updates product basic info
- Deletes old price record
- Creates 1 new record in `product_prices` with `channel = NULL`
- Updates inventory record (same inventory_id preserved)
- Creates stock update record tracking the change (150 - 100 = +50)

---

### Scenario 2: Update Simple Product - Add Channel Pricing

**Use Case:** Convert from standard pricing to channel-specific pricing

**Request:**

```json
{
    "name": "Product Name",
    "slug": "product-name",
    "sku": "SP-001",
    "category_id": 1,
    "brand_id": 1,
    "unit_id": 1,
    "type": "goods",

    "has_codes": true,
    "track_inventory": true,
    "has_variants": false,
    "channel": true,
    "sales_channels": "pos,online",

    "inventory": [
        {
            "branch_id": 1,
            "stock_qty": 100,
            "cost_price": 45
        }
    ],

    "price": {
        "pos": {
            "retail": 100,
            "wholesale": 80
        },
        "online": {
            "retail": 120,
            "wholesale": 95
        }
    }
}
```

**Result:**

- Deletes old standard price record (channel = NULL)
- Creates 2 new records in `product_prices`:
    - `{channel: 'pos', retail: 100, wholesale: 80}`
    - `{channel: 'online', retail: 120, wholesale: 95}`

---

### Scenario 3: Update Channel Prices Only

**Use Case:** Just update the prices for existing channels

**Request:**

```json
{
    "name": "Product Name",
    "sku": "SP-001",
    "category_id": 1,
    "brand_id": 1,
    "unit_id": 1,

    "has_variants": false,
    "channel": true,
    "sales_channels": "pos,online",

    "inventory": [
        {
            "branch_id": 1,
            "stock_qty": 100,
            "cost_price": 45
        }
    ],

    "price": {
        "pos": {
            "retail": 110,
            "wholesale": 85
        },
        "online": {
            "retail": 130,
            "wholesale": 100
        }
    }
}
```

**Result:**

- Deletes 2 old price records
- Creates 2 new price records with updated values

---

### Scenario 4: Convert Simple to Variant Product

**Use Case:** Add variants to a previously simple product

**Request:**

```json
{
    "name": "T-Shirt",
    "slug": "t-shirt",
    "sku": "TS-001",
    "category_id": 1,
    "brand_id": 1,
    "unit_id": 1,

    "has_codes": true,
    "track_inventory": true,
    "has_variants": true,
    "channel": true,
    "sales_channels": "pos,online",

    "product_options": [
        {
            "id": "opt-1",
            "name": "Size",
            "values": ["Small", "Medium", "Large"]
        },
        {
            "id": "opt-2",
            "name": "Color",
            "values": ["Red", "Blue"]
        }
    ],

    "product_variants": [
        {
            "id": "var-1",
            "title": "Small / Red",
            "sku": "TS-001-SM-RED"
        },
        {
            "id": "var-2",
            "title": "Small / Blue",
            "sku": "TS-001-SM-BLUE"
        },
        {
            "id": "var-3",
            "title": "Medium / Red",
            "sku": "TS-001-MD-RED"
        },
        {
            "id": "var-4",
            "title": "Medium / Blue",
            "sku": "TS-001-MD-BLUE"
        }
    ],

    "inventory": [
        {
            "branch_id": 1,
            "product_variant_id": "var-1",
            "stock_qty": 20,
            "cost_price": 30
        },
        {
            "branch_id": 1,
            "product_variant_id": "var-2",
            "stock_qty": 25,
            "cost_price": 30
        },
        {
            "branch_id": 1,
            "product_variant_id": "var-3",
            "stock_qty": 30,
            "cost_price": 32
        },
        {
            "branch_id": 1,
            "product_variant_id": "var-4",
            "stock_qty": 35,
            "cost_price": 32
        }
    ],

    "price": {
        "variants": {
            "var-1": {
                "pos": {
                    "retail": 50,
                    "wholesale": 40
                },
                "online": {
                    "retail": 60,
                    "wholesale": 48
                }
            },
            "var-2": {
                "pos": {
                    "retail": 50,
                    "wholesale": 40
                },
                "online": {
                    "retail": 60,
                    "wholesale": 48
                }
            },
            "var-3": {
                "pos": {
                    "retail": 55,
                    "wholesale": 44
                },
                "online": {
                    "retail": 65,
                    "wholesale": 52
                }
            },
            "var-4": {
                "pos": {
                    "retail": 55,
                    "wholesale": 44
                },
                "online": {
                    "retail": 65,
                    "wholesale": 52
                }
            }
        }
    }
}
```

**Result:**

- Deletes old simple product price
- Creates product options and variant records
- Creates 8 price records in `product_prices` (4 variants × 2 channels)
- Creates 4 inventory records with variant links

---

### Scenario 5: Update Variant Product - Change Variants

**Use Case:** Add/remove variants or change variant structure

**Request:**

```json
{
    "name": "T-Shirt",
    "sku": "TS-001",
    "category_id": 1,
    "brand_id": 1,
    "unit_id": 1,

    "has_variants": true,
    "channel": true,
    "sales_channels": "pos,online,offline",

    "product_options": [
        {
            "id": "opt-1",
            "name": "Size",
            "values": ["Small", "Medium", "Large", "XL"]
        }
    ],

    "product_variants": [
        {
            "id": "var-1",
            "title": "Small",
            "sku": "TS-001-SM"
        },
        {
            "id": "var-2",
            "title": "Medium",
            "sku": "TS-001-MD"
        },
        {
            "id": "var-3",
            "title": "Large",
            "sku": "TS-001-LG"
        },
        {
            "id": "var-4",
            "title": "XL",
            "sku": "TS-001-XL"
        }
    ],

    "inventory": [
        {
            "branch_id": 1,
            "product_variant_id": "var-1",
            "stock_qty": 50,
            "cost_price": 30
        },
        {
            "branch_id": 1,
            "product_variant_id": "var-2",
            "stock_qty": 60,
            "cost_price": 30
        },
        {
            "branch_id": 1,
            "product_variant_id": "var-3",
            "stock_qty": 40,
            "cost_price": 32
        },
        {
            "branch_id": 1,
            "product_variant_id": "var-4",
            "stock_qty": 30,
            "cost_price": 32
        }
    ],

    "price": {
        "variants": {
            "var-1": {
                "retail": 50,
                "wholesale": 40
            },
            "var-2": {
                "retail": 52,
                "wholesale": 42
            },
            "var-3": {
                "retail": 55,
                "wholesale": 44
            },
            "var-4": {
                "retail": 58,
                "wholesale": 46
            }
        }
    }
}
```

**Result:**

- Deletes all old variants, options, and prices
- Creates new option structure (Size only, no Color)
- Creates 4 new variants
- Creates 12 price records (4 variants × 3 channels with same price)

---

### Scenario 6: Update Only Inventory (Keep Pricing)

**Use Case:** Just adjust stock levels without changing prices

**Request:**

```json
{
    "name": "Product Name",
    "sku": "SP-001",
    "category_id": 1,
    "brand_id": 1,
    "unit_id": 1,

    "has_variants": false,
    "channel": false,
    "track_inventory": true,

    "inventory": [
        {
            "branch_id": 1,
            "stock_qty": 200,
            "cost_price": 45
        }
    ],

    "price": {
        "retail": 100,
        "wholesale": 80
    }
}
```

**Result:**

- Updates inventory record (keeps same inventory_id)
- Creates stock update record: `+100` (200 - 100 = +100)
- Deletes and recreates price record (even though values are same)
- All previous stock_update history is preserved

---

## Important Update Behaviors

### 1. Variant ID Mapping

When updating variants, frontend IDs (like `"var-1"`) are mapped to database IDs:

```
Frontend: "var-1" → Database: 123
Frontend: "var-2" → Database: 124
```

This mapping ensures prices and inventory are linked to the correct database variant IDs.

### 2. Stock Change Tracking

Every inventory update creates a `stock_update` record:

```json
{
    "inventory_id": 45,
    "update_type": "manual",
    "manual_type": "increase",
    "quantity": 50,
    "cost_price": 45,
    "reference": "Product update",
    "note": "Stock adjusted during product update (+50)"
}
```

### 3. Activity Logging

All updates are logged with changes tracked:

```json
{
    "action": "product_updated",
    "changes": {
        "name": {
            "old": "Old Name",
            "new": "New Name"
        },
        "status": {
            "old": 1,
            "new": 0
        }
    }
}
```

---

## Common Update Patterns

### Pattern 1: Partial Update (Only Changed Fields)

You can send only the fields you want to update:

```json
{
    "name": "New Product Name",
    "status": 0
}
```

**Note:** For pricing, inventory, and variants, you must send the **complete** structure because these are deleted and recreated.

### Pattern 2: Price Structure Change

**From Standard to Channel:**

```json
{
    "channel": false,  // OLD
    "price": {
        "retail": 100
    }
}

// BECOMES ↓

{
    "channel": true,   // NEW
    "sales_channels": "pos,online",
    "price": {
        "pos": { "retail": 100 },
        "online": { "retail": 120 }
    }
}
```

**From Channel to Standard:**

```json
{
    "channel": true,   // OLD
    "sales_channels": "pos,online",
    "price": {
        "pos": { "retail": 100 },
        "online": { "retail": 120 }
    }
}

// BECOMES ↓

{
    "channel": false,  // NEW
    "price": {
        "retail": 100
    }
}
```

### Pattern 3: Add Channels

```json
{
    "sales_channels": "pos",  // OLD
    "price": {
        "retail": 100
    }
}

// BECOMES ↓

{
    "sales_channels": "pos,online,offline",  // NEW
    "price": {
        "retail": 100  // Same price for all 3 channels
    }
}
```

Or with different prices:

```json
{
    "sales_channels": "pos,online,offline",
    "price": {
        "pos": { "retail": 100 },
        "online": { "retail": 120 },
        "offline": { "retail": 90 }
    }
}
```

---

## Response Format

**Success Response:**

```json
{
    "success": true,
    "message": "Product updated successfully",
    "data": {
        "id": 123,
        "name": "Updated Product Name",
        "slug": "updated-product-name",
        "sku": "SP-001-UPDATED",
        "status": 1,
        "updated_at": "2026-03-05T10:30:00.000000Z"
    }
}
```

**Error Response:**

```json
{
    "success": false,
    "message": "Validation failed",
    "errors": {
        "sku": ["The SKU has already been taken."]
    }
}
```

---

## Validation Rules

### Required Fields

- `name` - Product name
- `sku` - Stock keeping unit (must be unique)
- `category_id` - Valid category ID
- `brand_id` - Valid brand ID
- `unit_id` - Valid unit ID

### Optional Fields

- `slug` - Auto-generated from name if not provided
- `type` - Default: "goods"
- `status` - Default: 1 (active)
- `is_featured` - Default: false
- All pricing and inventory fields

### Field Constraints

- `sku` - Must be unique across all products
- `slug` - Must be unique across all products
- `channel` - Must be boolean
- `sales_channels` - Must be comma-separated enum values
- `status` - Must be valid status enum value
- Prices - Must be decimal(10,2)
- Stock quantities - Must be integers

---

## Testing Checklist

### Basic Updates

- [ ] Update product name and description
- [ ] Update SKU (ensure uniqueness validation)
- [ ] Update status
- [ ] Update category/brand/unit

### Pricing Updates

- [ ] Update standard prices (channel = false)
- [ ] Update channel-specific prices (channel = true)
- [ ] Convert from standard to channel pricing
- [ ] Convert from channel to standard pricing
- [ ] Add new channels
- [ ] Remove channels
- [ ] Change prices for specific channels

### Inventory Updates

- [ ] Increase stock quantity (verify stock_update record created)
- [ ] Decrease stock quantity (verify stock_update record created)
- [ ] Update cost price
- [ ] Update reorder level

### Variant Updates

- [ ] Convert simple to variant product
- [ ] Convert variant to simple product
- [ ] Add new variants
- [ ] Remove variants
- [ ] Change variant structure (different options)
- [ ] Update variant prices

### Edge Cases

- [ ] Update with same values (verify idempotency)
- [ ] Update with partial data
- [ ] Update with invalid SKU (duplicate)
- [ ] Update deleted product (should fail)
- [ ] Update with invalid enum values
- [ ] Update with missing required fields

---

## Database Impact Summary

**Tables Affected by Update:**

| Table                           | Operation         | Notes                                                              |
| ------------------------------- | ----------------- | ------------------------------------------------------------------ |
| `products`                      | UPDATE            | Main product record updated in place                               |
| `product_codes`                 | UPDATE/CREATE     | Uses updateOrCreate                                                |
| `product_descriptions`          | UPDATE/CREATE     | Uses updateOrCreate                                                |
| `product_media`                 | UPDATE/CREATE     | Uses updateOrCreate                                                |
| `product_extra`                 | UPDATE/CREATE     | Uses updateOrCreate                                                |
| `product_shipping`              | UPDATE/CREATE     | Uses updateOrCreate                                                |
| `product_seo`                   | UPDATE/CREATE     | Uses updateOrCreate                                                |
| `product_prices`                | DELETE + CREATE   | All prices deleted and recreated                                   |
| `product_inventory`             | **UPDATE/CREATE** | **Uses updateOrCreate - preserves inventory_id and stock history** |
| `stock_update`                  | CREATE            | New records created for changes - **NEVER deleted**                |
| `product_options`               | DELETE + CREATE   | Deleted and recreated if has_variants                              |
| `product_option_values`         | DELETE + CREATE   | Deleted and recreated if has_variants                              |
| `product_variants`              | DELETE + CREATE   | Deleted and recreated if has_variants                              |
| `product_variant_option_values` | DELETE + CREATE   | Deleted and recreated if has_variants                              |
| `activity_log`                  | CREATE            | Activity logged for the update                                     |

---

## Best Practices

### 1. Always Send Complete Pricing Structure

Even if you're only changing one price, send the complete pricing structure:

```json
{
    "price": {
        "pos": { "retail": 100, "wholesale": 80 },
        "online": { "retail": 120, "wholesale": 95 },
        "offline": { "retail": 90, "wholesale": 72 }
    }
}
```

### 2. Track Stock Changes

Use the `stock_update` table to audit all stock changes:

```sql
SELECT * FROM stock_update
WHERE inventory_id = 123
ORDER BY created_at DESC;
```

### 3. Use Activity Logs

Monitor product changes via activity logs:

```sql
SELECT * FROM activity_log
WHERE relation_type = 'ProductApp\\Product\\Models\\Product'
AND relation_id = 123
ORDER BY created_at DESC;
```

### 4. Handle Variant Frontend IDs

When updating variants, maintain a consistent mapping between frontend temporary IDs and database IDs. The service handles this automatically.

### 5. Validate Before Updating

Always validate SKU uniqueness and other constraints before attempting update to avoid database errors.

---

## Quick Reference

### Update Standard Price

```json
POST /products/{id}
{
    "channel": false,
    "price": { "retail": 100, "wholesale": 80 }
}
```

### Update Channel Prices (Same for All)

```json
POST /products/{id}
{
    "channel": true,
    "sales_channels": "pos,online",
    "price": { "retail": 100, "wholesale": 80 }
}
```

### Update Channel Prices (Different)

```json
POST /products/{id}
{
    "channel": true,
    "sales_channels": "pos,online",
    "price": {
        "pos": { "retail": 100, "wholesale": 80 },
        "online": { "retail": 120, "wholesale": 95 }
    }
}
```

### Update Inventory Only

```json
POST /products/{id}
{
    "inventory": [
        { "branch_id": 1, "stock_qty": 200, "cost_price": 45 }
    ]
}
```
