# Product API Testing Guide V2 - Simplified Pricing Model

This guide covers the **simplified pricing architecture** where prices are set at product/variant/channel level only.

## Key Architecture Principles

✅ **Pricing (Product/Variant/Channel Level):**

- All prices stored in `product_prices` table with `channel` column
- **When `channel = false`:** Creates one record with `channel = NULL` (standard pricing for all channels)
- **When `channel = true`:** Creates multiple records with specific channel values (e.g., `channel = 'pos'`, `channel = 'online'`)
- NO branch dimension in pricing - same price applies across all branches
- Each sales channel (pos, online, offline, etc.) can have different prices

✅ **Stock & Cost (Branch Level):**

- Stored in `product_inventory` + `stock_update` tables
- Each branch maintains its own stock quantity and cost price
- Inventory tracking is ALWAYS branch-specific

⚠️ **Important Note:**

- The `channel` flag determines how the `channel` column is set in `product_prices`:
    - `channel = false` → One record with `channel = NULL` (applies to all channels)
    - `channel = true` → Multiple records with specific channel values (pos, online, etc.)
- The `sales_channels` parameter specifies which channels to create when `channel = true` (e.g., "pos", "online", "offline")
- Stock and cost are ALWAYS maintained per branch via `inventory` array

---

## Quick Reference: Channel Pricing Options

When `channel = true`, you have TWO ways to set prices:

### Option 1: Same Price for All Channels (Backward Compatible)

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

**Result:** Both POS and Online get the same price (100/80)

### Option 2: Different Price per Channel ⭐ RECOMMENDED

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

**Result:** POS gets 100/80, Online gets 120/95 (different prices!)

**For Variants:**

```json
{
    "price": {
        "variants": {
            "var-1": {
                "pos": { "retail": 50, "wholesale": 40 },
                "online": { "retail": 60, "wholesale": 48 }
            }
        }
    }
}
```

---

## Channel Flag Decision Guide

### When to use `channel = false` (Standard Pricing)?

✅ **Use Cases:**

- Same price for all sales channels (POS, Online, Offline)
- Simple pricing structure
- Price consistency across all channels

📦 **Database:**

- Stores in: `product_prices` table
- Records created: 1 per product (or 1 per variant if has_variants=true)

### When to use `channel = true` (Channel Pricing)?

✅ **Use Cases:**

- Different prices for different sales channels
- POS price ≠ Online price ≠ Offline price
- Channel-specific promotions or pricing strategies

📦 **Database:**

- Stores in: `product_prices` table with `channel` column set to specific channel values
- Records created: 1 per channel (or variants × channels if has_variants=true)

### Decision Flowchart

```
Do you need different prices for different sales channels?
│
├─ NO  → channel = false → Store in product_prices with channel=NULL
│         ✓ Single price applies to all channels
│         ✓ Simpler data structure
│
└─ YES → channel = true → Store in product_prices with specific channel values
          ✓ Each channel can have separate prices
          ✓ More flexibility in pricing strategy
          ✓ Specify channels in sales_channels parameter
```

---

## Base Configuration

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

**Headers:**

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

---

## Scenario 1: Simple Product - No Channel

**Endpoint:** `POST /products`

**Request:**

```json
{
    "name": "Simple Product",
    "slug": "simple-product",
    "sku": "SP-001",
    "category_id": 1,
    "brand_id": 1,
    "unit_id": 1,
    "type": "goods",
    "status": 1,
    "has_codes": true,
    "track_inventory": true,
    "has_variants": false,
    "channel": false,

    "product_code": "PC-001",
    "barcode": "123456789",
    "description": "Simple product description",

    "inventory": [
        {
            "branch_id": 1,
            "stock_qty": 100,
            "cost_price": 50
        },
        {
            "branch_id": 2,
            "stock_qty": 75,
            "cost_price": 48
        }
    ],

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

**Result:**

- Creates 1 price record in `product_prices` (no branch_id)
- Creates 2 inventory records in `product_inventory` (branch 1 & 2 with different costs)

---

## Scenario 2: Simple Product - WITH Channel (Single - Same Price)

**Request:**

```json
{
    "name": "Simple Product - POS Channel",
    "slug": "simple-product-pos",
    "sku": "SP-002",
    "category_id": 1,
    "brand_id": 1,
    "unit_id": 1,
    "status": 1,
    "has_codes": false,
    "track_inventory": true,
    "has_variants": false,
    "channel": true,
    "sales_channels": "pos",

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

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

**Result:**

- Creates 1 record in `product_prices` with `channel = 'pos'`
- Creates 1 inventory record for branch 1

---

## Scenario 3: Simple Product - Multi-Channel (Same Price for All Channels)

**Request:**

```json
{
    "name": "Simple Product - Multi Channel Same Price",
    "slug": "simple-product-multi-channel-same",
    "sku": "SP-003",
    "category_id": 1,
    "brand_id": 1,
    "unit_id": 1,
    "status": 1,
    "has_codes": false,
    "track_inventory": true,
    "has_variants": false,
    "channel": true,
    "sales_channels": "pos,online,offline",

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

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

**Result:**

- Creates 3 records in `product_prices` (pos, online, offline - all with same price)
- Creates 1 inventory record

---

## Scenario 4: Simple Product - Multi-Channel (Different Price per Channel) ⭐ NEW

**This is what you need for POS vs Online different pricing!**

**Request:**

```json
{
    "name": "Simple Product - Different Price per Channel",
    "slug": "simple-product-diff-channel-price",
    "sku": "SP-004",
    "category_id": 1,
    "brand_id": 1,
    "unit_id": 1,
    "status": 1,
    "has_codes": false,
    "track_inventory": true,
    "has_variants": false,
    "channel": true,
    "sales_channels": "pos,online",

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

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

**Result:**

- Creates 2 records in `product_prices`:
    - POS channel: retail=100, wholesale=80
    - Online channel: retail=120, wholesale=95
- Creates 2 inventory records (different costs per branch)

---

## Scenario 5: Variant Product - No Channel

**Request:**

```json
{
    "name": "T-Shirt with Variants",
    "slug": "t-shirt-variants",
    "sku": "VP-001",
    "category_id": 1,
    "brand_id": 1,
    "unit_id": 1,
    "status": 1,
    "has_codes": true,
    "track_inventory": true,
    "has_variants": true,
    "channel": false,

    "inventory": [
        {
            "branch_id": 1,
            "stock_qty": 200,
            "cost_price": 30
        },
        {
            "branch_id": 2,
            "stock_qty": 150,
            "cost_price": 32
        }
    ],

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

    "product_variants": [
        {
            "id": "var-1",
            "title": "Small",
            "sku": "VP-001-S"
        },
        {
            "id": "var-2",
            "title": "Medium",
            "sku": "VP-001-M"
        },
        {
            "id": "var-3",
            "title": "Large",
            "sku": "VP-001-L"
        }
    ],

    "price": {
        "variants": {
            "var-1": {
                "retail": 50,
                "wholesale": 40
            },
            "var-2": {
                "retail": 55,
                "wholesale": 45
            },
            "var-3": {
                "retail": 60,
                "wholesale": 50
            }
        }
    }
}
```

**Result:**

- Creates 3 price records in `product_prices` (one per variant, no branch_id)
- Creates 2 inventory records (different costs per branch)

---

## Scenario 5: Variant Product - No Channel

**Request:**

```json
{
    "name": "T-Shirt with Variants",
    "slug": "t-shirt-variants",
    "sku": "VP-001",
    "category_id": 1,
    "brand_id": 1,
    "unit_id": 1,
    "status": 1,
    "has_codes": true,
    "track_inventory": true,
    "has_variants": true,
    "channel": false,

    "inventory": [
        {
            "branch_id": 1,
            "stock_qty": 200,
            "cost_price": 30
        },
        {
            "branch_id": 2,
            "stock_qty": 150,
            "cost_price": 32
        }
    ],

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

    "product_variants": [
        {
            "id": "var-1",
            "title": "Small",
            "sku": "VP-001-S"
        },
        {
            "id": "var-2",
            "title": "Medium",
            "sku": "VP-001-M"
        },
        {
            "id": "var-3",
            "title": "Large",
            "sku": "VP-001-L"
        }
    ],

    "price": {
        "variants": {
            "var-1": {
                "retail": 50,
                "wholesale": 40
            },
            "var-2": {
                "retail": 55,
                "wholesale": 45
            },
            "var-3": {
                "retail": 60,
                "wholesale": 50
            }
        }
    }
}
```

**Result:**

- Creates 3 price records in `product_prices` (one per variant, no branch_id)
- Creates 2 inventory records (different costs per branch)

---

## Scenario 6: Variant Product - WITH Channel (Same Price for All Channels)

**Request:**

```json
{
    "name": "T-Shirt - Channel Same Price",
    "slug": "t-shirt-channel-same",
    "sku": "VP-002",
    "category_id": 1,
    "brand_id": 1,
    "unit_id": 1,
    "status": 1,
    "has_codes": true,
    "track_inventory": true,
    "has_variants": true,
    "channel": true,
    "sales_channels": "pos,online",

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

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

    "product_variants": [
        {
            "id": "var-1",
            "title": "Small",
            "sku": "VP-002-S"
        },
        {
            "id": "var-2",
            "title": "Large",
            "sku": "VP-002-L"
        }
    ],

    "price": {
        "variants": {
            "var-1": {
                "retail": 60,
                "wholesale": 50
            },
            "var-2": {
                "retail": 70,
                "wholesale": 60
            }
        }
    }
}
```

**Result:**

- Creates 4 records in `product_prices` (2 variants × 2 channels, all with same prices)
- Creates 1 inventory record

---

## Scenario 7: Variant Product - WITH Channel (Different Price per Channel) ⭐ NEW

**This is what you need for variant products with POS vs Online different pricing!**

**Request:**

```json
{
    "name": "T-Shirt - Different Price per Channel",
    "slug": "t-shirt-diff-channel-price",
    "sku": "VP-003",
    "category_id": 1,
    "brand_id": 1,
    "unit_id": 1,
    "status": 1,
    "has_codes": true,
    "track_inventory": true,
    "has_variants": true,
    "channel": true,
    "sales_channels": "pos,online",

    "inventory": [
        {
            "branch_id": 1,
            "stock_qty": 100,
            "cost_price": 30
        },
        {
            "branch_id": 2,
            "stock_qty": 80,
            "cost_price": 32
        }
    ],

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

    "product_variants": [
        {
            "id": "var-1",
            "title": "Small",
            "sku": "VP-003-S"
        },
        {
            "id": "var-2",
            "title": "Large",
            "sku": "VP-003-L"
        }
    ],

    "price": {
        "variants": {
            "var-1": {
                "pos": {
                    "retail": 50,
                    "wholesale": 40
                },
                "online": {i 
                    "retail": 60,
                    "wholesale": 48
                }
            },
            "var-2": {
                "pos": {
                    "retail": 60,
                    "wholesale": 50
                },
                "online": {
                    "retail": 75,
                    "wholesale": 60
                }
            }
        }
    }
}
```

**Result:**

- Creates 4 records in `product_prices` with DIFFERENT prices:
    - Small + POS: retail=50, wholesale=40
    - Small + Online: retail=60, wholesale=48
    - Large + POS: retail=60, wholesale=50
    - Large + Online: retail=75, wholesale=60
- Creates 2 inventory records (different costs per branch)

---

## Summary Table

| Scenario | Has Variants | Channel Flag | Price Structure           | Price Records Created                        | Inventory Records Created |
| -------- | ------------ | ------------ | ------------------------- | -------------------------------------------- | ------------------------- |
| 1        | No           | false        | Standard                  | 1 in `product_prices`                        | Per branch (with costs)   |
| 2        | No           | true         | Same for all channels     | 1 in `product_prices` (pos)                  | Per branch (with costs)   |
| 3        | No           | true         | Same for all channels     | 3 in `product_prices` (same price)           | Per branch (with costs)   |
| 4 ⭐     | No           | true         | **Different per channel** | C in `product_prices` (different prices)     | Per branch (with costs)   |
| 5        | Yes          | false        | Standard                  | V in `product_prices` (one per variant)      | Per branch (with costs)   |
| 6        | Yes          | true         | Same for all channels     | V × C in `product_prices` (same prices)      | Per branch (with costs)   |
| 7 ⭐     | Yes          | true         | **Different per channel** | V × C in `product_prices` (different prices) | Per branch (with costs)   |

**Legend:**  
V = Number of variants  
C = Number of channels

**Key Points:**

- `channel = false` → Prices in `product_prices` table (standard pricing)
- `channel = true` + flat price structure → Same price for ALL channels
- `channel = true` + nested channel structure → **DIFFERENT price PER channel** ⭐
- Inventory is ALWAYS per branch, regardless of pricing model

**Price Structure Examples:**

````json
// Same price for all channels (backward compatible)
"price": {
    "retail": 100,
    "wholesale": 80
}

// Different price per channel ⭐ NEW
"price": {
    "pos": { "retail": 100, "wholesale": 80 },
    "online": { "retail": 120, "wholesale": 95 }
}

---

## Key Architecture Notes

### Pricing Tables

All pricing is stored in the `product_prices` table with channel-aware design:

#### `product_prices` Table Structure

✅ **Schema:**

- `channel` column: NULL or specific channel value ('pos', 'online', 'offline', etc.)
- `product_id`: Links to product
- `product_variant_id`: NULL for simple products, variant ID for variant products
- Retail price, wholesale price, special prices
- Bundle pricing configurations
- Minimum wholesale quantities

#### When `channel = false` (Standard Pricing)

✅ **Behavior:**

- Creates ONE record with `channel = NULL`
- Price applies to ALL sales channels
- Simpler pricing structure

✅ **Pricing dimensions:**

- Product (simple vs variant)
- Variant (if has_variants = true)

#### When `channel = true` (Channel-Specific Pricing)

✅ **Behavior:**

- Creates MULTIPLE records, one per channel
- Each record has `channel` set to specific value ('pos', 'online', etc.)
- Different prices for different sales channels

✅ **Pricing dimensions:**

- Product (simple vs variant)
- Variant (if has_variants = true)
- Channel (pos, online, offline, facebook, crm, etc.)

### Inventory (Branch Level)

✅ **What it stores:**

- Stock quantity per branch
- Cost price per branch
- Reorder levels per branch
- Variant-specific inventory (if applicable)

✅ **Inventory dimensions:**

- Branch ID (always required)
- Product ID
- Variant ID (if has_variants = true)

### Stock Updates (Branch Level Tracking)

✅ **What it tracks:**

- All stock changes per branch
- Cost price changes per branch
- Update types: initial_stock, manual, system
- Manual types: increase, decrease, setup
- Reference and notes for audit trail

---

## Request Structure Explanation

### Simple Product (channel = false - Standard Pricing)

Prices stored in `product_prices` table:

```json
{
    "channel": false,
    "price": {
        "retail": 100,
        "wholesale": 80,
        "special_price": null,
        "min_qty_wholesale": 10
    },
    "inventory": [
        {
            "branch_id": 1,
            "stock_qty": 100,
            "cost_price": 50  // Branch-specific cost
        },
        {
            "branch_id": 2,
            "stock_qty": 75,
            "cost_price": 48  // Different cost per branch
        }
    ]
}
````

### Simple Product (channel = true - SAME Price for All Channels)

Prices stored in `product_prices` table (same price duplicated per channel):

```json
{
    "channel": true,
    "sales_channels": "pos,online,offline", // Creates 3 separate price records

    "price": {
        "retail": 100,
        "wholesale": 80
    },
    "inventory": [
        {
            "branch_id": 1,
            "stock_qty": 100,
            "cost_price": 45
        }
    ]
}
```

**Result:** Creates 3 records in `product_prices`:

- POS channel: retail=100, wholesale=80
- Online channel: retail=100, wholesale=80
- Offline channel: retail=100, wholesale=80

### Simple Product (channel = true - DIFFERENT Price per Channel) ⭐ NEW

Prices stored in `product_prices` table (different price per channel):

```json
{
    "channel": true,
    "sales_channels": "pos,online",

    "price": {
        "pos": {
            "retail": 100,
            "wholesale": 80,
            "min_qty_wholesale": 10
        },
        "online": {
            "retail": 120,
            "wholesale": 95,
            "min_qty_wholesale": 5
        }
    },
    "inventory": [
        {
            "branch_id": 1,
            "stock_qty": 100,
            "cost_price": 45
        }
    ]
}
```

**Result:** Creates 2 records in `product_prices`:

- POS channel: retail=100, wholesale=80, min_qty=10
- Online channel: retail=120, wholesale=95, min_qty=5

### Variant Product (channel = false - Standard Pricing)

Prices stored in `product_prices` table:

```json
{
    "channel": false,
    "has_variants": true,
    "price": {
        "variants": {
            "var-1": {
                "retail": 50,
                "wholesale": 40
            },
            "var-2": {
                "retail": 60,
                "wholesale": 50
            }
        }
    },
    "inventory": [
        {
            "branch_id": 1,
            "stock_qty": 200,
            "cost_price": 30 // Branch-specific cost for all variants
        }
    ]
}
```

### Variant Product (channel = true - SAME Price for All Channels)

Prices stored in `product_prices` table (same price per variant, duplicated per channel):

```json
{
    "channel": true,
    "sales_channels": "pos,online",
    "has_variants": true,
    "price": {
        "variants": {
            "var-1": {
                "retail": 50,
                "wholesale": 40
            },
            "var-2": {
                "retail": 60,
                "wholesale": 50
            }
        }
    },
    "inventory": [
        {
            "branch_id": 1,
            "stock_qty": 200,
            "cost_price": 30
        }
    ]
}
```

**Result:** Creates 4 records in `product_prices`:

- var-1 + POS: retail=50, wholesale=40
- var-1 + Online: retail=50, wholesale=40
- var-2 + POS: retail=60, wholesale=50
- var-2 + Online: retail=60, wholesale=50

### Variant Product (channel = true - DIFFERENT Price per Channel) ⭐ NEW

Prices stored in `product_prices` table (different price per variant per channel):

```json
{
    "channel": true,
    "sales_channels": "pos,online",
    "has_variants": true,
    "price": {
        "variants": {
            "var-1": {
                "pos": {
                    "retail": 50,
                    "wholesale": 40
                },
                "online": {
                    "retail": 60,
                    "wholesale": 48
                }
            },
            "var-2": {
                "pos": {
                    "retail": 60,
                    "wholesale": 50
                },
                "online": {
                    "retail": 75,
                    "wholesale": 60
                }
            }
        }
    },
    "inventory": [
        {
            "branch_id": 1,
            "stock_qty": 200,
            "cost_price": 30
        }
    ]
}
```

**Result:** Creates 4 records in `product_prices` with DIFFERENT prices:

- var-1 + POS: retail=50, wholesale=40
- var-1 + Online: retail=60, wholesale=48
- var-2 + POS: retail=60, wholesale=50
- var-2 + Online: retail=75, wholesale=60

---

## Testing Checklist

**Standard Pricing (channel = false):**

- [ ] Test simple product → 1 record in `product_prices`
- [ ] Test variant product → V records in `product_prices`
- [ ] Test with multiple branches (different costs)
- [ ] Verify NO channel dimension in pricing

**Channel Pricing - Same Price (channel = true, flat structure):**

- [ ] Test simple product + single channel → 1 record in `product_prices`
- [ ] Test simple product + multi-channel → C records in `product_prices` (same price)
- [ ] Test variant product + single channel → V records in `product_prices`
- [ ] Test variant product + multi-channel → V×C records in `product_prices` (same price)
- [ ] Verify each channel has separate record with SAME price

**Channel Pricing - Different Price per Channel ⭐ NEW (channel = true, nested structure):**

- [ ] Test simple product + 2 channels with different prices → 2 records with DIFFERENT prices
- [ ] Test simple product + 3 channels with different prices → 3 records with DIFFERENT prices
- [ ] Test variant product + 2 channels with different prices → V×2 records with DIFFERENT prices
- [ ] Verify POS price ≠ Online price
- [ ] Verify each variant can have different prices per channel

**Inventory (Always Branch-Specific):**

- [ ] Test inventory creation per branch
- [ ] Test cost_price differences per branch
- [ ] Verify stock_update records are created
- [ ] Test update operations (price vs inventory changes)
- [ ] Verify inventory is independent of pricing model

**Integration Tests:**

- [ ] Test switching from channel=false to channel=true
- [ ] Test switching from same-price to different-price structure
- [ ] Verify pricing applies across all branches
- [ ] Verify inventory remains branch-specific

---

## Migration Notes

If migrating from the old branch-based pricing model:

1. **Pricing Migration:**
    - Old: `product_prices` had `branch_id` column
    - New: `product_prices` has NO `branch_id` column
    - Action: Remove `branch_id` from pricing records, keep only one price per product/variant/channel

2. **Inventory Migration:**
    - Old: Inventory might not have tracked cost_price per branch
    - New: `product_inventory` MUST have `cost_price` per branch
    - Action: Ensure all inventory records have branch_id and cost_price

3. **Stock Updates:**
    - New requirement: All stock changes tracked in `stock_update` table
    - Links to `inventory_id` which contains branch information
