# Inventory Auto-Marking and Dispatch Functionality

## Overview

This feature implements intelligent inventory management for mixed sales scenarios where items can be sold either:
1. **Via Scanning** - Each item has a unique UID/barcode that is scanned and tracked
2. **Randomly/Manually** - Items are sold without scanning specific UIDs

The system automatically marks inventory items as SOLD when sales are completed, ensuring quantity-based and UID-based inventory stay synchronized.

## Problem Statement

When items are sold:
- Some items may be scanned (tracked by UID)
- Some items may be sold randomly (no UID tracking)
- The system needs to automatically mark the remaining inventory items as SOLD
- After selling, there should be a dispatch option to remove items from the system

**Example Scenario:**
- Total quantity: 50 items
- 30 items sold via scanning (with inventory_item_id)
- 20 items sold randomly (without inventory_item_id)
- Result: All 50 items should be marked as SOLD, with the 20 random items auto-marked

## Implementation

### 1. Inventory Finalization Service

**File:** `server/modules/sales/services/inventoryFinalization.js`

**Function:** `finalizeInventoryForSale(saleId, transaction)`

**How it works:**
1. Retrieves all sale items for the sale
2. Groups items by `product_id + variant_id`
3. For each group:
   - Counts total quantity sold
   - Counts scanned items (those with `inventory_item_id`)
   - Calculates random items count: `totalQuantity - scannedCount`
4. **Marks scanned items as SOLD:**
   - Finds inventory items that were RESERVED (from sale creation)
   - Updates status to SOLD
   - Creates InventoryMovement records
5. **Auto-marks random items as SOLD:**
   - Finds available IN_STOCK inventory items (FIFO - oldest first)
   - Marks them as SOLD
   - Creates InventoryMovement records with reason 'SALE_FINALIZED_RANDOM'
6. Handles edge cases:
   - If quantity > 1 and inventory_item_id exists: 1 scanned, (quantity-1) random
   - If insufficient items available: logs warning and tracks errors

### 2. Integration with Payment Completion

**File:** `server/modules/payments/services/index.js`

**Integration Point:** `updateSaleStatusIfFullyPaid()`

When a sale is fully paid:
1. Sale status is updated to `PAID`
2. **Inventory finalization is automatically triggered**
3. Scanned items (RESERVED) are marked as SOLD
4. Random items are auto-marked as SOLD
5. Quantity and UID-based inventory are synchronized

### 3. Dispatch Functionality

**File:** `server/modules/sales/services/dispatch.js`

**Function:** `dispatchSale(saleId, userId, transaction)`

**Purpose:** Mark a sale as dispatched after items have been physically removed from inventory

**Features:**
- Only allows dispatch for PAID sales
- Prevents double-dispatch
- Records dispatcher and timestamp
- Verifies and syncs quantity-based inventory with UID-based inventory
- Auto-fixes any discrepancies

**Endpoint:** `POST /api/sales/:id/dispatch`

### 4. Database Schema Updates

**Migration:** `server/scripts/migrations/004_add_dispatched_to_sales.sql`

**New Fields in Sale Model:**
- `dispatched` (BOOLEAN, default: false) - Whether sale has been dispatched
- `dispatched_at` (DATETIME, nullable) - Timestamp when dispatched
- `dispatched_by` (BIGINT, nullable, FK to users) - User who dispatched

**Association:**
- `Sale.belongsTo(User, { foreignKey: 'dispatched_by', as: 'dispatcher' })`

## Usage

### Automatic Inventory Finalization

When a sale is paid:
```javascript
// Happens automatically in payment service
// No manual action required
```

### Manual Dispatch

```bash
# Dispatch a paid sale
POST /api/sales/:id/dispatch
Authorization: Bearer <token>

# Response:
{
  "success": true,
  "message": "Sale dispatched successfully",
  "data": {
    "saleId": 123,
    "dispatched": true,
    "dispatchedAt": "2026-01-09T17:00:00.000Z",
    "dispatchedBy": 1,
    "itemsProcessed": 50
  }
}
```

## Inventory Status Flow

1. **IN_STOCK** - Item is available in inventory
2. **RESERVED** - Item is reserved for a sale (when sale is created)
3. **SOLD** - Item has been sold (when sale is paid)
4. **DISPATCHED** - Sale is marked as dispatched (items physically removed)

## Sync Logic

The system maintains sync between:
- **Quantity-based inventory** (`Inventory.quantity`)
- **UID-based inventory** (`InventoryItem` count with status IN_STOCK)

**Sync happens:**
- During dispatch: Verifies and auto-fixes discrepancies
- After finalization: Both systems are updated consistently

**Formula:**
```
Inventory.quantity should equal COUNT(InventoryItems WHERE status = 'IN_STOCK')
```

## Error Handling

- **Insufficient items for random sales:**
  - Logs warning
  - Marks available items as SOLD
  - Returns error details in finalization result

- **Inventory discrepancies:**
  - Detected during dispatch
  - Auto-corrected by syncing quantity to match UID count
  - Logged for audit trail

## Testing Scenarios

### Scenario 1: All Random Sales
- Sale item: quantity=50, inventory_item_id=null
- Expected: 50 items auto-marked as SOLD

### Scenario 2: All Scanned Sales
- 50 sale items, each with quantity=1 and inventory_item_id
- Expected: 50 scanned items marked as SOLD

### Scenario 3: Mixed Sales
- 30 scanned items (with inventory_item_id)
- 20 random items (quantity=20, inventory_item_id=null)
- Expected: 30 scanned + 20 random = 50 items marked as SOLD

### Scenario 4: Partial Quantity with Scanning
- Sale item: quantity=5, inventory_item_id=123
- Expected: 1 scanned item + 4 random items = 5 items marked as SOLD

## Migration

Run the migration script to add dispatch fields:

```bash
mysql -u <user> -p <database> < server/scripts/migrations/004_add_dispatched_to_sales.sql
```

Or via Sequelize migration (if configured).

## Notes

- **FIFO (First In, First Out):** Random items are marked using oldest inventory first
- **Audit Trail:** All inventory movements are logged with reasons:
  - `SALE_FINALIZED` - Scanned items marked as SOLD
  - `SALE_FINALIZED_RANDOM` - Random items auto-marked as SOLD
- **Transaction Safety:** All operations use database transactions for atomicity
- **Idempotency:** Finalization and dispatch can be safely retried

