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
- Movement Types
- Viewing Stock Movements
- Product Movement History
- Warehouse Activity
- Movement Reports
- Movement Analytics
- Business Rules
- API Endpoints
Core Concepts
Stock Movement Model
Each stock movement is an immutable record that captures:
{
"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
{
"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
{
"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
{
"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
{
"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
{
"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
{
"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
{
"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
{
"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
{
"movement_type": "physical_count",
"quantity": -2,
"reason": "Monthly physical count variance"
}Viewing Stock Movements
List All Movements
Get paginated list with filters:
curl -X GET "https://api.crm.test/api/v1/operations/stock-movements?per_page=50" \
-H "Authorization: Bearer {token}"Query Parameters:
| Parameter | Type | Description | Example |
|---|---|---|---|
product_id | integer | Filter by product | 42 |
warehouse_id | integer | Filter by warehouse | 1 |
movement_type | string | Filter by type | sale |
direction | string | Filter by direction | inbound or outbound |
user_id | integer | Filter by user | 5 |
from | date | Start date (inclusive) | 2025-01-01 |
to | date | End date (inclusive) | 2025-01-31 |
search | string | Search in reason/notes | damaged |
reason | string | Filter by reason | Sales Order |
per_page | integer | Results per page (1-100) | 50 |
Response:
{
"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:
curl -X GET "https://api.crm.test/api/v1/operations/stock-movements/789" \
-H "Authorization: Bearer {token}"Response:
{
"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:
curl -X GET "https://api.crm.test/api/v1/operations/stock-movements?product_id=42" \
-H "Authorization: Bearer {token}"All movements in a warehouse:
curl -X GET "https://api.crm.test/api/v1/operations/stock-movements?warehouse_id=1" \
-H "Authorization: Bearer {token}"All sales movements:
curl -X GET "https://api.crm.test/api/v1/operations/stock-movements?movement_type=sale" \
-H "Authorization: Bearer {token}"All inbound movements:
curl -X GET "https://api.crm.test/api/v1/operations/stock-movements?direction=inbound" \
-H "Authorization: Bearer {token}"Movements by user:
curl -X GET "https://api.crm.test/api/v1/operations/stock-movements?user_id=5" \
-H "Authorization: Bearer {token}"Date range:
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:
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:
curl -X GET "https://api.crm.test/api/v1/operations/stock-movements/product/42/history?limit=100" \
-H "Authorization: Bearer {token}"Query Parameters:
| Parameter | Type | Description | Default |
|---|---|---|---|
limit | integer | Maximum records to return | 50 |
Response:
{
"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:
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:
| Parameter | Type | Description | Default |
|---|---|---|---|
warehouse_id | integer | Warehouse ID (null for default) | null |
days | integer | Number of days to analyze | 30 |
Response:
{
"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:
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:
| Parameter | Type | Required | Description |
|---|---|---|---|
start_date | date | No | Start date (YYYY-MM-DD) |
end_date | date | No | End date (YYYY-MM-DD) |
Response:
{
"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:
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:
{
"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:
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:
{
"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:
curl -X GET "https://api.crm.test/api/v1/operations/stock-movements/statistics" \
-H "Authorization: Bearer {token}"Response:
{
"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"
}
}Get Movement Trends
Analyze movement trends over time:
curl -X GET "https://api.crm.test/api/v1/operations/stock-movements/trends?days=90" \
-H "Authorization: Bearer {token}"Query Parameters:
| Parameter | Type | Description | Default |
|---|---|---|---|
days | integer | Number of days to analyze | 30 |
Response:
{
"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):
{
"movement_type": "adjustment",
"quantity": -10,
"reason": "Damaged units (incorrect count)"
}Compensating Entry:
{
"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_afterExample:
{
"quantity_before": 100,
"quantity": -5,
"quantity_after": 95
}
// ✅ Valid: 100 + (-5) = 95Invalid Example:
{
"quantity_before": 100,
"quantity": -5,
"quantity_after": 90
}
// ❌ Invalid: 100 + (-5) ≠ 90Reference Tracking
Movements can reference source transactions:
Polymorphic References:
- Purchase Orders
- Sales Orders
- Transfer Orders
- Return Orders
- Physical Counts
- Adjustments
Example:
{
"reference_type": "PurchaseOrder",
"reference_id": 1234,
"reference": {
"type": "PurchaseOrder",
"id": 1234,
"identifier": "PO-1234"
}
}API Endpoints
Movement Viewing
| Method | Endpoint | Description |
|---|---|---|
| GET | /stock-movements | List all movements with filters |
| GET | /stock-movements/{id} | Get single movement details |
| GET | /stock-movements/product/{productId}/history | Product movement history |
| GET | /stock-movements/warehouse-activity | Warehouse activity analysis |
Movement Reports
| Method | Endpoint | Description |
|---|---|---|
| GET | /stock-movements/reports/summary | Movement summary for date range |
| GET | /stock-movements/reports/product/{productId} | Product audit report |
| GET | /stock-movements/reports/warehouse/{warehouseId?} | Warehouse audit report |
Movement Analytics
| Method | Endpoint | Description |
|---|---|---|
| GET | /stock-movements/statistics | Overall movement statistics |
| GET | /stock-movements/trends | Movement 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.).
Related Guides
- Inventory Management - Multi-warehouse inventory tracking
- Warehouse Transfers - Inter-warehouse stock transfers
- Physical Counts - Physical inventory reconciliation
- Product Management - Product catalog
- Purchase Orders - Stock replenishment
Next Steps
- Explore Movements - Query your stock movement history
- Generate Reports - Create audit reports for compliance
- Analyze Trends - Understand movement patterns
- Track Products - Monitor specific product movements
- Warehouse Analysis - Review warehouse-specific activity