# Price Inheritance from Models to Products

## Feature Overview

Products can now inherit their price from the assigned Model's `base_price` field. This allows you to:

1. Set a base price on a model (e.g., "New Balance 9060" at ₪599)
2. Create product variants (different colors) without specifying price
3. All variants automatically use the model's base price
4. Optionally override the price for specific product variants

## How It Works

### Backend Logic

1. **Product Price Field**: Now nullable - can be `NULL` in database
2. **Price Resolution**: 
   - If product has its own `price` set → use product price
   - If product `price` is `NULL` AND product is linked to a model → inherit model's `base_price`
   - If both are `NULL` → defaults to 0.0 (fallback)

3. **Validation**: When creating/updating a product:
   - If `price` is not provided → `model_id` must be provided
   - The assigned model must have a `base_price` set
   - API returns 400 error if validation fails

### API Changes

#### Product Create (`POST /api/products`)

**Request Body** - Price is now optional:
```json
{
  "name": "New Balance 9060 - Black/Red",
  "description": "Stylish colorway",
  "price": null,  // ← Can be null now
  "category_id": 1,
  "model_id": 5,  // ← Required when price is null
  "brand": "New Balance",
  "gender": "men",
  "images": ["..."],
  "sizes": []  // Can also inherit from model
}
```

**Validation**:
- ✅ Price provided, model optional → Valid
- ✅ Price null, model with base_price provided → Valid (inherits)
- ❌ Price null, no model → Error: "Price is required when no model is assigned"
- ❌ Price null, model without base_price → Error: "Model must have a base_price set if product price is not provided"

**Response**:
```json
{
  "id": 123,
  "name": "New Balance 9060 - Black/Red",
  "price": 599.0,  // ← Inherited from model's base_price
  "model_id": 5,
  ...
}
```

#### Product Get (`GET /api/products`, `GET /api/products/{id}`)

Products returned with computed price:
- If product.price is set → returns product.price
- If product.price is NULL → returns model.base_price
- Response always includes effective price

### Database Changes

**Migration**: `006_make_product_price_nullable.sql`

```sql
-- Make price column nullable
ALTER TABLE product ALTER COLUMN price DROP NOT NULL;

-- Add documentation
COMMENT ON COLUMN product.price IS 'Product-specific price. If NULL, inherits from model.base_price';
```

**To apply migration**:
```bash
# Connect to database
kubectl exec -it -n my-apps brand-master-db-0 -- psql -U brand_master_user -d brand_master_db

# Run migration
\i /path/to/migrations/006_make_product_price_nullable.sql

# Or use apply-migration script
./apply-migration.sh 006_make_product_price_nullable.sql
```

### Frontend Changes

#### Admin Panel - Product Form

**Price Field Behavior**:

1. **No Model Selected**:
   - Label: "Price *" (required)
   - Field is required
   - Placeholder: "Enter price"

2. **Model Selected (with base_price)**:
   - Label: "Price (Optional - inherits from model)"
   - Field is optional
   - Placeholder: "Model price: ₪599.00"
   - Help text: "Leave empty to use model's base price of ₪599.00"

3. **Model Selected (without base_price)**:
   - Label: "Price *" (required)
   - Field is required
   - Must enter price manually

**Form Validation**:
- Price field becomes optional dynamically when a model with base_price is selected
- JavaScript validation checks: `required={!formData.model_id || !models.find(m => m.id === parseInt(formData.model_id))?.base_price}`

## Usage Examples

### Example 1: Model with Variants (Recommended Use Case)

**Step 1**: Create a Model
```
Name: 9060
Brand: New Balance
Category: Sneakers
Base Price: ₪599
Sizes: 40, 41, 42, 43, 44, 45
```

**Step 2**: Create Product Variants (without specifying price)
```
Product 1:
  Name: New Balance 9060 - Black/Red
  Model: New Balance 9060
  Price: (leave empty) → Inherits ₪599
  Images: [black-red-1.jpg, black-red-2.jpg]

Product 2:
  Name: New Balance 9060 - White/Blue
  Model: New Balance 9060
  Price: (leave empty) → Inherits ₪599
  Images: [white-blue-1.jpg, white-blue-2.jpg]

Product 3:
  Name: New Balance 9060 - Limited Edition Gold
  Model: New Balance 9060
  Price: ₪899 → Override model price
  Images: [gold-1.jpg, gold-2.jpg]
```

**Result**:
- Products 1 & 2: Display at ₪599 (inherited)
- Product 3: Displays at ₪899 (overridden)
- If you update model's base_price to ₪649, Products 1 & 2 automatically reflect new price
- Product 3 remains at ₪899

### Example 2: Standalone Product (No Model)

```
Product:
  Name: Nike Air Max Classic
  Price: ₪399 (required - no model)
  Model: (none)
  Category: Sneakers
  Brand: Nike
```

**Result**: Works as before, price must be specified

### Example 3: Bulk Price Update

**Scenario**: You have 10 colorways of "New Balance 9060", all using the model

**To update all prices**:
1. Go to Models tab
2. Edit "New Balance 9060" model
3. Change base_price from ₪599 to ₪549
4. Save

**Result**: All 10 products now show ₪549 (unless individually overridden)

## Code Changes Summary

### Backend Files Modified

1. **`backend/app/models/product.py`**
   - Made `price` field nullable
   - Added `get_effective_price()` helper method

2. **`backend/app/schemas/product.py`**
   - Changed `ProductCreate.price` from required to `Optional[float]`
   - Changed `ProductResponse.price` to `Optional[float]`

3. **`backend/app/routers/products.py`**
   - Added validation in `create_new_product()` endpoint
   - Added price inheritance logic in all GET endpoints
   - Added price inheritance in `update_existing_product()`

4. **`backend/migrations/006_make_product_price_nullable.sql`** (NEW)
   - Database migration to make price column nullable

### Frontend Files Modified

1. **`frontend/src/pages/Admin.jsx`**
   - Updated `handleSubmit()` to send null price when empty
   - Enhanced price input field with dynamic placeholder and help text
   - Made price field conditionally required based on model selection

## Testing Checklist

### Backend Testing

- [ ] Create product with price and no model → Works
- [ ] Create product with price and model → Works (uses product price)
- [ ] Create product without price but with model (has base_price) → Works (inherits)
- [ ] Create product without price and no model → Error 400
- [ ] Create product without price with model (no base_price) → Error 400
- [ ] Update product, set model_id, remove price → Inherits model price
- [ ] Get product list → Returns computed prices correctly
- [ ] Get single product → Returns computed price correctly
- [ ] Search products → Returns computed prices correctly

### Frontend Testing

- [ ] Create product without model → Price field required
- [ ] Select model without base_price → Price field still required
- [ ] Select model with base_price → Price field becomes optional
- [ ] See model's base_price in placeholder
- [ ] Leave price empty → Product created successfully
- [ ] Product list shows inherited price correctly
- [ ] Edit product with inherited price → Shows empty price field with placeholder
- [ ] Update model base_price → All linked products show new price

### Database Testing

```sql
-- Check nullable constraint
SELECT column_name, is_nullable, data_type 
FROM information_schema.columns 
WHERE table_name = 'product' AND column_name = 'price';
-- Expected: is_nullable = 'YES'

-- Find products with inherited price
SELECT p.id, p.name, p.price as product_price, m.base_price as model_price
FROM product p
LEFT JOIN model m ON p.model_id = m.id
WHERE p.price IS NULL;

-- Find models with products
SELECT m.id, m.name, m.base_price, COUNT(p.id) as product_count
FROM model m
LEFT JOIN product p ON p.model_id = m.id
GROUP BY m.id, m.name, m.base_price
ORDER BY product_count DESC;
```

## Migration Instructions

### 1. Apply Database Migration

```bash
# Option A: Using migration script
cd ~/OneDrive/Desktop/gitea/brand-master/backend
./apply-migration.sh migrations/006_make_product_price_nullable.sql

# Option B: Manual execution
kubectl exec -it -n my-apps brand-master-db-0 -- \
  psql -U brand_master_user -d brand_master_db \
  -c "ALTER TABLE product ALTER COLUMN price DROP NOT NULL;"
```

### 2. Deploy Backend Changes

```bash
cd ~/OneDrive/Desktop/gitea/brand-master

# Build and push backend
docker build -t harbor.dvirlabs.com/my-apps/brand-master-backend:latest -f backend/Dockerfile backend/
docker push harbor.dvirlabs.com/my-apps/brand-master-backend:latest
```

### 3. Deploy Frontend Changes

```bash
# Build and push frontend
docker build -t harbor.dvirlabs.com/my-apps/brand-master-frontend:latest -f frontend/Dockerfile frontend/
docker push harbor.dvirlabs.com/my-apps/brand-master-frontend:latest
```

### 4. Upgrade Helm Release

```bash
cd ~/OneDrive/Desktop/gitea/my-apps
helm upgrade brand-master charts/brand-master-chart \
  -f manifests/brand-master/values.yaml \
  -n my-apps
```

### 5. Verify Deployment

```bash
# Check pods are running
kubectl get pods -n my-apps -l app.kubernetes.io/name=brand-master

# Check backend logs
kubectl logs -n my-apps -l app.kubernetes.io/component=backend --tail=50

# Test API
curl -H "Authorization: Bearer YOUR_TOKEN" \
  https://api-brand-master.dvirlabs.com/api/products | jq '.[0] | {id, name, price, model_id}'
```

## Troubleshooting

### Error: "Price is required when no model is assigned"

**Cause**: Trying to create product without price and without model

**Solution**: Either:
1. Provide a price value, OR
2. Select a model that has a base_price set

### Error: "Model must have a base_price set if product price is not provided"

**Cause**: Selected model doesn't have base_price configured

**Solution**: 
1. Edit the model and set a base_price, OR
2. Provide a specific price for this product

### Products showing ₪0.00 price

**Cause**: Product has no price AND linked model has no base_price

**Solution**:
1. Edit the model and set base_price, OR
2. Edit each product and set individual price

### Cannot update existing products

**Cause**: Old products might have validation issues

**Solution**: Ensure all existing products either have:
- A price value set, OR
- A model_id linked to a model with base_price

## Benefits

✅ **Easier Product Management**: Create multiple colorways without re-entering price
✅ **Bulk Price Updates**: Change model price → all variants update
✅ **Flexibility**: Can still override price for special editions
✅ **Cleaner Data**: One source of truth for model prices
✅ **Inheritance**: Sizes and price both inherit from models
✅ **Backwards Compatible**: Existing products with prices continue to work

## Future Enhancements

Potential improvements for future versions:

1. **Discount Inheritance**: Inherit discount_price from model
2. **Price History**: Track when prices are inherited vs. overridden
3. **Bulk Operations**: Update multiple products to use/remove model price
4. **Price Comparison**: Show difference between model price and overridden price
5. **Reporting**: Analytics on price inheritance usage
