Stock Transfers Built

A stock transfer moves a set of products from one stock location to another through a short, deliberate lifecycle — each stage records its own quantity (requested, then sent, then received) so you always know exactly how much left the source and how much actually arrived at the destination.

What a stock transfer is

A transfer has a header — a source location, a destination location, an optional carrier, an optional schedule — and one or more items, each a product and a requested quantity. Nothing moves the moment you create it: a transfer starts life as a plan, and stock only actually leaves the source location when someone Dispatches it, and only actually arrives at the destination when someone Completes it. In between, three numbers travel with each item:

This three-number trail is what lets a transfer absorb the ordinary friction of moving physical goods between locations — a requested quantity that turns out to be unavailable in full, or a received quantity that falls short of what was despatched — without losing the record of what happened at each stage.

There is no edit or delete, ever

Once a transfer is created, its header and its item list are fixed. There is no edit page and no delete action at any state — canEdit() and canDelete() are hard-coded to refuse on this resource. Every change after creation happens only through the four lifecycle actions below (Process, Dispatch, Receive, Complete), each of which is deliberately narrow in what it is allowed to touch.

Where to find it & who can see it

Open Inventory › Stock transfer in the left navigation. The list is visible to anyone holding stock_transfer.view (or a super administrator). Creating a new transfer additionally needs stock_transfer.create — see Creating a transfer below.

The state machine

A transfer moves through six states. Five buttons on the list drive the five console-exposed transitions; there is no automatic step anywhere in this lifecycle — every move forward (or into Cancelled) is a deliberate click by someone holding the right permission.

StateMeaning
DraftJust created. Items carry only a requested quantity; nothing has been actioned yet.
ProcessingProcess has set a quantity-to-send on each item; the transfer is being prepared for despatch but stock has not moved.
In TransitDispatch has run — stock has left the source location and is on its way. No longer cancellable.
ReceivedReceive has recorded what arrived at the destination team's end, but the stock ledger at the destination has not been updated yet.
DoneComplete has run — destination stock is updated. The transfer's final, successful state.
CancelledAbandoned before stock moved. The transfer's final, unsuccessful state.

Creating a transfer

Select New stock transfer from the list. This needs stock_transfer.create. Creation is routed through TransferService::createDraft() rather than a plain Eloquent save, so that a reference number, the item rows and the creating user are all recorded consistently.

  1. Fill in the transfer header

    Choose the transaction type, the source and destination locations, and (optionally) a carrier and a schedule.

  2. Add one or more items

    Use Add product in the items repeater to add a row for each product being moved, with the quantity you intend to send. At least one item is required.

  3. Save

    The transfer is created in the Draft state with a generated reference such as TRF-2026-00001 (year-scoped, zero-padded, sequential). No stock is affected — creation only ever records the plan.

Header fields

FieldTypeRequiredNotes
Transaction typeSelectYesOne of five TransferTransactionType values: StockTransfer (default), MinQtyReplenish, Correction, ReusableQty, ReturnToSource. Purely classificatory — it does not change how the lifecycle behaves.
Source locationSelect (searchable)YesThe stock location stock will be dispatched from. Drawn from warehouse locations — see Warehouses.
Destination locationSelect (searchable)YesThe stock location stock will arrive at.
CarrierSelect (searchable)NoThe delivery carrier moving the goods between locations, where one is used.
Scheduled dateDate pickerNoWhen the transfer is expected to move.
Requires attachmentToggleNoFlags that this transfer needs a supporting document (e.g. a challan). Off by default.
NotesTextareaNoFree text, full width.

Items

FieldTypeRequiredNotes
ProductSelect (searchable)YesShown as "code — name". One row per product; the repeater requires at least one item.
Requested quantityNumberYesMinimum 0.01. This becomes each item's qty_requested — the starting point that Process will refine into a quantity actually being sent.
Screenshot placeholderThe Stock Transfer create form: the Transfer section (Transaction type, Source location, Destination location, Carrier, Scheduled date, Requires attachment toggle, Notes) above the items repeater with Product and Requested quantity columns and an Add product button.

The lifecycle actions

Permissions

The five actions split across two permissions. stock_transfer.approve covers the "outbound" side — Process, Dispatch and Cancel — the decisions made at or before the source location. stock_transfer.receive covers the "inbound" side — Receive and Complete — the decisions made at the destination. A colleague can hold either without the other, so the person confirming what arrived doesn't need the authority to have sent it in the first place. All five additionally require stock_transfer.view to see the list at all.

  1. Process DraftProcessing stock_transfer.approve

    Shown on a Draft transfer. Opens a form pre-filled with every item on the transfer — its product, its requested quantity (read-only, for reference) and an editable quantity to send:

    FieldTypeRequiredNotes
    ProductTextRead-only, shown for context.
    RequestedTextRead-only, shown for context — the quantity set when the transfer was created.
    Qty to sendNumberYesMinimum 0. Becomes the item's qty_sent. Does not have to match the requested quantity.

    Rows cannot be added, removed or reordered in this form — you are only ever setting a quantity against the transfer's existing items. Submitting the form is the confirmation; there is no separate "are you sure?" step. No stock moves yet — this stage only records intent.

  2. Dispatch ProcessingIn Transit stock_transfer.approve Confirmation required

    Shown on a Processing transfer. A confirmation dialog is all it asks for — no form. This is the point at which stock actually leaves the source location:

    What Dispatch actually does

    For every item with a qty_sent greater than zero, the source location's on-hand quantity (see Stock on hand) is decremented by that amount, and an Internal-type stock move is created linking source to destination — created already in the Shipped state, since the goods are physically in transit rather than merely reserved. If the source location does not hold enough stock to cover an item's quantity, Dispatch is refused outright and nothing on the transfer changes. Items left at a zero quantity to send are skipped entirely — no stock move is created for them. The transfer's sent_date is stamped and its state becomes In Transit.

  3. Receive In TransitReceived stock_transfer.receive

    Shown on an In Transit transfer. Opens a form pre-filled with every item — its product, the quantity that was sent (read-only, for reference) and an editable quantity received:

    FieldTypeRequiredNotes
    ProductTextRead-only, shown for context.
    Qty sentTextRead-only, shown for context — the quantity Dispatch sent.
    Qty receivedNumberYesMinimum 0. Becomes the item's qty_received. Cannot exceed the quantity sent — the service rejects the whole submission if any item's received quantity is higher, with the modal staying open and an error notification explaining why.

    As with Process, submitting the form is the confirmation. The transfer's received_date is stamped, the confirming user is recorded, and the state becomes Received — but destination stock is not updated yet; that happens on Complete.

  4. Complete ReceivedDone stock_transfer.receive Confirmation required

    Shown on a Received transfer. A confirmation dialog is all it asks for — no form. This is the point at which stock actually arrives at the destination location:

    What Complete actually does

    For every item with a qty_received greater than zero, the destination location's on-hand quantity is incremented by that amount. Each item's linked stock move (created back at Dispatch) is marked Done with a completed timestamp. The transfer's state becomes Done — its final, successful state.

  5. Cancel Draft or ProcessingCancelled stock_transfer.approve Confirmation required

    Shown in danger red. Only available before stock has moved — once a transfer reaches In Transit, Received or Done it can no longer be cancelled from this screen, because either stock has already left the source location or the transfer has already finished. A confirmation dialog is all it asks for — no reason field.

Why the buttons you see change from row to row

Each action's button is only shown when the transfer is in the exact state that action requires and you hold the permission it needs — the same safety rule as everywhere else in the console (see Getting started). A Done or Cancelled transfer never shows any action at all; it has reached the end of its life.

Screenshot placeholderThe Process modal on a Draft transfer: a read-only list of items (Product, Requested) each with an editable "Qty to send" field, and Submit/Cancel buttons.
Screenshot placeholderThe Receive modal on an In Transit transfer: a read-only list of items (Product, Qty sent) each with an editable "Qty received" field, and Submit/Cancel buttons.

Columns

ColumnShows
referenceThe transfer's identifying number, shown as a badge. Searchable and sortable.
Transaction typeBadge showing type_of_transaction — one of the five TransferTransactionType values.
FromsourceLocation.location_name.
TodestLocation.location_name.
StateA coloured badge — see the state machine above for the colour key.
ItemsA count of the transfer's item rows.
scheduled_dateDate the transfer is expected to move. Toggleable.
created_atDate and time the transfer was created. Sortable, hidden by default.

Filters

Select Filters to open two drop-downs: State (all six TransferState values) and Transaction type (all five TransferTransactionType values). The search box matches on reference, the only searchable column.

Screenshot placeholderThe Stock Transfers list: Reference, Transaction type, From, To, State and Items columns, with the State and Transaction type filter panel open, and Process/Cancel row actions visible on a Draft transfer.

The View page

Selecting a transfer's reference opens its view page. The top section is read-only: reference, transaction type, from/to locations, the coloured state badge, carrier, scheduled date, sent date, received date and notes.

Below it, an Items tab lists every item on the transfer, also read-only — items are never added, edited or removed here; they only change through the lifecycle actions on the list.

ColumnShows
Codeproduct.code.
Productproduct.name.
Requestedqty_requested — set at creation.
Sentqty_sent — set by Process, zero until then.
Receivedqty_received — set by Receive, zero until then.
MoveA coloured badge showing the state of the item's linked stock move — blank until Dispatch creates one.
Screenshot placeholderThe Stock Transfer view page: the read-only header details at the top, and the Items tab below listing Code, Product, Requested, Sent, Received and Move columns for a Received transfer.

Attachments tab Built

The view page also has an Attachments tab where you can attach supporting documents — a challan, a photo, a signed receipt — to the transfer. Unlike the lifecycle actions, this is available at every stage: draft, processing, in-transit, received or done. Each attachment records which stage the transfer was at when the file was added, who uploaded it, and its size.

This is where the Requires attachment flag set at creation is satisfied — the flag marks that a transfer should carry a document, and this tab is where you provide it.

Related pages

See Stock on hand to watch a location's qty_on_hand fall when a transfer is dispatched and rise when it is completed; Warehouses for how source and destination locations are set up; and Stock Moves for the internal move each dispatched item creates and how its state relates to the transfer.