Filter by entity UUID
Inventory & Stock
Products#
CRUD for the product catalogue. Each product tracks its aggregate stock_quantity across all warehouse locations.
List Products#
entityIdstringconst { data } = await $fetch('https://taxmtd.uk/api/products', {
params: { entityId: '...' }
})
// Response shape
interface Product {
id: string
name: string
sku: string
description: string | null
category: string
unit_price: number
cost_price: number
stock_qty: number
low_stock_threshold: number
unit: string
notes: string | null
barcode: string | null
active: boolean
entity_id: string | null
stock_levels: WarehouseStock[]
}Create Product#
const { data } = await $fetch('https://taxmtd.uk/api/products', {
method: 'POST',
body: {
name: 'Widget Pro',
sku: 'WDG-001', // optional - auto-generated if omitted
description: 'Premium widget',
category: 'Electronics',
unit_price: 29.99,
cost_price: 12.50,
stock_qty: 100,
low_stock_threshold: 10, // default: 5
unit: 'pcs', // default: 'pcs'
notes: 'Fragile',
barcode: '5012345678901',
entity_id: '...'
}
})Update Product#
Send id plus any updatable fields: name, sku, description, category, unit_price, cost_price, stock_qty, low_stock_threshold, unit, active, notes, barcode.
await $fetch('https://taxmtd.uk/api/products', {
method: 'PUT',
body: { id: '...', unit_price: 34.99, active: false }
})Delete Product#
await $fetch('https://taxmtd.uk/api/products', {
method: 'DELETE',
body: { id: '...' }
})
// Returns: { data: { success: true } }Warehouse Locations#
Manage physical storage locations. Each location has a status (active or inactive).
List Locations#
entityIdstringFilter by entity UUID
const { data } = await $fetch('https://taxmtd.uk/api/warehouse-locations', {
params: { entityId: '...' }
})
// Response shape
interface WarehouseLocation {
id: string
name: string
address: string | null
status: 'active' | 'inactive'
entity_id: string
}Create Location#
const { data } = await $fetch('https://taxmtd.uk/api/warehouse-locations', {
method: 'POST',
body: {
name: 'Main Warehouse',
address: '10 Industrial Park, London',
entity_id: '...' // required
}
})Update Location#
Allowed fields: name, address, status.
await $fetch('https://taxmtd.uk/api/warehouse-locations', {
method: 'PUT',
body: { id: '...', status: 'inactive' }
})Delete Location#
await $fetch('https://taxmtd.uk/api/warehouse-locations', {
method: 'DELETE',
body: { id: '...' }
})
// Returns: { data: { success: true } }Warehouse Stock#
Per-product, per-location stock levels. POST performs an upsert - if a row already exists for the given product_id + location_id pair, it updates instead of creating a duplicate.
List Stock Levels#
productIdstringFilter by product UUID
locationIdstringFilter by warehouse location UUID
entityIdstringFilter by entity UUID
const { data } = await $fetch('https://taxmtd.uk/api/warehouse-stock', {
params: { entityId: '...' }
})
// Response shape - product_id and location_id are expanded
interface WarehouseStock {
id: string
product_id: { name: string; sku: string }
location_id: { name: string }
quantity: number
reorder_point: number
reserved_quantity: number
entity_id: string | null
}Upsert Stock Level#
const { data } = await $fetch('https://taxmtd.uk/api/warehouse-stock', {
method: 'POST',
body: {
product_id: '...',
location_id: '...',
quantity: 50,
reorder_point: 10,
entity_id: '...'
}
})Update Stock Level#
Allowed fields: quantity, reorder_point, reserved_quantity.
await $fetch('https://taxmtd.uk/api/warehouse-stock', {
method: 'PUT',
body: { id: '...', quantity: 75, reorder_point: 15 }
})Stock Movements#
Immutable ledger of quantity changes. Recording a movement automatically updates both the relevant warehouse_stock row and the product's aggregate stock_quantity.
List Movements#
productIdstringFilter by product UUID
entityIdstringFilter by entity UUID
locationIdstringFilter by warehouse location UUID
typestringFilter by movement type: purchase, sale, adjustment, return, transfer_in, transfer_out, stock_take
dateFromstringInclusive start date (YYYY-MM-DD)
dateTostringInclusive end date (YYYY-MM-DD)
const { data } = await $fetch('https://taxmtd.uk/api/stock-movements', {
params: { entityId: '...', type: 'purchase', dateFrom: '2026-01-01' }
})
// Response shape
interface StockMovement {
id: string
product_id: { name: string } | string
qty: number
type: string
location_id: string | null
reference: string | null
notes: string | null
date: string
entity_id: string | null
}Record Movement#
Use positive values for stock increases (purchase, return, transfer_in, adjustment up) and negative values for decreases (sale, transfer_out, adjustment down). When location_id is provided, the warehouse stock row is updated (or created); otherwise the product's aggregate stock is adjusted directly.
const { data } = await $fetch('https://taxmtd.uk/api/stock-movements', {
method: 'POST',
body: {
product_id: '...',
qty: 25,
type: 'purchase',
location_id: '...', // optional - ties movement to a warehouse
reference: 'PO-001', // optional
notes: 'Supplier restock',
date: '2026-04-01', // defaults to today
entity_id: '...'
}
})Transfer Orders#
Move stock between warehouse locations with full status-lifecycle enforcement.
Status transitions:
draft→in_transit- validates source stock availability, createstransfer_outmovements, decrements source warehousein_transit→completed- createstransfer_inmovements, increments destination warehouse, recalculates product totalsdraftorin_transit→cancelled- if in-transit, reverses source deductions withtransfer_cancelledmovementscompletedorders cannot be cancelled
List Transfer Orders#
entityIdstringFilter by entity UUID
const { data } = await $fetch('https://taxmtd.uk/api/transfer-orders', {
params: { entityId: '...' }
})
// Response shape
interface TransferOrder {
id: string
number: string // e.g. "TO-001"
source_location_id: { name: string }
destination_location_id: { name: string }
status: 'draft' | 'in_transit' | 'completed' | 'cancelled'
notes: string | null
entity_id: string
date_created: string
date_completed: string | null
lines: TransferOrderLine[]
}
interface TransferOrderLine {
id: string
product_id: { name: string; sku: string }
quantity: number
received_quantity: number
}Create Transfer Order#
Source and destination must be different locations. At least one line is required.
const { data } = await $fetch('https://taxmtd.uk/api/transfer-orders', {
method: 'POST',
body: {
source_location_id: '...',
destination_location_id: '...',
entity_id: '...',
notes: 'Rebalance stock for Q2',
lines: [
{ product_id: '...', quantity: 20 },
{ product_id: '...', quantity: 10 }
]
}
})
// Returns the created order with auto-generated number (e.g. "TO-001")Update Transfer Order Status#
// Ship the order
await $fetch('https://taxmtd.uk/api/transfer-orders', {
method: 'PUT',
body: { id: '...', status: 'in_transit' }
})
// Receive the order
await $fetch('https://taxmtd.uk/api/transfer-orders', {
method: 'PUT',
body: { id: '...', status: 'completed' }
})
// Cancel (draft or in-transit only)
await $fetch('https://taxmtd.uk/api/transfer-orders', {
method: 'PUT',
body: { id: '...', status: 'cancelled' }
})Delete Transfer Order#
Only draft orders can be deleted. Lines are removed automatically.
await $fetch('https://taxmtd.uk/api/transfer-orders', {
method: 'DELETE',
body: { id: '...' }
})
// Returns: { data: { success: true } }Stock Takes#
Physical inventory counts with automatic variance adjustments. Creating a stock take snapshots the current warehouse stock as system_quantity on each line.
Status transitions:
draft→in_progress- counting beginsin_progress→completed- calculates variance per line, createsstock_takemovements, updates warehouse stock to match counted quantities, recalculates product totalsdraftorin_progress→cancelledcompletedstock takes cannot be cancelled
List Stock Takes#
entityIdstringFilter by entity UUID
const { data } = await $fetch('https://taxmtd.uk/api/stock-takes', {
params: { entityId: '...' }
})
// Response shape
interface StockTake {
id: string
number: string // e.g. "ST-001"
location_id: { name: string }
status: 'draft' | 'in_progress' | 'completed' | 'cancelled'
notes: string | null
entity_id: string
date_created: string
date_completed: string | null
lines: StockTakeLine[]
}
interface StockTakeLine {
id: string
product_id: { name: string; sku: string }
system_quantity: number
counted_quantity: number | null
}Create Stock Take#
Lines are auto-populated from the current warehouse_stock for the given location.
const { data } = await $fetch('https://taxmtd.uk/api/stock-takes', {
method: 'POST',
body: {
location_id: '...', // required
entity_id: '...', // required
notes: 'Monthly count - April 2026'
}
})
// Returns the stock take with pre-populated lines (system_quantity set, counted_quantity null)Update Stock Take#
You can update counted quantities and/or transition the status in a single request. Omit status to perform a lines-only update.
// Record counts
await $fetch('https://taxmtd.uk/api/stock-takes', {
method: 'PUT',
body: {
id: '...',
lines: [
{ id: 'line-uuid-1', counted_quantity: 48 },
{ id: 'line-uuid-2', counted_quantity: 100 }
]
}
})
// Start counting
await $fetch('https://taxmtd.uk/api/stock-takes', {
method: 'PUT',
body: { id: '...', status: 'in_progress' }
})
// Complete - applies variance adjustments
await $fetch('https://taxmtd.uk/api/stock-takes', {
method: 'PUT',
body: { id: '...', status: 'completed' }
})Delete Stock Take#
Only draft or cancelled stock takes can be deleted. Lines are removed automatically.
await $fetch('https://taxmtd.uk/api/stock-takes', {
method: 'DELETE',
body: { id: '...' }
})
// Returns: { data: { success: true } }Stock Valuation Report#
Returns a per-product, per-warehouse cost and retail valuation with margin calculations.
entityIdstringFilter by entity UUID
locationIdstringFilter by specific warehouse location UUID
const { data, summary } = await $fetch('https://taxmtd.uk/api/reports/stock-valuation', {
params: { entityId: '...' }
})
// data[] shape
interface StockValuationRow {
product_id: string
product_name: string
product_sku: string
category: string
location_id: string
location_name: string
quantity: number
cost_price: number
unit_price: number
total_cost: number // quantity * cost_price
total_retail: number // quantity * unit_price
margin: number // percentage
}
// summary shape
interface StockValuationSummary {
totalCostValue: number
totalRetailValue: number
totalUnits: number
avgMargin: number // percentage
}