W104: Create transfer between locations
Records the outbound leg of a finished-goods transfer between two locations. Triggered from the Finished Goods Inventory Transfers page, "New transfer". The single outbound row encodes both source and destination via to_location_id; the inbound leg is added later via W105 when the receiving location confirms arrival. Mirrors the materials equivalent (W097) with skuId in place of materialId and TFR-FG-NNN refs.
Steps
-
Validate the SKU. Confirm it exists and is active.
-
Validate the source and destination locations. Confirm both exist, are active in this org, and are distinct.
-
Write the outbound TRANSFER row. Call
FinishedGoodsInventory.recordTransfer({ skuId, locationId: sourceLocationId, toLocationId: destinationLocationId, quantity, eventDate, notes }). The L2 op auto-generates a per-tenantTFR-FG-NNNref. Stored shape:location_id = sourceLocationId,to_location_id = destinationLocationId,quantity = quantity(positive; the L1 schema requires it),event_type = 'TRANSFER'. Direction is encoded inevent_type; the summary query treats TRANSFER as outflow atlocation_idand inflow atto_location_idonce paired with a RECEIVE.
Returns
The outbound TRANSFER row plus the generated ref.
Business rules
- Single row encodes both ends. The TRANSFER row carries the source in
location_idand destination into_location_id. Fixed at creation. - In-transit derivation. A transfer is "in transit" while its outbound TRANSFER row exists without a matching RECEIVE row (matched by
ref). The in-transit summary usesto_location_idto attribute the inbound quantity to the destination. - Quantities are always positive. The L1 schema mandates
quantity > 0; theTRANSFERevent type encodes the outflow atlocation_idand the expected inflow atto_location_id. Manual transfers carry no unit cost (unit_costis null). - No stock guard in v1. The workflow does not block on insufficient source on-hand.
Errors
NotFoundError. The SKU or a location was not found.ValidationError. Source and destination are the same.