# Inventory Management API

Base URL: `/api/inventory`

Headers required for all endpoints:
- `Authorization: Bearer <jwt_token>`
- `Content-Type: application/json` for `POST`, `PUT`, and `PATCH`

All inventory APIs are JWT protected.

## Dashboard API

### `GET /api/inventory/dashboard/low-stock`
Fetch only low-stock items for dashboard table view.

Required:
- JWT token

Response:
```json
{
  "success": true,
  "count": 2,
  "data": [
    {
      "id": 1,
      "name": "Tomato",
      "current_stock": "3.000"
    },
    {
      "id": 2,
      "name": "Onion",
      "current_stock": "5.000"
    }
  ]
}
```

Notes:
- Returns only items where `current_stock <= min_threshold`
- Designed for dashboard table rendering
- Includes only `id`, `name`, and `current_stock`

## 0. History APIs

These APIs use the existing `inventory_stock_adjustments` table.

`reason` is already stored whenever stock is added or reduced through:
- `PATCH /api/inventory/items/:id/adjust`

### `GET /api/inventory/history`
Fetch complete inventory adjustment history.

Required:
- JWT token

Optional query params:
- `item_id`
- `type`
- `search`

Example:
```http
GET /api/inventory/history?item_id=1&type=addition&search=restock
```

Response:
```json
{
  "success": true,
  "count": 1,
  "data": [
    {
      "id": 10,
      "item_id": 1,
      "adjustment_type": "addition",
      "amount": "5.000",
      "previous_stock": "25.000",
      "new_stock": "30.000",
      "reason": "Restock",
      "created_by": "admin@example.com",
      "created_at": "2026-05-02T10:00:00.000Z",
      "item_name": "Tomato",
      "sku": "INV-TOM-001",
      "category_id": 1,
      "unit_id": 1,
      "category_name": "Vegetables",
      "unit_name": "Kilogram",
      "unit_short_name": "kg"
    }
  ]
}
```

### `GET /api/inventory/items/:id/history`
Fetch history for one inventory item only.

Required:
- Path param: `id`

Response:
```json
{
  "success": true,
  "item": {
    "id": 1,
    "name": "Tomato",
    "sku": "INV-TOM-001",
    "category_id": 1,
    "unit_id": 1,
    "category_name": "Vegetables",
    "unit_name": "Kilogram",
    "unit_short_name": "kg"
  },
  "count": 1,
  "data": [
    {
      "id": 10,
      "item_id": 1,
      "adjustment_type": "addition",
      "amount": "5.000",
      "previous_stock": "25.000",
      "new_stock": "30.000",
      "reason": "Restock",
      "created_by": "admin@example.com",
      "created_at": "2026-05-02T10:00:00.000Z",
      "item_name": "Tomato",
      "sku": "INV-TOM-001",
      "category_id": 1,
      "unit_id": 1,
      "category_name": "Vegetables",
      "unit_name": "Kilogram",
      "unit_short_name": "kg"
    }
  ]
}
```

### `DELETE /api/inventory/history`
Clear complete inventory history.

Required:
- JWT token

Response:
```json
{
  "success": true,
  "message": "Inventory history cleared successfully.",
  "data": {
    "deleted_count": 12
  }
}
```

### `DELETE /api/inventory/items/:id/history`
Clear history for one inventory item.

Required:
- Path param: `id`

Response:
```json
{
  "success": true,
  "message": "Inventory item history cleared successfully.",
  "data": {
    "item_id": 1,
    "item_name": "Tomato",
    "sku": "INV-TOM-001",
    "deleted_count": 4
  }
}
```

## 1. Category APIs

### `GET /api/inventory/categories`
Fetch all inventory categories.

Required:
- JWT token

Response:
```json
{
  "success": true,
  "count": 2,
  "data": [
    {
      "id": 1,
      "name": "Vegetables",
      "slug": "vegetables",
      "created_at": "2026-04-30T10:00:00.000Z",
      "updated_at": "2026-04-30T10:00:00.000Z"
    }
  ]
}
```

### `POST /api/inventory/categories`
Create a new inventory category.

Required body:
```json
{
  "name": "Vegetables"
}
```

Required fields:
- `name`: string, minimum 2 characters

Success response:
```json
{
  "success": true,
  "message": "Inventory category created successfully.",
  "data": {
    "id": 1,
    "name": "Vegetables",
    "slug": "vegetables"
  }
}
```

### `PUT /api/inventory/categories/:id`
Rename an inventory category.

Required:
- Path param: `id`

Required body:
```json
{
  "name": "Fresh Vegetables"
}
```

Required fields:
- `name`: string, minimum 2 characters

### `DELETE /api/inventory/categories/:id`
Delete an inventory category.

Required:
- Path param: `id`

Notes:
- Returns `409` if this category is already used by inventory items

## 2. Unit APIs

### `GET /api/inventory/units`
Fetch all inventory units.

Required:
- JWT token

Response:
```json
{
  "success": true,
  "count": 2,
  "data": [
    {
      "id": 1,
      "name": "Kilogram",
      "short_name": "kg",
      "created_at": "2026-04-30T10:00:00.000Z",
      "updated_at": "2026-04-30T10:00:00.000Z"
    }
  ]
}
```

### `POST /api/inventory/units`
Create a new inventory unit.

Required body:
```json
{
  "name": "Kilogram",
  "short_name": "kg"
}
```

Required fields:
- `name`: string
- `short_name`: string

Success response:
```json
{
  "success": true,
  "message": "Inventory unit created successfully.",
  "data": {
    "id": 1,
    "name": "Kilogram",
    "short_name": "kg"
  }
}
```

### `PUT /api/inventory/units/:id`
Update an inventory unit.

Required:
- Path param: `id`

Required body:
```json
{
  "name": "Kilogram",
  "short_name": "kg"
}
```

Required fields:
- `name`: string
- `short_name`: string

### `DELETE /api/inventory/units/:id`
Delete an inventory unit.

Required:
- Path param: `id`

Notes:
- Returns `409` if this unit is already used by inventory items

## 3. Inventory Item APIs

### `GET /api/inventory/items`
Fetch inventory items with optional filters.

Required:
- JWT token

Optional query params:
- `category_id`
- `search`
- `low_stock`

Example:
```http
GET /api/inventory/items?category_id=1&search=tomato&low_stock=true
```

Response:
```json
{
  "success": true,
  "count": 1,
  "data": [
    {
      "id": 1,
      "name": "Tomato",
      "sku": "INV-TOM-001",
      "category_id": 1,
      "unit_id": 1,
      "current_stock": "25.000",
      "min_threshold": "5.000",
      "created_at": "2026-04-30T10:00:00.000Z",
      "updated_at": "2026-04-30T10:00:00.000Z",
      "category_name": "Vegetables",
      "unit_name": "Kilogram",
      "unit_short_name": "kg",
      "is_low_stock": false
    }
  ]
}
```

### `POST /api/inventory/items`
Create a new inventory item.

Required body:
```json
{
  "name": "Tomato",
  "sku": "INV-TOM-001",
  "category_id": 1,
  "unit_id": 1,
  "current_stock": 25,
  "min_threshold": 5
}
```

Required fields:
- `name`: string, minimum 2 characters
- `sku`: string
- `category_id`: positive integer
- `unit_id`: positive integer
- `current_stock`: number, must be `0` or greater
- `min_threshold`: number, must be `0` or greater

Success response:
```json
{
  "success": true,
  "message": "Inventory item created successfully.",
  "data": {
    "id": 1,
    "name": "Tomato",
    "sku": "INV-TOM-001",
    "category_id": 1,
    "unit_id": 1,
    "current_stock": 25,
    "min_threshold": 5
  }
}
```

### `GET /api/inventory/items/:id`
Fetch a single inventory item by ID.

Required:
- Path param: `id`

Response fields:
- `id`
- `name`
- `sku`
- `category_id`
- `unit_id`
- `current_stock`
- `min_threshold`
- `category_name`
- `unit_name`
- `unit_short_name`
- `is_low_stock`
- `created_at`
- `updated_at`

### `PUT /api/inventory/items/:id`
Update item details.

Required:
- Path param: `id`

Required body:
```json
{
  "name": "Tomato Premium",
  "sku": "INV-TOM-001",
  "category_id": 1,
  "unit_id": 1,
  "min_threshold": 8
}
```

Required fields:
- `name`: string, minimum 2 characters
- `sku`: string
- `category_id`: positive integer
- `unit_id`: positive integer
- `min_threshold`: number, must be `0` or greater

Notes:
- `current_stock` is not updated from this API
- Use stock adjustment API for quantity changes

### `DELETE /api/inventory/items/:id`
Delete an inventory item.

Required:
- Path param: `id`

Notes:
- Related records in `inventory_stock_adjustments` are deleted automatically by cascade

### `PATCH /api/inventory/items/:id/adjust`
Adjust inventory stock.

Required:
- Path param: `id`

Required body for addition:
```json
{
  "amount": 5,
  "type": "addition",
  "reason": "Restock"
}
```

Required body for reduction:
```json
{
  "amount": 2,
  "type": "reduction",
  "reason": "Kitchen usage"
}
```

Required fields:
- `amount`: number, must be `0` or greater
- `type`: must be `addition` or `reduction`
- `reason`: string, minimum 2 characters

Success response:
```json
{
  "success": true,
  "message": "Inventory stock adjusted successfully.",
  "data": {
    "id": 1,
    "name": "Tomato",
    "sku": "INV-TOM-001",
    "category_id": 1,
    "unit_id": 1,
    "category_name": "Vegetables",
    "unit_name": "Kilogram",
    "unit_short_name": "kg",
    "previous_stock": 25,
    "current_stock": 30,
    "min_threshold": 5,
    "adjustment": {
      "type": "addition",
      "amount": 5,
      "reason": "Restock"
    },
    "is_low_stock": false
  }
}
```

Notes:
- Reduction is rejected if stock would go below `0`
- Every adjustment creates a row in `inventory_stock_adjustments`

### `PATCH /api/inventory/items/bulk-adjust`
Adjust stock for multiple items in one request.

Required:
- JWT token

Required body for selected items:
```json
{
  "scope": "selected",
  "item_ids": [1, 2, 3],
  "amount": 5,
  "type": "addition",
  "reason": "Bulk restock"
}
```

Required body for all items:
```json
{
  "scope": "all",
  "amount": 2,
  "type": "reduction",
  "reason": "Stock correction"
}
```

Optional filters when `scope` is `all`:
```json
{
  "scope": "all",
  "amount": 1,
  "type": "reduction",
  "reason": "Daily kitchen issue",
  "filters": {
    "category_id": 1,
    "search": "tomato",
    "low_stock": false
  }
}
```

Required fields:
- `scope`: must be `all` or `selected`
- `item_ids`: required when `scope` is `selected`
- `amount`: number, must be `0` or greater
- `type`: must be `addition` or `reduction`
- `reason`: string, minimum 2 characters

Flow:
1. Frontend selects all items or selected items.
2. Frontend sends one bulk-adjust request with amount, type, and reason.
3. Backend locks matching items inside one transaction.
4. Backend validates negative-stock protection for reductions.
5. Backend updates each item stock and stores one history row per item.
6. Backend commits once all updates succeed.

Success response:
```json
{
  "success": true,
  "message": "Inventory bulk stock adjustment completed successfully.",
  "data": {
    "scope": "selected",
    "adjusted_count": 3,
    "adjustment": {
      "type": "addition",
      "amount": 5,
      "reason": "Bulk restock"
    },
    "items": [
      {
        "id": 1,
        "name": "Tomato",
        "sku": "INV-TOM-001",
        "previous_stock": 10,
        "current_stock": 15,
        "min_threshold": 5,
        "category_name": "Vegetables",
        "unit_name": "Kilogram",
        "unit_short_name": "kg",
        "is_low_stock": false
      }
    ]
  }
}
```

Notes:
- For `selected`, all requested item IDs must exist
- For `all`, filters are optional and default to all inventory items
- If any reduction would make stock negative, the whole request is rejected
- Each adjusted item gets its own reason/history entry

## 4. Quick API List

```text
GET    /api/inventory/dashboard/low-stock

GET    /api/inventory/history
DELETE /api/inventory/history

GET    /api/inventory/categories
POST   /api/inventory/categories
PUT    /api/inventory/categories/:id
DELETE /api/inventory/categories/:id

GET    /api/inventory/units
POST   /api/inventory/units
PUT    /api/inventory/units/:id
DELETE /api/inventory/units/:id

GET    /api/inventory/items
POST   /api/inventory/items
GET    /api/inventory/items/:id/history
DELETE /api/inventory/items/:id/history
GET    /api/inventory/items/:id
PUT    /api/inventory/items/:id
DELETE /api/inventory/items/:id
PATCH  /api/inventory/items/:id/adjust
PATCH  /api/inventory/items/bulk-adjust
```

## 5. Common Error Responses

### `400 Bad Request`
Returned for:
- missing required fields
- invalid path/query/body values
- negative stock values
- invalid adjustment type
- invalid category/unit foreign key references
- invalid history filter values

Example:
```json
{
  "success": false,
  "message": "min_threshold cannot be negative."
}
```

### `401 Unauthorized`
Returned when authorization header is missing or malformed.

Example:
```json
{
  "success": false,
  "message": "Authorization header missing or invalid"
}
```

### `403 Forbidden`
Returned when JWT token is invalid or expired.

Example:
```json
{
  "success": false,
  "message": "Invalid or expired token"
}
```

### `404 Not Found`
Returned when category, unit, or item does not exist.

Example:
```json
{
  "success": false,
  "message": "Inventory item not found."
}
```

### `409 Conflict`
Returned for:
- duplicate category name
- duplicate unit
- duplicate SKU
- delete blocked by item reference

Example:
```json
{
  "success": false,
  "message": "Inventory item SKU already exists."
}
```
