# Android Scanner — API Changes & Integration Notes

## Overview

Three scanner endpoints are relevant to the handheld app:

| Endpoint | Method | Auth |
|---|---|---|
| `/api/scanner/barcode` | POST | X-API-Key header |
| `/api/scanner/sku` | POST | X-API-Key header |
| `/api/scanner/stock-update` | POST | X-API-Key header |
| `/api/scanner/scrap` | POST | X-API-Key header |

All requests must include:
```
X-API-Key: ck_your_key_here
Content-Type: application/json
```

---

## 1. Scan Barcode / Scan SKU

These two endpoints now return `product_cost` and `avg_cost` per location.

### Request

**POST** `/api/scanner/barcode`
```json
{ "barcode": "ERR3340" }
```

**POST** `/api/scanner/sku`
```json
{ "sku": "ERR3340" }
```

### Response shape

```json
{
  "success": true,
  "data": {
    "id": 42,
    "name": "Widget A",
    "barcode": "ERR3340",
    "sku": "ERR3340",
    "company": "My Company",
    "image": "http://...",
    "product_cost": 8.50,
    "stock": [
      {
        "location_id": 48,
        "warehouse": "Main Warehouse",
        "location": "WH/Stock",
        "on_hand_quantity": 130,
        "reserved_quantity": 10,
        "available_quantity": 120,
        "avg_cost": 8.75,
        "bin_racks": {
          "main_bin_rack": "A-01",
          "overflow_bin_rack": null
        }
      }
    ]
  }
}
```

### Key fields to use in the app

| Field | Use |
|---|---|
| `data.id` | Pass as `product_id` in stock-update and scrap calls |
| `data.product_cost` | Standard cost price from product record — display only |
| `data.stock[n].location_id` | Pass as `location_id` in stock-update and scrap calls |
| `data.stock[n].avg_cost` | Current weighted average cost at that location — display only |
| `data.stock[n].on_hand_quantity` | Current stock count — use to show the user the current level |

---

## 2. Stock Update (CHANGED — was absolute, now delta)

**BREAKING CHANGE**: The field `quantity` has been replaced by `quantity_change`.

Previously the app sent the *total* new count. Now it sends the *difference* — how many units to add or remove.

### Request

**POST** `/api/scanner/stock-update`

```json
{
  "product_id": 42,
  "location_id": 48,
  "quantity_change": 1,
  "unit_cost": 8.50
}
```

| Field | Type | Required | Notes |
|---|---|---|---|
| `product_id` | integer | yes | From scan response `data.id` |
| `location_id` | integer | yes | From scan response `data.stock[n].location_id` |
| `quantity_change` | integer | yes | +ve = add, -ve = remove. Cannot be 0. |
| `unit_cost` | decimal | no | Cost per unit for valuation. Only affects weighted avg when adding stock. |

### Validation errors (422)

- `quantity_change` is 0
- Result (`current + quantity_change`) would go below 0 → **"Insufficient Stock"** error

### Response

```json
{
  "success": true,
  "message": "Stock quantity updated.",
  "data": {
    "product_id": 42,
    "location_id": 48,
    "location": "WH/Stock",
    "warehouse": "Main Warehouse",
    "previous_quantity": 130,
    "quantity_change": 1,
    "new_quantity": 131,
    "unit_cost_applied": 8.50,
    "new_average_cost": 8.63,
    "new_total_value": 1130.53
  }
}
```

### App flow suggestion

1. User scans a product → display `on_hand_quantity` per location
2. User taps a location and enters the adjustment amount (+/-)
3. Send `quantity_change` = the entered number (not the new total)
4. On success: show "New quantity: X" confirmation

---

## 3. Scrap (NEW)

Moves stock from a warehouse location to the scrap bin and creates an official scrap record.

### Request

**POST** `/api/scanner/scrap`

```json
{
  "product_id": 42,
  "location_id": 48,
  "qty": 2
}
```

| Field | Type | Required | Notes |
|---|---|---|---|
| `product_id` | integer | yes | From scan response `data.id` |
| `location_id` | integer | yes | From scan response `data.stock[n].location_id` |
| `qty` | integer | yes | Min 1. Must not exceed on-hand quantity at the location. |

### Validation errors (422)

- `qty` < 1
- Not enough stock at the location → **"Insufficient Stock"** error
- Location is not an internal stock location

### Response

```json
{
  "success": true,
  "message": "2 unit(s) scrapped successfully.",
  "data": {
    "scrap_id": 7,
    "scrap_reference": "SP/7",
    "product_id": 42,
    "location_id": 48,
    "location": "WH/Stock",
    "warehouse": "Main Warehouse",
    "qty_scrapped": 2,
    "scrap_location": "Virtual Locations/Scrap",
    "scrapped_at": "2026-04-03T10:00:00+00:00"
  }
}
```

### App flow suggestion

1. User scans a product
2. User sees stock per location — including current `on_hand_quantity`
3. User taps "Scrap" on a location row
4. User enters quantity to scrap (validate client-side: must be > 0 and ≤ on_hand_quantity)
5. Show confirmation dialog: "Scrap 2 units from WH/Stock?"
6. On confirm: POST to `/api/scanner/scrap`
7. On success: show scrap reference (`SP/7`) and update the displayed stock count

---

## Error handling

All endpoints return consistent error shapes:

```json
{
  "success": false,
  "error": "Insufficient Stock",
  "message": "Cannot scrap 5 units — only 2 on hand at this location."
}
```

HTTP status codes:
- `422` — Validation error or business rule violation
- `404` — Product or stock record not found
- `401` — Missing or invalid API key
- `500` — Server configuration issue (e.g. no scrap location configured)

Always check `success === true` before using `data`.
