# Transfer-During-Receiving API Documentation

## Overview

This document describes the enhanced mobile API functionality that enables transfer order creation during the receive order counting process. The implementation allows mobile users to allocate counted items to different destination warehouses in real-time.

## Workflow

### 1. Enhanced Receive Order Counting
When a mobile user submits a box for a **transferable** receive order, the API now returns transfer requirements along with the standard response.

### 2. Transfer Allocation
Users can then allocate the counted quantities to different destination warehouses using the new confirm-transfers endpoint.

## API Endpoints

### Enhanced: POST /api/update-receive-order-box

**Purpose**: Submit counted items for a box (existing functionality) + return transfer requirements (new)

**Request**: (Same as before)
```json
{
  "receive_order_id": 123,
  "box_name": "BOX-001",
  "lines": [
    {"barcode": "ABC123", "counted_qty": 10},
    {"barcode": "DEF456", "counted_qty": 5}
  ],
  "not_found": [...]
}
```

**Enhanced Response**: (For transferable orders only)
```json
{
  "status": true,
  "my_receive_order": { /* existing response */ },
  "message": "Box processing has been queued successfully.",
  "transfer_requirements": {
    "ABC123": {
      "barcode": "ABC123",
      "description": "Red Cotton T-Shirt",
      "counted_qty": 10,
      "destinations": [
        {
          "warehouse_id": 2,
          "warehouse_name": "Shop A Downtown",
          "qty_required": 4,
          "qty_fulfilled": 1,
          "qty_remaining": 3,
          "max_allowed": 3,
          "default_allocation_qty": 3,
          "is_partial_allocation": false,
          "destination_box_name": null
        },
        {
          "warehouse_id": 3,
          "warehouse_name": "Shop B Mall",
          "qty_required": 6,
          "qty_fulfilled": 2,
          "qty_remaining": 4,
          "max_allowed": 3,
          "default_allocation_qty": 0,
          "is_partial_allocation": true,
          "destination_box_name": null
        }
      ]
    }
  }
}
```

**Field Notes**

- `counted_qty` mirrors the total pieces counted for the barcode in the submitted box.
- `qty_required` remains the total requirement pulled from the transfer request line; show this for full context.
- `qty_remaining` highlights what is still outstanding prior to the current allocation.
- `max_allowed` caps how many pieces the UI can allocate to that destination for this submission (`min(qty_remaining, available counted qty)`).
- `default_allocation_qty` indicates what the API would allocate by default; `0` signals a partial scenario that needs operator input.
- `is_partial_allocation` is `true` when the counted quantity cannot fully satisfy the remaining requirement, allowing the UI to flag shortages.
- `destination_box_name` continues to surface the box name used in prior allocations when available.

### New: POST /api/confirm-transfers

**Purpose**: Allocate counted items to destination warehouses and create transfer orders

**Request**:
```json
{
  "receive_order_id": 123,
  "source_box_name": "BOX-001",
  "transfer_allocations": [
    {
      "barcode": "ABC123",
      "description": "Red Cotton T-Shirt",
      "codein": "RCT001",
      "style": "CASUAL",
      "colour": "Red",
      "size": "M",
      "allocations": [
        {
          "destination_warehouse_id": 2,
          "qty": 3,
          "destination_box_name": "SHOP-A-BOX-001"
        },
        {
          "destination_warehouse_id": 3,
          "qty": 2,
          "destination_box_name": "SHOP-B-BOX-001"
        }
      ]
    }
  ]
}
```

**Response**:
```json
{
  "status": true,
  "message": "Transfer confirmation has been queued for processing."
}
```

## Data Flow

### 1. Excel Import (Existing)
- Creates `TransferOrderRequestLines` (transfer requirements)
- Creates `ReceiveOrderLines` (items to receive)

### 2. Mobile Counting (Enhanced)
- Updates `ReceiveOrderLines` (existing)
- Returns transfer requirements (new)

### 3. Transfer Confirmation (New)
- Creates `TransferOrders` (parent records)
- Creates `TransferOrderLines` (actual transfer lines)
- Updates `TransferOrderRequestLines.qty_fulfilled`

### 4. Admin Processing (Existing)
- Reviews completed transfer orders
- Processes physical transfers

## Background Processing

All transfer operations are processed asynchronously using the `ProcessTransferConfirmation` job to ensure optimal mobile app performance.

### Job Responsibilities:
- Create parent `TransferOrder` records per destination warehouse
- Create `TransferOrderLine` records for each allocation
- Update `TransferOrderRequestLines.qty_fulfilled`
- Handle validation and error cases
- Maintain data consistency with transactions

## Validation Rules

### Transfer Confirmation Validation:
- Receive order must exist and be open
- Receive order must be marked as transferable
- Destination warehouses must exist
- Quantities must be positive integers
- Cannot exceed available unfulfilled requirements

### Business Logic:
- Prevents overfulfillment of transfer requests
- Maintains referential integrity
- Supports partial fulfillment scenarios
- Handles concurrent access safely

## Error Handling

### Common Error Scenarios:
1. **Non-transferable Order**: Returns 400 error
2. **Overfulfillment**: Prevents exceeding requested quantities
3. **Invalid Warehouse**: Validates destination warehouse existence
4. **Concurrent Updates**: Uses database transactions for consistency

### Error Response Format:
```json
{
  "status": false,
  "message": "Error description"
}
```

## Backward Compatibility

- Non-transferable receive orders work exactly as before
- Existing mobile apps continue to function without changes
- Transfer functionality is opt-in based on receive order configuration

## Mobile App Integration

### Recommended Flow:
1. User counts items normally.
2. Submit the box using `POST /api/update-receive-order-box` (or the async variant).
3. When the response includes `transfer_requirements`, iterate each barcode:
   - Read `counted_qty` to drive the badge/summary for pieces available this submission.
   - Build allocation controls per destination capped by `max_allowed`.
   - Pre-populate each control with `default_allocation_qty` (usually full requirement or `0` when the count is short).
   - Highlight destinations where `is_partial_allocation = true` to indicate a shortage and prompt follow-up.
   - Always show `qty_required` and `qty_remaining` so the operator sees the full context.
4. Validate client-side that the sum of entered quantities stays ≤ counted quantity and each entry ≤ `max_allowed` (the API revalidates, but the UI should prevent obvious errors).
5. Submit the chosen allocations via `POST /api/confirm-transfers`.
6. Refresh transfer requirements after confirmation to pick up the updated `qty_fulfilled`/`qty_remaining`.

### UI Considerations:
- Transfer allocation is optional (users can skip if they need to handle it later).
- Destinations where `default_allocation_qty = 0` and `is_partial_allocation = true` should be visually distinct (e.g., warning color or badge).
- Disable or clamp inputs when `max_allowed` is `0`.
- Show the outstanding `qty_remaining` after the operator has entered allocations so they see what’s left for future cartons.
- Provide clear feedback for a successful confirm call and refresh the requirements to avoid stale data.

## Database Schema

### Key Tables:
- `transfer_order_requests`: Parent transfer request records
- `transfer_order_request_lines`: Individual transfer requirements
- `transfer_orders`: Actual transfer orders (created by mobile)
- `transfer_order_lines`: Individual transfer lines (created by mobile)

### Key Relationships:
- `TransferOrderRequestLines.qty_fulfilled` tracks progress
- `TransferOrderLines` link to `TransferOrders` 
- Both link back to original `ReceiveOrder`

## Performance Considerations

- All heavy processing moved to background jobs
- Mobile responses remain fast and lightweight
- Database queries optimized with proper indexing
- Bulk operations used where possible

## Security

- All endpoints require Sanctum authentication
- User permissions validated per receive order
- Input validation prevents malicious data
- Database transactions ensure consistency

---

## Flutter Integration Cheat-Sheet

Use these notes when wiring the mobile client (see `receive_order_cubit.dart`):

1. **Model updates**
   - Extend the transfer requirement models with the new fields: `countedQty`, `maxAllowed`, `defaultAllocationQty`, `isPartialAllocation`, and `destinationBoxName`.
   - Preserve `qtyRequired`, `qtyFulfilled`, and `qtyRemaining` for full context.
2. **Cubit state**
   - Store the raw server payload so you can recalculate remaining availability after each user edit.
   - Track per-barcode totals to enforce `sum(selectedQty) <= countedQty`.
3. **UI binding**
   - Pre-fill the allocation input with `defaultAllocationQty`.
   - Clamp the input max to `maxAllowed`; disable the control when `maxAllowed == 0`.
   - Surface a warning badge when `isPartialAllocation` is true (e.g., “Short by X pcs” using `qtyRemaining - defaultAllocationQty`).
4. **Validation prior to submit**
   - Reject submissions where any selected quantity exceeds `maxAllowed` or when totals exceed `countedQty`.
   - If a destination is left blank, treat it as zero; you only need to send allocations the user confirmed.
5. **Submit**
   - POST `transfer_allocations` as today.
   - On success, refresh the current receive order boxes to fetch updated requirements and progress.
6. **Offline / retry**
   - Retain unsent allocations in local state so the operator can retry if the confirm call fails.

---

# Updated API Endpoints Documentation

> **Note:** The canonical response schema is described in the sections above (including `counted_qty`, `max_allowed`, `default_allocation_qty`, and `is_partial_allocation`). The legacy examples below remain for reference but should be interpreted with those additional fields.

## Enhanced Endpoints (13.10.2025)

### 1. Enhanced: GET /api/receive-order-boxes

**Purpose**: Retrieve boxes for a receive order with optional transfer requirements

**Request**:
```json
{
  "receive_order_id": 123,
  "is_open": true,
  "box_name": "BOX-001"
}
```

**Enhanced Response** (for transferable orders):
```json
{
  "status": true,
  "data": [
    {
      "id": 1,
      "name": "BOX-001",
      "is_open": false,
      "receive_order_lines": [...],
      "receive_order_not_found": [...],
      "receive_order_line_counts": [...]
    }
  ],
  "transfer_requirements": {
    "ABC123": {
      "barcode": "ABC123",
      "description": "Red Cotton T-Shirt",
      "destinations": [
        {
          "warehouse_id": 2,
          "warehouse_name": "Shop A Downtown",
          "qty_required": 4,
          "qty_fulfilled": 1,
          "qty_remaining": 3
        }
      ]
    }
  }
}
```

**Behavior**:
- Returns `transfer_requirements` **only if** `receive_order.transferable = true`
- Extracts barcodes from all receive order lines in the boxes
- Only includes unfulfilled requirements (`qty_fulfilled < qty`)
- Backward compatible - existing functionality unchanged

### 2. Enhanced: POST /api/update-receive-order-box-async

**Purpose**: Submit counted items for a box with transfer requirements (existing) + return transfer requirements (enhanced)

**Request**: (Same as before)
```json
{
  "receive_order_id": 123,
  "box_name": "BOX-001",
  "lines": [
    {"barcode": "ABC123", "counted_qty": 10}
  ]
}
```

**Enhanced Response** (for transferable orders):
```json
{
  "status": true,
  "my_receive_order": {
    "receive_orders.id": 123,
    "franchise": "Main Franchise",
    "warehouse": "Central Warehouse",
    "progress": 75,
    "total_pcs": 100,
    "total_counted": 75
  },
  "message": "Box processing has been queued successfully.",
  "transfer_requirements": {
    "ABC123": {
      "barcode": "ABC123",
      "description": "Red Cotton T-Shirt",
      "destinations": [
        {
          "warehouse_id": 2,
          "warehouse_name": "Shop A Downtown",
          "qty_required": 4,
          "qty_fulfilled": 0,
          "qty_remaining": 4
        }
      ]
    }
  }
}
```

**Behavior**:
- Returns `transfer_requirements` **only if** `receive_order.transferable = true` AND `lines` are provided
- Uses existing `getTransferRequirements()` method
- Maintains all existing functionality

### 3. Enhanced: POST /api/confirm-transfers

**Purpose**: Allocate counted items to destination warehouses (existing) + handle updates to existing allocations (enhanced)

**Request**: (Same as before)
```json
{
  "receive_order_id": 123,
  "source_box_name": "BOX-001",
  "transfer_allocations": [
    {
      "barcode": "ABC123",
      "allocations": [
        {
          "destination_warehouse_id": 2,
          "qty": 3,
          "destination_box_name": "SHOP-A-BOX-001"
        }
      ]
    }
  ]
}
```

**Enhanced Response**:
```json
{
  "status": true,
  "message": "Transfer allocations updated successfully."
}
```

**Enhanced Behavior**:
- **Detects Update vs New**: Automatically determines if this is updating existing allocations or creating new ones
- **Dual Processing Modes**:
  - **New Allocations**: Dispatches `ProcessTransferConfirmation` job (existing behavior)
  - **Updates**: Processes immediately using `updateExistingAllocations()` method
- **Smart Detection**: Checks for existing `transfer_orders` with same `receive_order_id` and barcode
- **Different Messages**: Returns appropriate success message for each mode

## Implementation Details

### Update Detection Logic

The system automatically detects updates using this logic:

```php
private function isUpdateAllocation(array $validatedData): bool
{
    foreach ($validatedData['transfer_allocations'] as $allocation) {
        $barcode = $allocation['barcode'];

        $existingTransfers = DB::table('transfer_order_lines')
            ->join('transfer_orders', 'transfer_orders.id', '=', 'transfer_order_lines.transfer_order_id')
            ->join('items', 'items.id', '=', 'transfer_order_lines.item_id')
            ->where('transfer_orders.receive_order_id', $validatedData['receive_order_id'])
            ->where('items.barcode', $barcode)
            ->where('transfer_orders.status', '!=', 'closed')
            ->exists();

        if ($existingTransfers) {
            return true; // This is an update
        }
    }
    return false; // This is a new allocation
}
```

### Update Processing

When updates are detected, the system:

1. **Finds existing TransferOrders** for the receive_order_id and destination_warehouse_id
2. **Updates or creates TransferOrderLines** within existing TransferOrders
3. **Updates TransferOrderRequestLines.qty_fulfilled** in real-time
4. **Maintains referential integrity** with existing data structures

### Error Handling

**Enhanced Error Responses**:
```json
{
  "status": false,
  "message": "This receive order is not configured for transfers."
}
```

**Validation Errors**:
- Receive order must exist and be open
- Receive order must have `transferable = true`
- Quantities must be positive integers
- Cannot exceed available unfulfilled requirements

## Frontend Integration Guide

### Mobile App Workflow

1. **Box Submission**:
   ```dart
   // Submit box normally
   final response = await api.updateReceiveOrderBoxAsync(request);

   // Check if transfer requirements exist
   if (response.containsKey('transfer_requirements')) {
     // Show transfer allocation UI
     showTransferAllocationScreen(response['transfer_requirements']);
   }
   ```

2. **Transfer Allocation**:
   ```dart
   // Check if this is an update or new allocation
   final isUpdate = await checkExistingAllocations(receiveOrderId, barcodes);

   // Submit with appropriate handling
   final response = await api.confirmTransfers(request);

   if (isUpdate) {
     // Handle immediate response
     showSuccessMessage('Allocations updated successfully');
   } else {
     // Handle queued response
     showSuccessMessage('Transfer processing queued');
   }
   ```

3. **Edit Existing Box**:
   ```dart
   // Get box data with transfer requirements
   final response = await api.receiveOrderBoxes(
     receiveOrderId: id,
     includeTransferRequirements: true
   );

   if (response['transfer_requirements'] != null) {
     // Show existing allocations for editing
     showEditAllocationsScreen(response['transfer_requirements']);
   }
   ```

## Backward Compatibility

✅ **Fully Backward Compatible**:
- Non-transferable receive orders work exactly as before
- Existing mobile apps continue to function without changes
- Transfer functionality is opt-in based on `receive_order.transferable` flag
- All existing API responses maintained

## Performance Considerations

- **Lazy Loading**: Transfer requirements only loaded when `transferable = true`
- **Efficient Queries**: Uses existing optimized database queries
- **Minimal Overhead**: No performance impact on non-transferable orders
- **Smart Caching**: Leverages existing Laravel query optimizations

## Testing Recommendations

1. **Test Non-Transferable Orders**: Ensure no transfer data is returned
2. **Test Transferable Orders**: Verify transfer requirements are included
3. **Test Update Detection**: Confirm proper detection of updates vs new allocations
4. **Test Error Handling**: Verify proper error responses for invalid requests
5. **Test Backward Compatibility**: Ensure existing functionality unchanged

---

# Updated API Endpoints Documentation

## Enhanced Endpoints

### 1. Enhanced: GET /api/receive-order-boxes

**Purpose**: Retrieve boxes for a receive order with optional transfer requirements

**Request**:
```json
{
  "receive_order_id": 123,
  "is_open": true,
  "box_name": "BOX-001"
}
```

**Enhanced Response** (for transferable orders):
```json
{
  "status": true,
  "data": [
    {
      "id": 1,
      "name": "BOX-001",
      "is_open": false,
      "receive_order_lines": [...],
      "receive_order_not_found": [...],
      "receive_order_line_counts": [...]
    }
  ],
  "transfer_requirements": {
    "ABC123": {
      "barcode": "ABC123",
      "description": "Red Cotton T-Shirt",
      "destinations": [
        {
          "warehouse_id": 2,
          "warehouse_name": "Shop A Downtown",
          "qty_required": 4,
          "qty_fulfilled": 1,
          "qty_remaining": 3
        }
      ]
    }
  }
}
```

**Behavior**:
- Returns `transfer_requirements` **only if** `receive_order.transferable = true`
- Extracts barcodes from all receive order lines in the boxes
- Only includes unfulfilled requirements (`qty_fulfilled < qty`)
- Backward compatible - existing functionality unchanged

### 2. Enhanced: POST /api/update-receive-order-box-async

**Purpose**: Submit counted items for a box with transfer requirements (existing) + return transfer requirements (enhanced)

**Request**: (Same as before)
```json
{
  "receive_order_id": 123,
  "box_name": "BOX-001",
  "lines": [
    {"barcode": "ABC123", "counted_qty": 10}
  ]
}
```

**Enhanced Response** (for transferable orders):
```json
{
  "status": true,
  "my_receive_order": {
    "receive_orders.id": 123,
    "franchise": "Main Franchise",
    "warehouse": "Central Warehouse",
    "progress": 75,
    "total_pcs": 100,
    "total_counted": 75
  },
  "message": "Box processing has been queued successfully.",
  "transfer_requirements": {
    "ABC123": {
      "barcode": "ABC123",
      "description": "Red Cotton T-Shirt",
      "destinations": [
        {
          "warehouse_id": 2,
          "warehouse_name": "Shop A Downtown",
          "qty_required": 4,
          "qty_fulfilled": 0,
          "qty_remaining": 4
        }
      ]
    }
  }
}
```

**Behavior**:
- Returns `transfer_requirements` **only if** `receive_order.transferable = true` AND `lines` are provided
- Uses existing `getTransferRequirements()` method
- Maintains all existing functionality

### 3. Enhanced: POST /api/confirm-transfers

**Purpose**: Allocate counted items to destination warehouses (existing) + handle updates to existing allocations (enhanced)

**Request**: (Same as before)
```json
{
  "receive_order_id": 123,
  "source_box_name": "BOX-001",
  "transfer_allocations": [
    {
      "barcode": "ABC123",
      "allocations": [
        {
          "destination_warehouse_id": 2,
          "qty": 3,
          "destination_box_name": "SHOP-A-BOX-001"
        }
      ]
    }
  ]
}
```

**Enhanced Response**:
```json
{
  "status": true,
  "message": "Transfer allocations updated successfully."
}
```

**Enhanced Behavior**:
- **Detects Update vs New**: Automatically determines if this is updating existing allocations or creating new ones
- **Dual Processing Modes**:
  - **New Allocations**: Dispatches `ProcessTransferConfirmation` job (existing behavior)
  - **Updates**: Processes immediately using `updateExistingAllocations()` method
- **Smart Detection**: Checks for existing `transfer_orders` with same `receive_order_id` and barcode
- **Different Messages**: Returns appropriate success message for each mode

## Implementation Details

### Update Detection Logic

The system automatically detects updates using this logic:

```php
private function isUpdateAllocation(array $validatedData): bool
{
    foreach ($validatedData['transfer_allocations'] as $allocation) {
        $barcode = $allocation['barcode'];

        $existingTransfers = DB::table('transfer_order_lines')
            ->join('transfer_orders', 'transfer_orders.id', '=', 'transfer_order_lines.transfer_order_id')
            ->join('items', 'items.id', '=', 'transfer_order_lines.item_id')
            ->where('transfer_orders.receive_order_id', $validatedData['receive_order_id'])
            ->where('items.barcode', $barcode)
            ->where('transfer_orders.status', '!=', 'closed')
            ->exists();

        if ($existingTransfers) {
            return true; // This is an update
