Skip to content

Warehouse Transfer Guide

Overview

Warehouse Transfers enable seamless movement of inventory between warehouse locations. The system ensures accurate stock tracking by creating atomic transactions that decrease stock at the source warehouse and increase it at the destination, with complete audit trail preservation through immutable stock movements.

Transfers support business scenarios like warehouse restocking, seasonal redistribution, consolidation, emergency fulfillment, and optimizing inventory across multiple locations.

Table of Contents

Core Concepts

What is a Warehouse Transfer?

A warehouse transfer moves inventory from one warehouse location to another:

Source Warehouse (From)     →     Destination Warehouse (To)
Main DC: 100 units                 West Coast: 50 units
Transfer: 25 units

After Transfer:
Main DC: 75 units                  West Coast: 75 units

Key Properties:

  • Atomic Operation: Both source decrease and destination increase happen in a single database transaction
  • Immutable Audit Trail: Creates two stock movement records (transfer_out, transfer_in)
  • Immediate Effect: No "in-transit" status - transfer completes instantly
  • Reversible: Can be corrected with compensating transfer

Transfer Components

A transfer consists of:

  1. Product: What is being transferred
  2. Source Warehouse: Where stock is coming from
  3. Destination Warehouse: Where stock is going to
  4. Quantity: How many units to transfer
  5. Reason: Why the transfer is happening
  6. User: Who initiated the transfer

Atomic Transaction

Transfers execute in a database transaction, ensuring that both the source decrease and destination increase happen together as a single unit of work.

How it Works:

  1. Transaction begins
  2. Decrease stock at source warehouse
  3. Increase stock at destination warehouse
  4. If both operations succeed, transaction commits
  5. If either operation fails, transaction rolls back (nothing changes)

Benefits:

  • Data Integrity: Never lose stock in the system
  • Consistency: Source decrease always matches destination increase
  • Audit Trail: Complete traceability for compliance

Transfer Workflow

Simple Transfer Process

mermaid
graph LR
    A[Initiate Transfer] --> B{Validate}
    B -->|Valid| C[Start Transaction]
    B -->|Invalid| Z[Error]
    C --> D[Decrease Source Stock]
    D --> E[Create transfer_out Movement]
    E --> F[Increase Destination Stock]
    F --> G[Create transfer_in Movement]
    G --> H[Commit Transaction]
    H --> I[Transfer Complete]

Steps:

  1. Validation:

    • Product exists
    • Source and destination warehouses are different
    • Source has sufficient available stock
    • Quantity is positive
  2. Transaction Start:

    • Database transaction begins
    • Ensures atomicity
  3. Source Update:

    • Decrease stock at source warehouse
    • Create transfer_out movement record
    • Validate stock doesn't go negative
  4. Destination Update:

    • Increase stock at destination warehouse
    • Create transfer_in movement record
    • Link to source movement via reason
  5. Transaction Commit:

    • All changes saved atomically
    • Return updated inventory records

Transfer States

Unlike purchase orders or sales orders, transfers are immediate:

  • No "Pending" State: Transfer executes immediately
  • No "In-Transit" State: Stock instantly available at destination
  • No "Approval" Required: Executes on request (permissions enforced via middleware)

Why Immediate?

  • Simplicity: No additional state management needed
  • Real-time Accuracy: Inventory levels always current
  • Operational Speed: No delays waiting for approval/transit
  • Reversible: Easy to correct with reverse transfer

For physical transfers that take time (trucks, shipping), consider:

  • Recording transfer when shipment arrives (not when it leaves)
  • Using notes field to track shipping details
  • Creating custom "Transfer Order" model if you need in-transit tracking

Creating a Transfer

Transfer Stock Between Warehouses

Execute a warehouse transfer:

Endpoint: POST /api/v1/operations/inventory/transfer

Request:

bash
curl -X POST "https://api.crm.test/api/v1/operations/inventory/transfer" \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "product_id": 42,
    "from_warehouse_id": 1,
    "to_warehouse_id": 2,
    "quantity": 25,
    "reason": "Restocking West Coast warehouse for holiday season",
    "reference_number": "TRF-2025-001",
    "notes": "Rush transfer - ship via express courier"
  }'

Request Parameters:

ParameterTypeRequiredDescription
product_idinteger✅ YesProduct to transfer
from_warehouse_idintegerNoSource warehouse (null = default)
to_warehouse_idintegerNoDestination warehouse (null = default)
quantityinteger✅ YesUnits to transfer (must be positive)
reasonstringNoTransfer reason (max 500 chars)
reference_numberstringNoExternal reference number
notesstringNoAdditional details

Response (200 OK):

json
{
  "message": "Stock transferred successfully",
  "data": {
    "from": {
      "id": 123,
      "product_id": 42,
      "warehouse_id": 1,
      "quantity_on_hand": 75,
      "quantity_reserved": 10,
      "quantity_available": 65,
      "warehouse": {
        "id": 1,
        "name": "Main Distribution Center",
        "code": "MDC"
      },
      "product": {
        "id": 42,
        "name": "Laptop - Dell XPS 13",
        "sku": "DELL-XPS13-001"
      },
      "last_movement_at": "2025-01-16T10:30:00.000000Z"
    },
    "to": {
      "id": 124,
      "product_id": 42,
      "warehouse_id": 2,
      "quantity_on_hand": 75,
      "quantity_reserved": 5,
      "quantity_available": 70,
      "warehouse": {
        "id": 2,
        "name": "West Coast Distribution",
        "code": "WCD"
      },
      "product": {
        "id": 42,
        "name": "Laptop - Dell XPS 13",
        "sku": "DELL-XPS13-001"
      },
      "last_movement_at": "2025-01-16T10:30:00.000000Z"
    }
  }
}

Response Fields:

  • from: Updated inventory at source warehouse (after decrease)
  • to: Updated inventory at destination warehouse (after increase)

Default Warehouse Transfers

Transfer from/to the default warehouse using null:

From default warehouse to specific warehouse:

json
{
  "product_id": 42,
  "from_warehouse_id": null,
  "to_warehouse_id": 2,
  "quantity": 25,
  "reason": "Stock new West Coast location"
}

From specific warehouse to default warehouse:

json
{
  "product_id": 42,
  "from_warehouse_id": 2,
  "to_warehouse_id": null,
  "quantity": 25,
  "reason": "Consolidate to main warehouse"
}

Cannot transfer within default warehouse:

json
{
  "product_id": 42,
  "from_warehouse_id": null,
  "to_warehouse_id": null,
  "quantity": 25
}
// ❌ Error: Cannot transfer stock within the same warehouse

Transfer Validation

Validation Rules

The system enforces these validation rules:

1. Product Validation

json
{
  "product_id": 99999
}
// ❌ Error: Selected product does not exist

Product must exist in the system.

2. Warehouse Validation

json
{
  "from_warehouse_id": 99999
}
// ❌ Error: Source warehouse does not exist

{
  "to_warehouse_id": 99999
}
// ❌ Error: Destination warehouse does not exist

Both warehouses must exist (if specified).

3. Different Warehouses

json
{
  "from_warehouse_id": 1,
  "to_warehouse_id": 1,
  "quantity": 25
}
// ❌ Error: Source and destination warehouses must be different

Cannot transfer within the same warehouse.

json
{
  "from_warehouse_id": null,
  "to_warehouse_id": null,
  "quantity": 25
}
// ❌ Error: Cannot transfer stock within the same warehouse.
// Please specify different warehouses.

Both null values represent the default warehouse.

4. Quantity Validation

json
{
  "quantity": 0
}
// ❌ Error: Quantity must be at least 1

{
  "quantity": -5
}
// ❌ Error: Quantity must be at least 1

{
  "quantity": "abc"
}
// ❌ Error: Quantity must be a whole number

Quantity must be a positive integer.

5. Sufficient Stock

json
{
  "product_id": 42,
  "from_warehouse_id": 1,
  "quantity": 500
}
// Source warehouse only has 100 available units
// ❌ Error: Stock cannot be negative.
// Current: 100, Adjustment: -500

Source warehouse must have enough available stock (not reserved).

Error Responses

Validation Error (422 Unprocessable Entity):

json
{
  "message": "The given data was invalid.",
  "errors": {
    "product_id": [
      "Selected product does not exist"
    ],
    "quantity": [
      "Quantity must be at least 1"
    ],
    "to_warehouse_id": [
      "Source and destination warehouses must be different"
    ]
  }
}

Insufficient Stock (500 Internal Server Error):

json
{
  "message": "Stock cannot be negative. Current: 100, Adjustment: -500"
}

Not Found (404 Not Found):

json
{
  "message": "Inventory not found for product 42 in warehouse 1"
}

Stock Movement Records

Transfer Creates Two Movements

Each transfer creates two immutable stock movement records:

1. Transfer Out (Source Warehouse)

json
{
  "id": 890,
  "product_id": 42,
  "warehouse_id": 1,
  "movement_type": {
    "value": "transfer_out",
    "label": "Transfer Out"
  },
  "is_inbound": false,
  "is_outbound": true,
  "quantity": -25,
  "quantity_before": 100,
  "quantity_after": 75,
  "warehouse": {
    "id": 1,
    "name": "Main Distribution Center",
    "code": "MDC"
  },
  "reason": "Transfer to warehouse 2",
  "reference_type": null,
  "reference_id": null,
  "created_by": 5,
  "creator": {
    "id": 5,
    "name": "Warehouse Manager",
    "email": "manager@example.com"
  },
  "created_at": "2025-01-16T10:30:00.000000Z"
}

2. Transfer In (Destination Warehouse)

json
{
  "id": 891,
  "product_id": 42,
  "warehouse_id": 2,
  "movement_type": {
    "value": "transfer_in",
    "label": "Transfer In"
  },
  "is_inbound": true,
  "is_outbound": false,
  "quantity": 25,
  "quantity_before": 50,
  "quantity_after": 75,
  "warehouse": {
    "id": 2,
    "name": "West Coast Distribution",
    "code": "WCD"
  },
  "reason": "Transfer from warehouse 1",
  "reference_type": null,
  "reference_id": null,
  "created_by": 5,
  "creator": {
    "id": 5,
    "name": "Warehouse Manager",
    "email": "manager@example.com"
  },
  "created_at": "2025-01-16T10:30:00.000000Z"
}

Key Points:

  • Both movements have same created_at timestamp
  • Both movements created by same user
  • Quantities are inverse: -25 (out) and +25 (in)
  • Reason automatically includes warehouse context
  • Movements are permanently linked via product and timestamp

Viewing Transfer Movements

Get all transfer movements for a product:

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

Filter by transfer type:

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

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

Get warehouse transfer activity:

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

Transfer Examples

Example 1: Seasonal Restocking

Scenario: Transfer laptops from main warehouse to West Coast for holiday rush

bash
curl -X POST "https://api.crm.test/api/v1/operations/inventory/transfer" \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "product_id": 42,
    "from_warehouse_id": 1,
    "to_warehouse_id": 2,
    "quantity": 50,
    "reason": "Holiday season demand - West Coast",
    "reference_number": "HOLIDAY-2025-WC",
    "notes": "Expected surge in online orders from California"
  }'

Effect:

  • Main DC: 100 → 50 units
  • West Coast: 30 → 80 units
  • Two movement records created
  • Inventory immediately available for West Coast orders

Example 2: Emergency Fulfillment

Scenario: Transfer stock to fulfill urgent order in different location

bash
curl -X POST "https://api.crm.test/api/v1/operations/inventory/transfer" \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "product_id": 15,
    "from_warehouse_id": 3,
    "to_warehouse_id": 2,
    "quantity": 10,
    "reason": "Emergency transfer for SO-12345",
    "reference_number": "URGENT-SO-12345",
    "notes": "Customer needs by EOD - ship overnight"
  }'

Example 3: Warehouse Consolidation

Scenario: Close seasonal warehouse, move all stock back to main

bash
curl -X POST "https://api.crm.test/api/v1/operations/inventory/transfer" \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "product_id": 42,
    "from_warehouse_id": 4,
    "to_warehouse_id": 1,
    "quantity": 125,
    "reason": "Seasonal warehouse closure - consolidating inventory",
    "reference_number": "CONSOLIDATE-2025-Q1",
    "notes": "Summer popup location closing"
  }'

Example 4: Damage Replacement

Scenario: Transfer stock to replace damaged units at another location

bash
curl -X POST "https://api.crm.test/api/v1/operations/inventory/transfer" \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "product_id": 8,
    "from_warehouse_id": 1,
    "to_warehouse_id": 2,
    "quantity": 5,
    "reason": "Replacement for damaged units at WC warehouse",
    "reference_number": "DMG-REPLACE-001",
    "notes": "Flood damage at West Coast - replacing units"
  }'

Example 5: Balanced Distribution

Scenario: Rebalance inventory across warehouses for optimal service levels

bash
# Transfer 1: Main DC → East Coast
curl -X POST "https://api.crm.test/api/v1/operations/inventory/transfer" \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "product_id": 42,
    "from_warehouse_id": 1,
    "to_warehouse_id": 3,
    "quantity": 30,
    "reason": "Monthly rebalancing - optimizing service levels",
    "reference_number": "REBAL-2025-01-EC"
  }'

# Transfer 2: Main DC → West Coast
curl -X POST "https://api.crm.test/api/v1/operations/inventory/transfer" \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "product_id": 42,
    "from_warehouse_id": 1,
    "to_warehouse_id": 2,
    "quantity": 30,
    "reason": "Monthly rebalancing - optimizing service levels",
    "reference_number": "REBAL-2025-01-WC"
  }'

Business Rules

Rule 1: Atomic Execution

Transfers are all-or-nothing:

Success Scenario (Both operations succeed):

Transfer: Main DC (100 → 75) ✓
Transfer: West Coast (50 → 75) ✓
Result: Transfer complete

Failure Scenario (Either operation fails):

Transfer: Main DC (100 → 75) ✓
Transfer: West Coast ERROR ✗
Result: Transaction rolled back, Main DC still has 100 units

Rule 2: Stock Cannot Go Negative

Source warehouse must have sufficient available stock:

Warehouse Stock: 100 units
Reserved: 25 units
Available: 75 units

✅ Can transfer: 75 units or less
❌ Cannot transfer: 76+ units

Reserved stock cannot be transferred.

Rule 3: Immediate Effect

Stock is immediately available at destination:

Before Transfer:
- Source: 100 units available
- Destination: 50 units available

After Transfer (25 units):
- Source: 75 units available ✅ Instant
- Destination: 75 units available ✅ Instant

No "in-transit" or "pending" state.

Rule 4: Immutable Audit Trail

Transfer movements cannot be modified or deleted. The system enforces immutability to maintain a complete audit trail.

Restrictions:

  • Cannot update movement records
  • Cannot delete movement records
  • All movements are permanent

Solution - Reverse Transfer:

If you need to undo a transfer, create a compensating transfer in the opposite direction:

bash
POST /inventory/transfer
json
{
  "from_warehouse_id": 2,
  "to_warehouse_id": 1,
  "quantity": 25,
  "reason": "Reverse transfer - original was incorrect"
}

Rule 5: Warehouse Validation

Source and destination must be different:

✅ Valid:
- From: Warehouse 1, To: Warehouse 2
- From: null (default), To: Warehouse 2
- From: Warehouse 1, To: null (default)

❌ Invalid:
- From: Warehouse 1, To: Warehouse 1
- From: null, To: null

Best Practices

1. Use Descriptive Reasons

Good:

json
{
  "reason": "Holiday season restocking - projected 30% demand increase in West region"
}

Bad:

json
{
  "reason": "transfer"
}

2. Include Reference Numbers

Link transfers to business processes:

json
{
  "reference_number": "REBAL-2025-Q1-001",
  "notes": "Part of quarterly rebalancing initiative"
}

3. Verify Stock Before Transfer

Check available stock before initiating:

bash
# Get inventory levels
GET /inventory/show?product_id=42

# Verify source has enough available stock
# Then initiate transfer
POST /inventory/transfer

4. Document Physical Transfers

Use notes for shipping details:

json
{
  "notes": "Shipped via FedEx Ground - Tracking: 1Z999AA10123456784 - ETA: 2025-01-20"
}

5. Monitor Transfer Activity

Regularly review transfer movements:

bash
# Weekly transfer audit
GET /stock-movements?movement_type=transfer_out&from=2025-01-01&to=2025-01-07

# Warehouse-specific transfers
GET /stock-movements/warehouse-activity?warehouse_id=1&days=30

6. Plan Bulk Transfers

For multiple products, execute sequentially with error handling:

javascript
const products = [42, 43, 44, 45];
const results = [];

for (const productId of products) {
  try {
    const response = await transferStock({
      product_id: productId,
      from_warehouse_id: 1,
      to_warehouse_id: 2,
      quantity: 25,
      reason: "Bulk seasonal transfer"
    });
    results.push({ productId, status: 'success', data: response });
  } catch (error) {
    results.push({ productId, status: 'failed', error: error.message });
  }
}

console.log('Transfer Results:', results);

7. Reverse Transfers for Corrections

If you need to undo a transfer, create a compensating transfer in the opposite direction:

Original transfer:

bash
POST /inventory/transfer
json
{
  "from_warehouse_id": 1,
  "to_warehouse_id": 2,
  "quantity": 25,
  "reason": "Seasonal restock"
}

Reverse transfer:

bash
POST /inventory/transfer
json
{
  "from_warehouse_id": 2,
  "to_warehouse_id": 1,
  "quantity": 25,
  "reason": "Reverse transfer - original was incorrect quantity"
}

8. Coordinate with Physical Movement

Option A: Record After Arrival

Ship physical goods → Arrive at destination → Record transfer

Option B: Track Separately

Record transfer immediately → Track shipment in notes field

Choose based on your business needs.

API Endpoints

Transfer Operations

MethodEndpointDescription
POST/inventory/transferTransfer stock between warehouses
MethodEndpointDescription
GET/inventory/show?product_id={id}View inventory across warehouses
GET/stock-movementsView transfer movements
GET/stock-movements/product/{productId}/historyProduct transfer history
GET/stock-movements/warehouse-activityWarehouse transfer activity

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

Next Steps

  1. Execute Transfer - Try transferring stock between warehouses
  2. View Movements - Check the created transfer_out and transfer_in records
  3. Monitor Activity - Review warehouse transfer activity
  4. Plan Rebalancing - Optimize inventory distribution across locations
  5. Setup Automation - Consider automated transfers based on demand patterns

Documentation for SynthesQ CRM/ERP Platform