# Plan: Replace Catalog Products with SalesProducts

## Overview
Replace the Catalog domain's Product/ProductVariant system with the unified SalesProducts system from the Accounting domain. The Supply domain will use SalesProducts with `section='procurement'`.

---

## Current State Analysis

### Catalog Domain (TO BE REPLACED)
| Component | File | Purpose |
|-----------|------|---------|
| Product | `app/Domains/Catalog/Products/Models/Product.php` | Table: `catalog_products` |
| ProductVariant | `app/Domains/Catalog/Products/Models/ProductVariant.php` | Table: `catalog_product_variants` |
| Inventory | `app/Domains/Catalog/Inventory/Models/Inventory.php` | Uses `variant_id` references |

### SalesProducts (TARGET)
| Component | File | Purpose |
|-----------|------|---------|
| Product | `app/Domains/Accounting/Sales/Models/Product.php` | Table: `sales_products`, has `section` enum |

### Supply Domain Dependencies (MUST UPDATE)
| File | Current Reference | Change Required |
|------|-------------------|-----------------|
| `SupplyOrderItem.php:144-147` | `temporaryProduct() -> Catalog\Products\Product` | Change to SalesProduct |
| `InventoryIntegrationService.php:5-9` | Uses Catalog Inventory models | Update to SalesProduct inventory |
| `ProductMatchingService.php:5-7` | Uses Catalog Product/Variant/VariantGroup | Update to SalesProduct |
| `TemporaryProductService.php:5-7` | Creates Catalog Products | Create SalesProducts |

---

## Implementation Plan

### Phase 1: Database Migrations

#### 1.1 Add Inventory Fields to SalesProducts
```
Migration: add_inventory_fields_to_sales_products_table.php
```
- Add `variant_id` (nullable, for backward compatibility during migration)
- Add inventory tracking fields if missing

#### 1.2 Update Catalog Inventory Tables
```
Migration: update_catalog_inventories_for_sales_products.php
```
Changes:
- Add `sales_product_id` column to `catalog_inventories` (nullable initially)
- Add `sales_product_id` column to `catalog_inventory_transactions`
- Add `sales_product_id` column to `catalog_inventory_reservations`
- Keep `variant_id` for backward compatibility during migration

#### 1.3 Data Migration
```
Migration: migrate_catalog_products_to_sales_products.php
```
Steps:
1. Copy `catalog_products` -> `sales_products` with `section='procurement'`
2. Flatten `catalog_product_variants` into individual `sales_products` (one per variant)
3. Map inventory from `catalog_inventories` to new tables
4. Create `catalog_to_sales_product_map` for reference tracking

#### 1.4 Update Supply Foreign Keys
```
Migration: update_supply_order_items_for_sales_products.php
```
- Change `temporary_product_id` FK from `catalog_products` to `sales_products`
- Add `sales_product_id` column for direct product reference

---

### Phase 2: Model Layer Changes

#### 2.1 Update SalesProduct Model
**File:** `app/Domains/Accounting/Sales/Models/Product.php`

Add:
```php
// Inventory relationship
public function inventories() {
    return $this->hasMany(ProductInventory::class, 'product_id');
}

// Supply-specific scopes
public function scopeByBrand($query, string $brand) {
    return $query->where('brand', $brand);
}

public function scopeByQualityGrade($query, string $grade) {
    return $query->where('quality_grade', $grade);
}

public function scopeActive($query) {
    return $query->where('status', true);
}
```

#### 2.2 Update Catalog Inventory Models
**Location:** `app/Domains/Catalog/Inventory/Models/`

Update existing models to support both `variant_id` and `sales_product_id`:
- `Inventory.php` - add `salesProduct()` relationship
- `InventoryTransaction.php` - add `salesProduct()` relationship
- `InventoryReservation.php` - add `salesProduct()` relationship

#### 2.3 Create ProcurementProduct Facade (Optional)
**File:** `app/Domains/Supply/Models/ProcurementProduct.php`

```php
class ProcurementProduct extends \App\Domains\Accounting\Sales\Models\Product
{
    protected static function booted()
    {
        parent::booted();
        static::addGlobalScope('procurement', fn($q) => $q->where('section', 'procurement'));
    }

    protected static function boot()
    {
        parent::boot();
        static::creating(fn($m) => $m->section = 'procurement');
    }
}
```

---

### Phase 3: Service Layer Updates

#### 3.1 Update InventoryIntegrationService
**File:** `app/Domains/Supply/Services/InventoryIntegrationService.php`

Changes:
```php
// Keep existing Catalog Inventory imports (no change needed)
use App\Domains\Catalog\Inventory\Models\Inventory;
use App\Domains\Catalog\Inventory\Models\InventoryReservation;
use App\Domains\Catalog\Inventory\Models\InventoryTransaction;

// Update methods to use sales_product_id instead of variant_id
// Example line 37:
- $inventory = Inventory::where('variant_id', $variantId)
+ $inventory = Inventory::where('sales_product_id', $productId)

// Update all method signatures to accept $productId instead of $variantId
```

#### 3.2 Update ProductMatchingService
**File:** `app/Domains/Supply/SubDomains/SupplyOrders/Services/ProductMatchingService.php`

Changes:
```php
// Line 5-7: Change imports
- use App\Domains\Catalog\Products\Models\Product;
- use App\Domains\Catalog\Products\Models\ProductVariant;
- use App\Domains\Catalog\Products\Models\ProductVariantGroup;
+ use App\Domains\Accounting\Sales\Models\Product;

// Update matching logic to query SalesProduct directly by sku/brand
public function matchProduct(string $partNumber, string $brand): ?Product
{
    $partNumberNormalized = trim(strtoupper($partNumber));
    $brandNormalized = trim(strtoupper($brand));

    return Product::procurement()
        ->whereRaw('UPPER(TRIM(sku)) = ?', [$partNumberNormalized])
        ->whereRaw('UPPER(TRIM(brand)) = ?', [$brandNormalized])
        ->where('status', true)
        ->first();
}
```

#### 3.3 Update TemporaryProductService
**File:** `app/Domains/Supply/SubDomains/SupplyOrders/Services/TemporaryProductService.php`

Changes:
```php
// Line 5-7: Change imports
- use App\Domains\Catalog\Products\Models\Product;
- use App\Domains\Catalog\Products\Enums\ProductStatus;
- use App\Domains\Catalog\Products\Enums\ProductType;
+ use App\Domains\Accounting\Sales\Models\Product;
+ use App\Domains\Accounting\Sales\Enums\ProductSection;

// Line 27-40: Update create call
$product = Product::create([
    'name' => "{$brand} - {$partNumber}",
    'description' => $unitNumber ? "Temporary product - Unit: {$unitNumber}" : "Temporary product pending verification",
    'brand' => $brand,
    'sku' => $partNumber,
    'section' => ProductSection::Catalog->value,
    'type' => 'product',
    'status' => false, // Inactive until approved
]);
```

---

### Phase 4: Model Relationship Updates

#### 4.1 Update SupplyOrderItem
**File:** `app/Domains/Supply/SubDomains/SupplyOrders/Models/SupplyOrderItem.php`

Change line 144-147:
```php
- public function temporaryProduct(): BelongsTo
- {
-     return $this->belongsTo(\App\Domains\Catalog\Products\Models\Product::class, 'temporary_product_id');
- }
+ public function temporaryProduct(): BelongsTo
+ {
+     return $this->belongsTo(\App\Domains\Accounting\Sales\Models\Product::class, 'temporary_product_id');
+ }

// Add new relationship
+ public function salesProduct(): BelongsTo
+ {
+     return $this->belongsTo(\App\Domains\Accounting\Sales\Models\Product::class, 'sales_product_id');
+ }
```

#### 4.2 Update CategoryItem (if products relationship exists)
**File:** `app/Domains/Catalog/Categories/Models/CategoryItem.php`

If `products()` relationship exists, update:
```php
- return $this->hasMany(\App\Domains\Catalog\Products\Models\Product::class, 'catalog_item_id');
+ return $this->hasMany(\App\Domains\Accounting\Sales\Models\Product::class, 'catalog_item_id');
```

---

### Phase 5: Event Listeners Updates

Update any listeners in `app/Domains/Supply/` that reference Catalog models:
- `ValidateProductsExistListener` - validate against SalesProduct
- `UpdateInventoryFromReceiptListener` - use new inventory models
- `ReserveIncomingInventoryListener` - use new reservation models

---

### Phase 6: Maintenance Domain (Minimal Changes)

**File:** `app/Domains/Maintenance/SubDomains/Visits/Models/MaintenanceVisitSparePart.php`

The `external_product_id` field is generic - add optional relationship:
```php
public function salesProduct(): BelongsTo
{
    return $this->belongsTo(\App\Domains\Accounting\Sales\Models\Product::class, 'external_product_id');
}
```

---

## Files to Modify Summary

### High Priority (Core Changes)
1. `app/Domains/Supply/Services/InventoryIntegrationService.php`
2. `app/Domains/Supply/SubDomains/SupplyOrders/Services/ProductMatchingService.php`
3. `app/Domains/Supply/SubDomains/SupplyOrders/Services/TemporaryProductService.php`
4. `app/Domains/Supply/SubDomains/SupplyOrders/Models/SupplyOrderItem.php`
5. `app/Domains/Accounting/Sales/Models/Product.php`

### Medium Priority (Updates/New Files)
6. `app/Domains/Catalog/Inventory/Models/Inventory.php` (update - add salesProduct relationship)
7. `app/Domains/Catalog/Inventory/Models/InventoryTransaction.php` (update - add salesProduct relationship)
8. `app/Domains/Catalog/Inventory/Models/InventoryReservation.php` (update - add salesProduct relationship)
9. `database/migrations/xxxx_update_catalog_inventories_for_sales_products.php` (create)
10. `database/migrations/xxxx_migrate_catalog_products_to_sales_products.php` (create)

### Low Priority (Optional)
11. `app/Domains/Supply/Models/ProcurementProduct.php` (create - facade)
12. `app/Domains/Maintenance/SubDomains/Visits/Models/MaintenanceVisitSparePart.php`

---

## Verification Plan

### 1. Data Migration Verification
- Verify all catalog products migrated to sales_products
- Verify inventory quantities match
- Verify foreign key references updated correctly

---

## Rollback Strategy

1. Keep `catalog_products` and `catalog_inventories` tables for 30 days
2. Create `catalog_to_sales_product_map` table for reference tracking
3. Maintain backup of all migrated data
4. Feature flag to toggle between old/new systems during transition
