Skip to content

Stock Movement Tracking Guide

Overview

Stock Movements provide an immutable audit trail of all inventory changes in the system. Every stock adjustment, transfer, reservation, or physical count creates a permanent record that cannot be modified or deleted. This ensures complete traceability for regulatory compliance, inventory reconciliation, and operational analytics.

Built on the Event Sourcing pattern, stock movements capture before/after snapshots and support complex queries for audits, variance analysis, and trend reporting.

Table of Contents

Core Concepts

Stock Movement Model

Each stock movement is an immutable record that captures:

json
{
  "id": 789,
  "product_id": 42,
  "warehouse_id": 1,
  "movement_type": {
    "value": "sale",
    "label": "Sale"
  },
  "is_inbound": false,
  "is_outbound": true,
  "quantity": -5,
  "quantity_before": 100,
  "quantity_after": 95,
  "product": {
    "id": 42,
    "name": "Laptop - Dell XPS 13",
    "sku": "DELL-XPS13-001",
    "type": "standard"
  },
  "warehouse": {
    "id": 1,
    "name": "Main Distribution Center",
    "code": "MDC",
    "type": "distribution"
  },
  "reference_type": "SalesOrder",
  "reference_id": 12345,
  "reference": {
    "type": "SalesOrder",
    "id": 12345,
    "identifier": "SO-12345"
  },
  "reason": "Sales Order #12345 fulfilled",
  "notes": null,
  "created_by": 5,
  "creator": {
    "id": 5,
    "name": "John Smith",
    "email": "john.smith@example.com"
  },
  "created_at": "2025-01-15T14:30:00.000000Z"
}

Immutability Pattern

Stock movements follow the Event Sourcing pattern:

  • Cannot be Updated: Once created, movements cannot be modified
  • Cannot be Deleted: Movements form a permanent audit trail
  • Compensating Entries: Errors are corrected by creating offsetting movements
  • Snapshot Validation: quantity_before + quantity = quantity_after

Why Immutable?

  • Regulatory Compliance: Required for SOX, ISO 9001, FDA CFR Part 11
  • Audit Trail: Complete history for forensic analysis
  • Data Integrity: Prevents tampering with historical records
  • Reconciliation: Reliable source of truth for stock calculations

Movement Direction

Movements are classified by direction:

Inbound (Positive Quantity):

  • Purchases from suppliers
  • Returns from customers
  • Transfers in from other warehouses
  • Physical count adjustments (increase)
  • Found during audit

Outbound (Negative Quantity):

  • Sales to customers
  • Transfers out to other warehouses
  • Damage/theft/expiry writeoffs
  • Physical count adjustments (decrease)
  • Lost during audit

Movement Types

The system supports these movement types:

Purchase

Direction: Inbound (+) Description: Stock received from supplier Created By: Purchase Order receipt

json
{
  "movement_type": "purchase",
  "quantity": 50,
  "reference_type": "PurchaseOrder",
  "reference_id": 1234,
  "reason": "PO-1234 received from Tech Supplies Inc"
}

Sale

Direction: Outbound (-) Description: Stock sold to customer Created By: Sales Order fulfillment

json
{
  "movement_type": "sale",
  "quantity": -5,
  "reference_type": "SalesOrder",
  "reference_id": 5678,
  "reason": "SO-5678 shipped to customer"
}

Return

Direction: Inbound (+) Description: Stock returned by customer Created By: Return authorization

json
{
  "movement_type": "return",
  "quantity": 2,
  "reference_type": "ReturnOrder",
  "reference_id": 9012,
  "reason": "Customer return - damaged on arrival"
}

Adjustment

Direction: Either (+/-) Description: Manual stock correction Created By: Manual adjustment or physical count

json
{
  "movement_type": "adjustment",
  "quantity": -3,
  "reason": "Damaged units found during inspection",
  "notes": "3 units damaged, removed from inventory"
}

Transfer In

Direction: Inbound (+) Description: Stock received from another warehouse Created By: Warehouse transfer

json
{
  "movement_type": "transfer_in",
  "quantity": 25,
  "warehouse_id": 2,
  "reason": "Transfer from Main DC (Warehouse #1)"
}

Transfer Out

Direction: Outbound (-) Description: Stock sent to another warehouse Created By: Warehouse transfer

json
{
  "movement_type": "transfer_out",
  "quantity": -25,
  "warehouse_id": 1,
  "reason": "Transfer to West Coast (Warehouse #2)"
}

Reservation

Direction: Neither (no quantity change) Description: Stock reserved for order (affects available, not on_hand) Created By: Order reservation

json
{
  "movement_type": "reservation",
  "quantity": 0,
  "reason": "Reserved for SO-5678"
}

Release

Direction: Neither (no quantity change) Description: Reserved stock released (affects available, not on_hand) Created By: Order cancellation

json
{
  "movement_type": "release",
  "quantity": 0,
  "reason": "SO-5678 cancelled, reservation released"
}

Physical Count

Direction: Either (+/-) Description: Adjustment from physical inventory count Created By: Physical count reconciliation

json
{
  "movement_type": "physical_count",
  "quantity": -2,
  "reason": "Monthly physical count variance"
}

Viewing Stock Movements

List All Movements

Get paginated list with filters:

bash
curl -X GET "https://api.crm.test/api/v1/operations/stock-movements?per_page=50" \
  -H "Authorization: Bearer {token}"

Query Parameters:

ParameterTypeDescriptionExample
product_idintegerFilter by product42
warehouse_idintegerFilter by warehouse1
movement_typestringFilter by typesale
directionstringFilter by directioninbound or outbound
user_idintegerFilter by user5
fromdateStart date (inclusive)2025-01-01
todateEnd date (inclusive)2025-01-31
searchstringSearch in reason/notesdamaged
reasonstringFilter by reasonSales Order
per_pageintegerResults per page (1-100)50

Response:

json
{
  "data": [
    {
      "id": 789,
      "product_id": 42,
      "warehouse_id": 1,
      "movement_type": {
        "value": "sale",
        "label": "Sale"
      },
      "is_inbound": false,
      "is_outbound": true,
      "quantity": -5,
      "quantity_before": 100,
      "quantity_after": 95,
      "product": {
        "id": 42,
        "name": "Laptop - Dell XPS 13",
        "sku": "DELL-XPS13-001",
        "type": "standard"
      },
      "warehouse": {
        "id": 1,
        "name": "Main Distribution Center",
        "code": "MDC",
        "type": "distribution"
      },
      "reference_type": "SalesOrder",
      "reference_id": 12345,
      "reference": {
        "type": "SalesOrder",
        "id": 12345,
        "identifier": "SO-12345"
      },
      "reason": "Sales Order #12345 fulfilled",
      "notes": null,
      "created_by": 5,
      "creator": {
        "id": 5,
        "name": "John Smith",
        "email": "john.smith@example.com"
      },
      "created_at": "2025-01-15T14:30:00.000000Z"
    }
  ],
  "links": {
    "first": "https://api.crm.test/api/v1/operations/stock-movements?page=1",
    "last": "https://api.crm.test/api/v1/operations/stock-movements?page=10",
    "prev": null,
    "next": "https://api.crm.test/api/v1/operations/stock-movements?page=2"
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 10,
    "per_page": 50,
    "to": 50,
    "total": 487
  }
}

Get Single Movement

View detailed movement record:

bash
curl -X GET "https://api.crm.test/api/v1/operations/stock-movements/789" \
  -H "Authorization: Bearer {token}"

Response:

json
{
  "data": {
    "id": 789,
    "product_id": 42,
    "warehouse_id": 1,
    "movement_type": {
      "value": "sale",
      "label": "Sale"
    },
    "is_inbound": false,
    "is_outbound": true,
    "quantity": -5,
    "quantity_before": 100,
    "quantity_after": 95,
    "product": {
      "id": 42,
      "name": "Laptop - Dell XPS 13",
      "sku": "DELL-XPS13-001",
      "type": "standard"
    },
    "warehouse": {
      "id": 1,
      "name": "Main Distribution Center",
      "code": "MDC",
      "type": "distribution"
    },
    "reference_type": "SalesOrder",
    "reference_id": 12345,
    "reference": {
      "type": "SalesOrder",
      "id": 12345,
      "identifier": "SO-12345"
    },
    "reason": "Sales Order #12345 fulfilled",
    "notes": null,
    "created_by": 5,
    "creator": {
      "id": 5,
      "name": "John Smith",
      "email": "john.smith@example.com"
    },
    "created_at": "2025-01-15T14:30:00.000000Z"
  }
}

Filter Examples

All movements for a product:

bash
curl -X GET "https://api.crm.test/api/v1/operations/stock-movements?product_id=42" \
  -H "Authorization: Bearer {token}"

All movements in a warehouse:

bash
curl -X GET "https://api.crm.test/api/v1/operations/stock-movements?warehouse_id=1" \
  -H "Authorization: Bearer {token}"

All sales movements:

bash
curl -X GET "https://api.crm.test/api/v1/operations/stock-movements?movement_type=sale" \
  -H "Authorization: Bearer {token}"

All inbound movements:

bash
curl -X GET "https://api.crm.test/api/v1/operations/stock-movements?direction=inbound" \
  -H "Authorization: Bearer {token}"

Movements by user:

bash
curl -X GET "https://api.crm.test/api/v1/operations/stock-movements?user_id=5" \
  -H "Authorization: Bearer {token}"

Date range:

bash
curl -X GET "https://api.crm.test/api/v1/operations/stock-movements?from=2025-01-01&to=2025-01-31" \
  -H "Authorization: Bearer {token}"

Search in reason/notes:

bash
curl -X GET "https://api.crm.test/api/v1/operations/stock-movements?search=damaged" \
  -H "Authorization: Bearer {token}"

Product Movement History

Get Product History

View complete movement history for a specific product:

bash
curl -X GET "https://api.crm.test/api/v1/operations/stock-movements/product/42/history?limit=100" \
  -H "Authorization: Bearer {token}"

Query Parameters:

ParameterTypeDescriptionDefault
limitintegerMaximum records to return50

Response:

json
{
  "data": [
    {
      "id": 791,
      "movement_type": {
        "value": "purchase",
        "label": "Purchase"
      },
      "quantity": 50,
      "quantity_before": 95,
      "quantity_after": 145,
      "warehouse": {
        "id": 1,
        "name": "Main Distribution Center",
        "code": "MDC"
      },
      "reason": "PO-1234 received from Tech Supplies Inc",
      "reference": {
        "type": "PurchaseOrder",
        "id": 1234,
        "identifier": "PO-1234"
      },
      "creator": {
        "id": 3,
        "name": "Receiving Clerk",
        "email": "receiving@example.com"
      },
      "created_at": "2025-01-16T09:00:00.000000Z"
    },
    {
      "id": 790,
      "movement_type": {
        "value": "adjustment",
        "label": "Adjustment"
      },
      "quantity": -5,
      "quantity_before": 100,
      "quantity_after": 95,
      "warehouse": {
        "id": 1,
        "name": "Main Distribution Center",
        "code": "MDC"
      },
      "reason": "Damaged units removed",
      "notes": "Found during quality inspection",
      "creator": {
        "id": 8,
        "name": "QA Manager",
        "email": "qa@example.com"
      },
      "created_at": "2025-01-15T16:45:00.000000Z"
    },
    {
      "id": 789,
      "movement_type": {
        "value": "sale",
        "label": "Sale"
      },
      "quantity": -5,
      "quantity_before": 105,
      "quantity_after": 100,
      "warehouse": {
        "id": 1,
        "name": "Main Distribution Center",
        "code": "MDC"
      },
      "reason": "Sales Order #12345 fulfilled",
      "reference": {
        "type": "SalesOrder",
        "id": 12345,
        "identifier": "SO-12345"
      },
      "creator": {
        "id": 5,
        "name": "John Smith",
        "email": "john.smith@example.com"
      },
      "created_at": "2025-01-15T14:30:00.000000Z"
    }
  ]
}

Use Cases:

  • Audit product inventory changes
  • Investigate stock discrepancies
  • Track product lifecycle
  • Generate product-specific reports

Warehouse Activity

Get Warehouse Activity

View movement activity for a warehouse over time:

bash
curl -X GET "https://api.crm.test/api/v1/operations/stock-movements/warehouse-activity?warehouse_id=1&days=30" \
  -H "Authorization: Bearer {token}"

Query Parameters:

ParameterTypeDescriptionDefault
warehouse_idintegerWarehouse ID (null for default)null
daysintegerNumber of days to analyze30

Response:

json
{
  "success": true,
  "data": {
    "warehouse": {
      "id": 1,
      "name": "Main Distribution Center",
      "code": "MDC"
    },
    "period": {
      "from": "2024-12-18",
      "to": "2025-01-16",
      "days": 30
    },
    "summary": {
      "total_movements": 1247,
      "inbound_movements": 542,
      "outbound_movements": 705,
      "total_inbound_quantity": 12450,
      "total_outbound_quantity": 8920,
      "net_change": 3530,
      "unique_products": 187,
      "active_users": 23
    },
    "by_type": [
      {
        "movement_type": "sale",
        "count": 458,
        "total_quantity": -5890,
        "percentage": 36.73
      },
      {
        "movement_type": "purchase",
        "count": 345,
        "total_quantity": 9870,
        "percentage": 27.66
      },
      {
        "movement_type": "transfer_out",
        "count": 125,
        "total_quantity": -2150,
        "percentage": 10.02
      },
      {
        "movement_type": "transfer_in",
        "count": 98,
        "total_quantity": 1680,
        "percentage": 7.86
      }
    ],
    "daily_activity": [
      {
        "date": "2025-01-16",
        "movements": 52,
        "inbound": 28,
        "outbound": 24
      },
      {
        "date": "2025-01-15",
        "movements": 47,
        "inbound": 19,
        "outbound": 28
      }
    ]
  }
}

Movement Reports

Product Audit Report

Generate detailed audit report for a product:

bash
curl -X GET "https://api.crm.test/api/v1/operations/stock-movements/reports/product/42?start_date=2025-01-01&end_date=2025-01-31" \
  -H "Authorization: Bearer {token}"

Query Parameters:

ParameterTypeRequiredDescription
start_datedateNoStart date (YYYY-MM-DD)
end_datedateNoEnd date (YYYY-MM-DD)

Response:

json
{
  "success": true,
  "data": {
    "product": {
      "id": 42,
      "name": "Laptop - Dell XPS 13",
      "sku": "DELL-XPS13-001"
    },
    "period": {
      "start_date": "2025-01-01",
      "end_date": "2025-01-31"
    },
    "opening_balance": 85,
    "closing_balance": 145,
    "net_change": 60,
    "total_movements": 23,
    "movements_by_type": {
      "purchase": {
        "count": 8,
        "quantity": 150
      },
      "sale": {
        "count": 12,
        "quantity": -78
      },
      "adjustment": {
        "count": 3,
        "quantity": -12
      }
    },
    "movements": [
      {
        "date": "2025-01-16",
        "type": "purchase",
        "quantity": 50,
        "balance_after": 145,
        "reason": "PO-1234 received"
      }
    ]
  }
}

Warehouse Audit Report

Generate audit report for warehouse:

bash
curl -X GET "https://api.crm.test/api/v1/operations/stock-movements/reports/warehouse/1?start_date=2025-01-01&end_date=2025-01-31" \
  -H "Authorization: Bearer {token}"

Response:

json
{
  "success": true,
  "data": {
    "warehouse": {
      "id": 1,
      "name": "Main Distribution Center",
      "code": "MDC"
    },
    "period": {
      "start_date": "2025-01-01",
      "end_date": "2025-01-31"
    },
    "summary": {
      "total_movements": 1247,
      "products_affected": 187,
      "total_inbound": 12450,
      "total_outbound": 8920,
      "net_change": 3530,
      "value_change": 425000.00
    },
    "by_product": [
      {
        "product_id": 42,
        "product_name": "Laptop - Dell XPS 13",
        "sku": "DELL-XPS13-001",
        "movements": 23,
        "opening_balance": 85,
        "closing_balance": 145,
        "net_change": 60
      }
    ]
  }
}

Movement Summary

Get summary statistics for a date range:

bash
curl -X GET "https://api.crm.test/api/v1/operations/stock-movements/reports/summary?start_date=2025-01-01&end_date=2025-01-31" \
  -H "Authorization: Bearer {token}"

Response:

json
{
  "success": true,
  "data": {
    "period": {
      "start_date": "2025-01-01",
      "end_date": "2025-01-31"
    },
    "totals": {
      "total_movements": 3458,
      "total_products": 542,
      "total_warehouses": 5,
      "total_users": 47
    },
    "by_type": {
      "purchase": {
        "count": 892,
        "quantity": 28450
      },
      "sale": {
        "count": 1547,
        "quantity": -19820
      },
      "transfer_in": {
        "count": 234,
        "quantity": 4560
      },
      "transfer_out": {
        "count": 234,
        "quantity": -4560
      },
      "adjustment": {
        "count": 551,
        "quantity": -890
      }
    },
    "net_change": 7740,
    "value_impact": 950000.00
  }
}

Movement Analytics

Get Movement Statistics

View overall movement statistics:

bash
curl -X GET "https://api.crm.test/api/v1/operations/stock-movements/statistics" \
  -H "Authorization: Bearer {token}"

Response:

json
{
  "success": true,
  "data": {
    "total_movements": 45789,
    "movements_today": 123,
    "movements_this_week": 847,
    "movements_this_month": 3458,
    "by_type": {
      "purchase": 12450,
      "sale": 18920,
      "adjustment": 5430,
      "transfer_in": 4120,
      "transfer_out": 4120,
      "return": 749
    },
    "by_direction": {
      "inbound": 23567,
      "outbound": 22222
    },
    "average_per_day": 115.2,
    "busiest_hour": "14:00",
    "busiest_day": "Tuesday"
  }
}

Analyze movement trends over time:

bash
curl -X GET "https://api.crm.test/api/v1/operations/stock-movements/trends?days=90" \
  -H "Authorization: Bearer {token}"

Query Parameters:

ParameterTypeDescriptionDefault
daysintegerNumber of days to analyze30

Response:

json
{
  "success": true,
  "data": {
    "period": {
      "from": "2024-10-18",
      "to": "2025-01-16",
      "days": 90
    },
    "trends": {
      "total_movements": 10374,
      "average_per_day": 115.3,
      "trend": "increasing",
      "growth_rate": 12.5
    },
    "by_week": [
      {
        "week": "2025-W02",
        "movements": 847,
        "inbound": 412,
        "outbound": 435
      },
      {
        "week": "2025-W01",
        "movements": 789,
        "inbound": 398,
        "outbound": 391
      }
    ],
    "top_products": [
      {
        "product_id": 42,
        "product_name": "Laptop - Dell XPS 13",
        "movements": 178,
        "velocity": "high"
      }
    ]
  }
}

Business Rules

Immutability Enforcement

Stock movements cannot be updated or deleted. Any attempt to modify or delete a movement record will result in an error. The system enforces immutability to maintain a complete and reliable audit trail.

Why You Cannot Update or Delete:

  • Updates would destroy the historical record
  • Deletions would create gaps in the audit trail
  • Immutability is required for regulatory compliance

Instead: Use compensating entries to correct errors (see below)

Correcting Errors

Use compensating entries to fix mistakes:

Original (Incorrect):

json
{
  "movement_type": "adjustment",
  "quantity": -10,
  "reason": "Damaged units (incorrect count)"
}

Compensating Entry:

json
{
  "movement_type": "adjustment",
  "quantity": 7,
  "reason": "Correction: actual damage was 3 units, not 10"
}

Net Effect: -10 + 7 = -3 (correct amount)

Quantity Consistency Validation

Every movement validates:

quantity_before + quantity = quantity_after

Example:

json
{
  "quantity_before": 100,
  "quantity": -5,
  "quantity_after": 95
}
// ✅ Valid: 100 + (-5) = 95

Invalid Example:

json
{
  "quantity_before": 100,
  "quantity": -5,
  "quantity_after": 90
}
// ❌ Invalid: 100 + (-5) ≠ 90

Reference Tracking

Movements can reference source transactions:

Polymorphic References:

  • Purchase Orders
  • Sales Orders
  • Transfer Orders
  • Return Orders
  • Physical Counts
  • Adjustments

Example:

json
{
  "reference_type": "PurchaseOrder",
  "reference_id": 1234,
  "reference": {
    "type": "PurchaseOrder",
    "id": 1234,
    "identifier": "PO-1234"
  }
}

API Endpoints

Movement Viewing

MethodEndpointDescription
GET/stock-movementsList all movements with filters
GET/stock-movements/{id}Get single movement details
GET/stock-movements/product/{productId}/historyProduct movement history
GET/stock-movements/warehouse-activityWarehouse activity analysis

Movement Reports

MethodEndpointDescription
GET/stock-movements/reports/summaryMovement summary for date range
GET/stock-movements/reports/product/{productId}Product audit report
GET/stock-movements/reports/warehouse/{warehouseId?}Warehouse audit report

Movement Analytics

MethodEndpointDescription
GET/stock-movements/statisticsOverall movement statistics
GET/stock-movements/trendsMovement trend analysis

Base URL: https://api.crm.test/api/v1/operations/

Note: Movements are read-only via API. They are created automatically by inventory operations (adjust, transfer, reserve, etc.).

Next Steps

  1. Explore Movements - Query your stock movement history
  2. Generate Reports - Create audit reports for compliance
  3. Analyze Trends - Understand movement patterns
  4. Track Products - Monitor specific product movements
  5. Warehouse Analysis - Review warehouse-specific activity

Documentation for SynthesQ CRM/ERP Platform