Skip to main content
Stock movements track all inventory changes. Each movement creates ledger entries and updates stock levels automatically.

Register Opening Balance

Register initial stock when setting up inventory or correcting balances. Creates a stock movement with reason OPENING_BALANCE.
curl -X POST "https://api.sushigo.com/api/v1/inventory/opening-balance" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "inventory_location_id": 1,
    "item_variant_id": 5,
    "quantity": 100,
    "uom_id": 3,
    "unit_cost": 27.50,
    "reference": "INV-2026-001",
    "notes": "Initial inventory count"
  }'

Request Body

inventory_location_id
integer
required
Target inventory location ID
item_variant_id
integer
required
Item variant ID
quantity
number
required
Quantity in the specified UOM (must be > 0)
uom_id
integer
required
Unit of measure ID for the quantity
unit_cost
number
required
Cost per unit in the specified UOM (must be ≥ 0)
reference
string
Reference number (e.g., physical count document number)
notes
string
Additional notes about this opening balance

How It Works

  1. The system converts the quantity to the variant’s base UOM using UOM conversions
  2. Creates a StockMovement with reason OPENING_BALANCE and status POSTED
  3. Creates movement lines with FIFO costing data
  4. Updates the Stock record for the location/variant (increases on_hand)
  5. Updates the variant’s avg_unit_cost using weighted average

Response

data
object

Example Response

{
  "status": 201,
  "data": {
    "id": 123,
    "inventory_location_id": 1,
    "item_variant_id": 5,
    "quantity": 100.0,
    "uom": "KG",
    "base_quantity": 100.0,
    "base_uom": "KG",
    "unit_cost": 27.50,
    "base_cost": 27.50,
    "reference": "INV-2026-001",
    "notes": "Initial inventory count",
    "status": "POSTED",
    "posted_at": "2026-03-06T14:45:00+00:00",
    "location": {
      "id": 1,
      "name": "Almacén Principal",
      "type": "MAIN"
    },
    "variant": {
      "id": 5,
      "code": "ARR-KG",
      "name": "Arroz Premium 1kg",
      "item_name": "Arroz Sushi Premium",
      "avg_unit_cost": 27.50
    }
  }
}

Errors

  • 422 - Validation error (invalid IDs, negative quantity, etc.)
  • 400 - Business logic error (e.g., no UOM conversion found between entry UOM and variant base UOM)
  • 403 - Insufficient permissions
UOM Conversion: If the entry UOM differs from the variant’s base UOM, a valid UOM conversion must exist. For example, if the variant’s base UOM is KG and you enter quantity in GR (grams), there must be a GR → KG conversion defined.

Register Stock Out

Register stock consumption or sale. Reduces stock using FIFO costing.
curl -X POST "https://api.sushigo.com/api/v1/inventory/stock-out" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "inventory_location_id": 1,
    "item_variant_id": 5,
    "qty": 10,
    "uom_id": 3,
    "reason": "SALE",
    "sale_price": 35.00,
    "reference": "SALE-2026-042",
    "notes": "Venta mostrador"
  }'

Request Body

inventory_location_id
integer
required
Source inventory location ID (where stock is deducted from)
item_variant_id
integer
required
Item variant ID
qty
number
required
Quantity to deduct in the specified UOM (must be > 0)
uom_id
integer
required
Unit of measure ID for the quantity
reason
string
required
Reason for stock out: SALE, CONSUMPTION, WASTE, ADJUSTMENT, TRANSFER, or OTHER
sale_price
number
Sale price per unit (for SALE reason, used to calculate profit margin)
reference
string
Reference number (e.g., sale order number, production order)
notes
string
Additional notes about this movement

How It Works

  1. Validates that sufficient stock is available (available >= requested qty)
  2. Converts quantity to base UOM
  3. Applies FIFO costing: consumes oldest stock first based on movement lines
  4. Creates StockMovement with specified reason and status POSTED
  5. Creates movement lines with cost and profit data
  6. Updates Stock record (decreases on_hand)
  7. If reason is SALE and sale_price is provided, calculates profit margin

Response

data
object

Example Response

{
  "success": true,
  "data": {
    "id": 456,
    "from_location_id": 1,
    "to_location_id": null,
    "item_variant_id": 5,
    "user_id": 1,
    "qty": "10.0000",
    "reason": "SALE",
    "status": "POSTED",
    "reference": "SALE-2026-042",
    "notes": "Venta mostrador",
    "meta": {
      "original_qty": 10,
      "original_uom": "KG",
      "unit_cost": 27.80,
      "sale_price": 35.00,
      "profit_margin": 7.20
    },
    "posted_at": "2026-03-06T15:20:00+00:00",
    "lines": [
      {
        "id": 789,
        "qty": "10.0000",
        "base_qty": "10.0000",
        "unit_cost": "27.8000",
        "line_total": "278.0000",
        "sale_price": "35.0000",
        "sale_total": "350.0000",
        "profit_margin": "7.2000",
        "profit_total": "72.0000"
      }
    ],
    "from_location": {
      "id": 1,
      "name": "Almacén Principal"
    },
    "item_variant": {
      "id": 5,
      "sku": "ARR-KG",
      "name": "Arroz Premium 1kg",
      "item": {
        "id": 1,
        "name": "Arroz Sushi Premium"
      }
    }
  },
  "message": "Stock out movement registered successfully"
}

Errors

  • 422 - Validation error (invalid IDs, negative quantity, invalid reason)
  • 400 - Business logic error:
    • Insufficient stock: "Insufficient stock. Available: 5, Requested: 10"
    • No UOM conversion found
    • Location not found or inactive
  • 403 - Insufficient permissions
Stock Availability: The system validates that available quantity (on_hand - reserved) is sufficient. If you need to consume reserved stock, you must unreserve it first.

Movement Reasons

SALE

Product sold to a customer. Use sale_price to track revenue and profit margin.

CONSUMPTION

Material consumed in production or operations (e.g., ingredients used in recipes).

WASTE

Stock lost due to spoilage, damage, or expiration.

ADJUSTMENT

Inventory adjustment (e.g., after physical count discrepancy).

TRANSFER

Stock transferred to another location (creates corresponding IN movement at destination).

OTHER

Other reasons not covered above. Use notes to clarify.

FIFO Costing

The system uses First-In-First-Out (FIFO) costing for stock out movements:
  1. When stock is consumed, the system finds the oldest movement lines with remaining quantity
  2. Deducts from oldest lines first until the requested quantity is fulfilled
  3. Each movement line tracks the original unit cost from when stock was received
  4. This ensures accurate cost of goods sold (COGS) calculation
Example: If you have:
  • 50 kg received at $25/kg on Jan 1
  • 100 kg received at $28/kg on Jan 15
And you consume 75 kg on Jan 20:
  • 50 kg costed at $25/kg (from Jan 1 batch)
  • 25 kg costed at $28/kg (from Jan 15 batch)
  • Total COGS: (50 × 25)+(25×25) + (25 × 28) = $1,950
For profit analysis, compare the line_total (cost) with sale_total (revenue) in the movement lines response.

Build docs developers (and LLMs) love