Transfers and Stockcount
Move stock between branches with FEFO deduction and reconcile shelf counts with system quantities.
You keep branches balanced with transfers in apps/server/src/features/transfers/route.ts and you reconcile the shelf with the system through stockcounts in apps/server/src/features/stockcount/route.ts.
Transfer stock between branches
Post to POST /transfers with from_branch_id, to_branch_id, medicine_id, qty, and optional notes. You need admin, branch_manager, or inventory_manager. The two branches must differ, both must be active, and the medicine must be active.
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"from_branch_id":"'"$FROM"'","to_branch_id":"'"$TO"'","medicine_id":"'"$MEDICINE_ID"'","qty":24,"notes":"Cover weekend demand"}' \
"http://localhost:8000/api/v1/transfers"
The server deducts the source with FEFO through selectTransferFefoBatches in apps/server/src/features/transfers/queries.ts, decrements each source batch, then creates a destination batch whose number follows TRF-{from_branch_id first 4}-{UTC timestamp}-{batch uuid slice} such as TRF-BR001-202605040915-A1B2, carrying the earliest slice expiry and unit cost per the logic in apps/server/src/features/transfers/service.ts. The ledger sees one OUT movement at the source and one IN movement at the destination, both with reference_type: transfer sharing one transfer id. The transfer record lands as COMPLETED immediately. List history with GET /transfers filtered by branch_id, medicine_id, from_date, and to_date, capped at 100 rows.
Print the count sheet
Call GET /stockcount to get your branch sheet: every active medicine with branch_quantity, system_quantity, and batch_count, computed from active batches with remaining quantity and a valid expiry. Fill in the physical count per row on your side before submitting.
curl -H "Authorization: Bearer $TOKEN" "http://localhost:8000/api/v1/stockcount"
Your branch comes from your own auth context, so you cannot count another branch by parameter. Roles allowed are admin, branch_manager, inventory_manager, pharmacist, and auditor for reading.
Submit counts and apply adjustments
Post counted rows to POST /stockcount/submit with an items array of medicine_id, counted_quantity, and optional notes. Each non-zero delta repairs the batch quantities and writes one ADJUST movement with reference_type: stock_count, so the ledger sum for that medicine drops or rises accordingly.
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"items":[{"medicine_id":"'"$MEDICINE_ID"'","counted_quantity":118,"notes":"Shelf plus overstock"}]}' \
"http://localhost:8000/api/v1/stockcount/submit"
Batch repair follows a fixed rule: a surplus is added to the newest active batch (created_at DESC), while a deficit is taken oldest-first by expiry_date ASC across active batches. Zero-variance rows are skipped, and the response reports adjustments_made with per-item db_quantity, counted_quantity, and delta.
Avoid failed transfers and counts
You get 400 Source and destination branches must differ when both ids match, 404 Source branch not found when the source branch is missing or inactive, 404 Destination branch not found when the destination is, and 400 from FEFO when the source holds no sellable batches. Submit an empty items array and you get 400 No items submitted. Count at opening or closing time so sales do not move quantities under you.
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:8000/api/v1/transfers?branch_id=$BRANCH_ID&medicine_id=$MEDICINE_ID"
Re-pull GET /stockcount after submitting to confirm every variance reads zero before you close the session. When a deficit exceeds what the oldest-first walk can cover, recount that shelf before you post again so you do not adjust against a miscount.
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"items":[{"medicine_id":"'"$MEDICINE_ID"'","counted_quantity":0,"notes":"Empty shelf confirmed"}]}' \
"http://localhost:8000/api/v1/stockcount/submit"
Every submit writes to the audit log with your branch and adjustment count, so a supervisor can trace who changed what.
Next steps
Review /inventory/medicines-and-batches to trace an adjustment in a medicine movement history, and open /inventory/alerts-and-expiry to confirm your corrected quantities cleared any low-stock alerts.