Skip to main content
Openfront provides comprehensive inventory management capabilities, allowing you to track stock levels, manage multiple warehouse locations, and monitor inventory movements throughout your supply chain.

Inventory Architecture

Inventory management in Openfront is built around three core concepts:
  1. Product Variants - Individual SKUs with inventory tracking
  2. Locations - Warehouse and fulfillment center management
  3. Stock Movements - Audit trail of inventory changes

Product Variant Inventory

Each product variant maintains its own inventory settings:
ProductVariant {
  title: string
  sku: string                    // Unique identifier
  barcode: string                // Barcode/UPC/EAN
  ean: string                    // European Article Number
  upc: string                    // Universal Product Code
  inventoryQuantity: integer     // Current stock level
  allowBackorder: boolean        // Allow orders when out of stock
  manageInventory: boolean       // Enable inventory tracking
  
  // Relationships
  product: → Product
  location: → Location
  stockMovements: [→ StockMovement]
  lineItems: [→ OrderLineItem]
}

Enabling Inventory Tracking

Configure inventory settings for a variant:
mutation UpdateVariantInventory {
  updateProductVariant(
    where: { id: "variant_id" }
    data: {
      sku: "SHIRT-BLK-M"
      barcode: "1234567890123"
      manageInventory: true
      allowBackorder: false
      inventoryQuantity: 100
      location: { connect: { id: "location_warehouse1" } }
    }
  ) {
    id
    sku
    inventoryQuantity
    manageInventory
    location {
      name
    }
  }
}

Checking Stock Availability

Query current stock levels:
query CheckStock {
  productVariant(where: { sku: "SHIRT-BLK-M" }) {
    id
    title
    sku
    inventoryQuantity
    allowBackorder
    manageInventory
    location {
      name
      address
    }
  }
}

Location Management

Locations represent physical warehouses or fulfillment centers:
Location {
  name: string
  description: string
  address: string
  
  variants: [→ ProductVariant]
}

Creating Locations

Set up warehouse locations:
mutation CreateLocation {
  createLocation(data: {
    name: "Main Warehouse"
    description: "Primary fulfillment center"
    address: "123 Warehouse Blvd, City, ST 12345"
  }) {
    id
    name
    address
  }
}

Assigning Products to Locations

mutation AssignToLocation {
  updateProductVariant(
    where: { id: "variant_id" }
    data: {
      location: { connect: { id: "location_id" } }
    }
  ) {
    id
    sku
    location {
      name
    }
  }
}

Multi-Location Inventory

View inventory across all locations:
query LocationInventory {
  locations {
    id
    name
    variants {
      id
      sku
      title
      inventoryQuantity
      product {
        title
      }
    }
  }
}

Stock Movements

All inventory changes are tracked as stock movements:
StockMovement {
  type: "adjustment" | "sale" | "return" | "restock" | "transfer"
  quantity: integer              // Positive for increases, negative for decreases
  reason: string                 // Why the movement occurred
  note: string                   // Additional details
  
  variant: → ProductVariant
}

Movement Types

  • adjustment - Manual inventory adjustments
  • sale - Inventory sold to customers
  • return - Customer returns
  • restock - Receiving new inventory
  • transfer - Moving between locations

Recording Stock Movements

Manual inventory adjustments:
mutation AdjustInventory {
  createStockMovement(data: {
    variant: { connect: { id: "variant_id" } }
    type: "adjustment"
    quantity: -5
    reason: "Damaged goods"
    note: "Water damage from warehouse leak"
  }) {
    id
    type
    quantity
    reason
    variant {
      sku
      inventoryQuantity
    }
  }
}

Viewing Movement History

query InventoryHistory {
  stockMovements(
    where: { variant: { id: { equals: "variant_id" } } }
    orderBy: [{ createdAt: desc }]
    take: 50
  ) {
    id
    type
    quantity
    reason
    note
    createdAt
    variant {
      sku
    }
  }
}

Automatic Inventory Updates

Openfront automatically tracks inventory changes:

Order Fulfillment

When an order is fulfilled, inventory is decremented:
// Automatically creates stock movement
{
  type: "sale",
  quantity: -2,  // Sold 2 units
  reason: "Order fulfillment",
  note: "Order #1234"
}

Customer Returns

Returned items increase inventory:
// Automatically creates stock movement
{
  type: "return",
  quantity: 1,   // Returned 1 unit
  reason: "Customer return",
  note: "Return ID: ret_123"
}

Claim Processing

Replacement items affect inventory:
// Creates stock movement for replacement
{
  type: "adjustment",
  quantity: -1,
  reason: "Claim replacement",
  note: "Claim #claim_456"
}

Backorder Management

Handle out-of-stock scenarios:

Allowing Backorders

Enable customers to order out-of-stock items:
mutation EnableBackorders {
  updateProductVariant(
    where: { id: "variant_id" }
    data: {
      allowBackorder: true
      inventoryQuantity: 0
    }
  ) {
    id
    allowBackorder
    inventoryQuantity
  }
}

Stock Validation

Validate stock before checkout:
function validateStock(cart: Cart) {
  for (const item of cart.lineItems) {
    const variant = item.variant;
    
    if (!variant.manageInventory) {
      continue; // Skip items without inventory tracking
    }
    
    if (item.quantity > variant.inventoryQuantity) {
      if (!variant.allowBackorder) {
        throw new Error(
          `Insufficient stock for ${variant.sku}. ` +
          `Available: ${variant.inventoryQuantity}, ` +
          `Requested: ${item.quantity}`
        );
      }
    }
  }
}

Low Stock Alerts

Monitor inventory levels:
query LowStockProducts {
  productVariants(
    where: {
      AND: [
        { manageInventory: { equals: true } }
        { inventoryQuantity: { lte: 10 } }
        { allowBackorder: { equals: false } }
      ]
    }
  ) {
    id
    sku
    title
    inventoryQuantity
    product {
      title
    }
    location {
      name
    }
  }
}

Inventory Reports

Current Stock Report

query StockReport {
  productVariants(
    where: { manageInventory: { equals: true } }
  ) {
    sku
    title
    inventoryQuantity
    location { name }
    product {
      title
      status
    }
  }
}

Stock Movement Report

query MovementReport($startDate: DateTime!, $endDate: DateTime!) {
  stockMovements(
    where: {
      AND: [
        { createdAt: { gte: $startDate } }
        { createdAt: { lte: $endDate } }
      ]
    }
    orderBy: [{ createdAt: desc }]
  ) {
    type
    quantity
    reason
    createdAt
    variant {
      sku
      product { title }
    }
  }
}

Inventory Value

Calculate total inventory value:
interface InventoryValue {
  sku: string;
  quantity: number;
  unitCost: number;
  totalValue: number;
}

function calculateInventoryValue(
  variants: ProductVariant[]
): InventoryValue[] {
  return variants
    .filter(v => v.manageInventory)
    .map(variant => {
      const price = variant.prices[0]?.amount || 0;
      return {
        sku: variant.sku,
        quantity: variant.inventoryQuantity,
        unitCost: price / 100,
        totalValue: (price / 100) * variant.inventoryQuantity,
      };
    });
}

Best Practices

SKU Management

  • Use consistent SKU naming conventions
  • Include product attributes in SKUs (e.g., SHIRT-BLK-M)
  • Make SKUs human-readable
  • Never reuse SKUs

Inventory Tracking

  • Enable inventory management for all physical products
  • Disable for digital products and services
  • Set realistic safety stock levels
  • Regular cycle counts to verify accuracy

Stock Movements

  • Always provide descriptive reasons
  • Add detailed notes for auditing
  • Review movement history regularly
  • Investigate unexpected movements

Multi-Location

  • Assign default locations to new products
  • Plan fulfillment based on location proximity
  • Monitor stock distribution across locations
  • Transfer inventory to optimize fulfillment

Backorders

  • Only enable for products with reliable restock
  • Communicate expected availability dates
  • Fulfill backorders in chronological order
  • Consider partial fulfillment options

Integration Examples

Inventory Sync Script

Sync inventory from external system:
import { keystoneClient } from './lib/keystone';

async function syncInventory(sku: string, quantity: number) {
  const mutation = `
    mutation UpdateInventory($sku: String!, $quantity: Int!) {
      updateProductVariant(
        where: { sku: $sku }
        data: { inventoryQuantity: $quantity }
      ) {
        id
        sku
        inventoryQuantity
      }
    }
  `;
  
  const result = await keystoneClient(mutation, {
    sku,
    quantity,
  });
  
  console.log(`Updated ${sku}: ${quantity} units`);
}

// Sync inventory from CSV or API
await syncInventory('SHIRT-BLK-M', 150);
await syncInventory('PANTS-BLUE-L', 75);

Low Stock Notification

async function checkLowStock() {
  const query = `
    query LowStock {
      productVariants(
        where: {
          inventoryQuantity: { lte: 10 }
          manageInventory: { equals: true }
        }
      ) {
        sku
        title
        inventoryQuantity
      }
    }
  `;
  
  const result = await keystoneClient(query);
  const lowStock = result.data.productVariants;
  
  if (lowStock.length > 0) {
    // Send notification email
    await sendEmail({
      to: '[email protected]',
      subject: 'Low Stock Alert',
      body: `${lowStock.length} products are low on stock`,
      data: lowStock,
    });
  }
}

Build docs developers (and LLMs) love