Skip to content

Inter-Warehouse Transfers

Inter-warehouse transfers move stock between warehouses through a 3-step workflow: draft, ship (deducts from source), and receive (adds to destination). Transfers can be cancelled at any stage before receiving — if already shipped, the stock reversal is automatic.

Purpose

  • Move stock between warehouses with full audit trail
  • Validate sufficient stock before shipping
  • Support partial receives (received quantity can differ from shipped)
  • Capture unit cost from weighted-average cost at ship time
  • Record transfer_out and transfer_in stock movements
  • Reverse stock changes automatically on cancellation of shipped transfers
  • Generate transfer numbers automatically via the Sequence system (TRF-000001)

Entity Attributes

Inventory Transfer (Header)

FieldTypeDescription
idbigintPrimary key
company_idbigintFK to companies
transfer_numberstring(30)Auto-generated unique number (e.g., TRF-000001)
datedateTransfer date
expected_datedate?Expected delivery date
from_warehouse_idbigintFK to warehouses (source)
to_warehouse_idbigintFK to warehouses (destination, must differ from source)
statusenumdraft, shipped, received, cancelled
total_quantitydecimal(15,3)Sum of requested quantities
notestext?Additional notes
notes_artext?Arabic notes
requested_bybigint?FK to users
shipped_bybigint?FK to users
shipped_attimestamp?Ship timestamp
received_bybigint?FK to users
received_attimestamp?Receive timestamp
cancelled_bybigint?FK to users
cancelled_attimestamp?Cancellation timestamp
created_bybigint?FK to users
created_attimestampCreation timestamp
updated_attimestampLast update timestamp
deleted_attimestamp?Soft-delete timestamp

Indexes:

  • UNIQUE (company_id, transfer_number)
  • INDEX (company_id, status)
  • INDEX (company_id, date)

Inventory Transfer Item

FieldTypeDescription
idbigintPrimary key
inventory_transfer_idbigintFK to inventory_transfers (cascade delete)
product_idbigintFK to products
product_variant_idbigint?FK to product_variants
unit_idbigintFK to units
requested_quantitydecimal(15,3)Quantity requested for transfer
shipped_quantitydecimal(15,3)?Quantity actually shipped (set on ship)
received_quantitydecimal(15,3)?Quantity actually received (set on receive)
unit_costdecimal(15,3)Cost per unit (set from weighted avg on ship)
batch_numberstring(50)?Batch/lot tracking number
notestext?Item-level notes

Relationships

API Endpoints

MethodEndpointDescriptionPermission
GET/api/inventory/transfersList transfers (paginated)inventory.transfers.view
POST/api/inventory/transfersCreate a draft transferinventory.transfers.create
GET/api/inventory/transfers/{id}Get transfer detailsinventory.transfers.view
PUT/api/inventory/transfers/{id}Update a draft transferinventory.transfers.create
DELETE/api/inventory/transfers/{id}Delete a draft transferinventory.transfers.create
POST/api/inventory/transfers/{id}/shipShip transfer (deduct from source)inventory.transfers.ship
POST/api/inventory/transfers/{id}/receiveReceive transfer (add to destination)inventory.transfers.receive
POST/api/inventory/transfers/{id}/cancelCancel transferinventory.transfers.cancel

Query Parameters (Index)

ParameterTypeDescription
pageintegerPage number
statusstringFilter by status: draft, shipped, received, cancelled
from_warehouse_idintegerFilter by source warehouse
to_warehouse_idintegerFilter by destination warehouse
date_fromstringFilter from date (YYYY-MM-DD)
date_tostringFilter to date (YYYY-MM-DD)
searchstringSearch by transfer number

Examples

Create a Transfer

bash
curl -X POST https://moon-erp.test/api/inventory/transfers \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "date": "2026-02-22",
    "from_warehouse_id": 1,
    "to_warehouse_id": 2,
    "notes": "Monthly stock rebalancing",
    "items": [
      {
        "product_id": 1,
        "unit_id": 1,
        "requested_quantity": 50
      },
      {
        "product_id": 2,
        "unit_id": 1,
        "requested_quantity": 25
      }
    ]
  }'
dart
final response = await dio.post('/api/inventory/transfers', data: {
  'date': '2026-02-22',
  'from_warehouse_id': 1,
  'to_warehouse_id': 2,
  'notes': 'Monthly stock rebalancing',
  'items': [
    {'product_id': 1, 'unit_id': 1, 'requested_quantity': 50},
    {'product_id': 2, 'unit_id': 1, 'requested_quantity': 25},
  ],
});

Ship a Transfer

bash
curl -X POST https://moon-erp.test/api/inventory/transfers/1/ship \
  -H "Authorization: Bearer {token}"
dart
final response = await dio.post('/api/inventory/transfers/1/ship');

Receive with Partial Quantities

bash
curl -X POST https://moon-erp.test/api/inventory/transfers/1/receive \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "received_quantities": {
      "1": 48.000,
      "2": 25.000
    }
  }'
dart
final response = await dio.post('/api/inventory/transfers/1/receive', data: {
  'received_quantities': {'1': 48.0, '2': 25.0},
});

Cancel a Shipped Transfer

bash
curl -X POST https://moon-erp.test/api/inventory/transfers/1/cancel \
  -H "Authorization: Bearer {token}"
dart
final response = await dio.post('/api/inventory/transfers/1/cancel');

Business Rules

  1. Source and destination warehouses must be different — validated on create
  2. Only draft transfers can be edited or deleted — shipped/received/cancelled are immutable
  3. Ship validates sufficient stock — unless the source warehouse has allow_negative_stock = true
  4. Unit cost is captured at ship time from the weighted-average cost in the source warehouse
  5. Shipped quantity equals requested quantity — always set to the full requested amount on ship
  6. Partial receives are supported — pass received_quantities map with item_id => quantity; defaults to shipped_quantity if not specified
  7. Ship creates transfer_out movements — stock is deducted from source warehouse
  8. Receive creates transfer_in movements — stock is added to destination warehouse
  9. Cancelling a shipped transfer reverses stock — increases source warehouse balance by shipped amounts
  10. Cancelling a draft transfer has no stock effect — simply marks as cancelled
  11. Received transfers cannot be cancelled — once stock is received, it cannot be reversed via cancel
  12. Already cancelled transfers cannot be cancelled again — returns 422

Status Lifecycle

Ship/Receive Flow

Moon ERP API Documentation