Inventory & Stock

Product catalogue, warehouse locations, stock levels, movements, transfer orders, stock takes, and valuation reports.

Products#

CRUD for the product catalogue. Each product tracks its aggregate stock_quantity across all warehouse locations.

List Products#

entityIdstring

Filter by entity UUID

const { 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#

entityIdstring

Filter 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#

productIdstring

Filter by product UUID

locationIdstring

Filter by warehouse location UUID

entityIdstring

Filter 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#

productIdstring

Filter by product UUID

entityIdstring

Filter by entity UUID

locationIdstring

Filter by warehouse location UUID

typestring

Filter by movement type: purchase, sale, adjustment, return, transfer_in, transfer_out, stock_take

dateFromstring

Inclusive start date (YYYY-MM-DD)

dateTostring

Inclusive 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:

  • draftin_transit - validates source stock availability, creates transfer_out movements, decrements source warehouse
  • in_transitcompleted - creates transfer_in movements, increments destination warehouse, recalculates product totals
  • draft or in_transitcancelled - if in-transit, reverses source deductions with transfer_cancelled movements
  • completed orders cannot be cancelled

List Transfer Orders#

entityIdstring

Filter 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:

  • draftin_progress - counting begins
  • in_progresscompleted - calculates variance per line, creates stock_take movements, updates warehouse stock to match counted quantities, recalculates product totals
  • draft or in_progresscancelled
  • completed stock takes cannot be cancelled

List Stock Takes#

entityIdstring

Filter 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.

entityIdstring

Filter by entity UUID

locationIdstring

Filter 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
}
Was this page helpful? Share it.