Inventory
Base path: /api/inventory
Products
GET /inventory/products
Get all products with category and supplier data.
Auth: Logged in
Query parameters:
| Parameter | Type | Description |
|---|---|---|
search | string | Filter by product name |
supplier_id | integer | Filter by supplier |
page | integer | Page number (activates pagination) |
limit | integer | Items per page (default 50) |
Response: Array of product objects with category_name, category_color, supplier_name.
POST /inventory/products
Create a new product. An article_number is auto-generated using the ART_PRODUCT sequence. If no vat_rate_id is given, the default VAT rate is applied automatically.
Auth: Admin
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Product name |
sku | string | Yes | Stock-keeping unit code |
price | number | Yes | Sale price (>= 0) |
purchase_price | number | Yes | Purchase price (required for cost calculations) |
quantity_on_hand | integer | Yes | Current stock level |
low_stock_threshold | integer | No | Alert threshold (default 5) |
category_id | integer | No | Product category ID |
supplier_id | integer | No | Supplier ID |
vat_rate_id | integer | No | VAT rate ID (defaults to the system default VAT rate) |
ean_code | string | No | EAN barcode |
PUT /inventory/products/:id
Update a product.
Auth: Admin
All fields are optional. When purchase_orders_enabled is false in settings, you may update quantity_on_hand directly here.
DELETE /inventory/products/:id
Delete a product.
Auth: Admin
Product Categories
GET /inventory/categories
Get all product categories ordered by position.
POST /inventory/categories
Create a new category.
Auth: Admin
| Field | Type | Required | Default |
|---|---|---|---|
name | string | Yes | — |
color | string (hex) | No | #6e56cf |
PUT /inventory/categories/:id
Update a category (name, colour, position).
Auth: Admin
DELETE /inventory/categories/:id
Delete a category.
Auth: Admin
Stock Transactions
GET /inventory/transactions
Get stock mutations (incoming deliveries, sales, manual adjustments, internal consumption).
Auth: Logged in
Query parameters: product_id, from (date), to (date)
Each transaction has a delta field (positive = stock in, negative = stock out) and a reason string.
Suppliers
Base path: /api/suppliers
GET /suppliers
List all suppliers ordered by name.
Auth: Logged in
Query parameters: active=true to filter active suppliers only; page, limit for pagination.
POST /suppliers
Create a new supplier. An auto-generated creditor_number is assigned using the CRED sequence.
Auth: Admin
| Field | Type | Required |
|---|---|---|
name | string | Yes |
contact_person | string | No |
email | string | No |
phone | string | No |
address | string | No |
notes | string | No |
website | string | No |
GET /suppliers/:id
Get a single supplier with their 20 most recent purchase orders.
PUT /suppliers/:id
Update a supplier.
Auth: Admin
Additional field: active (boolean) to deactivate a supplier.
DELETE /suppliers/:id
Delete a supplier.
Auth: Admin
Purchase Orders
Base path: /api/suppliers (purchase order endpoints are under the suppliers route)
GET /suppliers/orders
List all purchase orders with supplier name and item counts.
Auth: Logged in
Query parameters: status to filter by status; page, limit for pagination.
GET /suppliers/orders/:id
Get a single purchase order with all its line items.
Auth: Logged in
POST /suppliers/orders
Create a new purchase order. An order_number is auto-generated using the IO sequence. New orders have status created.
Auth: Admin
| Field | Type | Required | Description |
|---|---|---|---|
supplier_id | integer | Yes | Supplier ID |
staff_id | integer | No | Assigned staff member (defaults to current user) |
notes | string | No | Order notes |
order_date | date | No | Order date (defaults to today) |
expected_date | date | No | Expected delivery date |
Response (201): Order object with empty items array.
PUT /suppliers/orders/:id/items
Bulk-set the line items on a purchase order. Replaces all existing items. Only allowed when status is created or ordered.
Auth: Admin
{
"items": [
{ "product_id": 5, "quantity": 10, "unit_cost": 4.50 },
{ "product_id": 8, "quantity": 2, "unit_cost": 12.00 }
]
}
PUT /suppliers/orders/:id
Update a purchase order's status or metadata.
Auth: Admin
| Field | Type | Description |
|---|---|---|
status | string | created, ordered, received, cancelled |
notes | string | Order notes |
order_date | date | Order date |
expected_date | date | Expected delivery date |
received_date | date | Actual receipt date |
packing_slip_number | string | Packing slip reference number |
When status transitions to received, stock is automatically incremented for each line item and inventory_transactions rows are created. received_date is set to today if not provided.
POST /suppliers/orders/:id/packing-slip
Upload a packing slip file (PDF, image) for a received order. Max 10MB. The file is saved with a structured name: {order_number}_{creditor_number}_{received_date}.
Auth: Admin
Content-Type: multipart/form-data
| Field | Type | Required |
|---|---|---|
file | file | Yes |
Response: { "ok": true, "filename": "IO2601_CRED001_20260325.pdf" }
GET /suppliers/orders/:id/packing-slip
Download the packing slip file for an order.
Auth: Logged in
GET /suppliers/packing-slips/zip
Download all packing slips for a given year as a ZIP archive.
Auth: Admin
Query parameters: year (defaults to current year)
DELETE /suppliers/orders/:id
Delete a purchase order. Works for all statuses.
Auth: Admin
Behaviour by status:
| Order status | Stock effect |
|---|---|
created | No stock change |
ordered | No stock change |
received | Stock is decremented for all received items; reversing inventory_transactions rows are created; packing slip file is removed |
cancelled | No stock change |
Response: { "ok": true }
Purchase Order Statuses
| Status | Description |
|---|---|
created | Order created, items being added |
ordered | Order sent to supplier |
received | Goods received; stock updated |
cancelled | Order cancelled |
Auto-numbering
| Sequence prefix | Entity | Example |
|---|---|---|
IO | Purchase orders | IO2601, IO2602 |
CRED | Supplier creditor numbers | CRED001, CRED002 |
ART_PRODUCT | Product article numbers | ART_PRODUCT001 |
Sequences reset yearly (last two digits of the year are part of the number).