# W1-INVTR — Transfer requests (manager routes) · in-transit warehouse · sent/received discrepancies [FIN] (full-stack, Inventory)

Migration timestamp range: `2026_11_25_400001…400099`. Read `_COMMON.md` first. [FIN] (stock + GL) — Fable reviews your diff. Another agent (W1-INVRPT) edits Inventory reports/stock-balance/dashboard files at the same time — you own everything transfer-related.

Client:
- «أمين مخزن معين يقدر يعمل طلب تحويل من مخزن آخر أو طلب تحويل عموماً والمدير يوجه الطلب ده للمخزن المناسب»
- «لما تعمل تحويل من مخزن لآخر.. الكمية دي تروح مخزن وسيط وتظهر فيه في التقارير لحين موافقة المخزن الآخر على التحويل»
- «طريقة لمعالجة إذا كان في فروق في الكمية المحولة بين الراسل والمرسل آلية»

Today: `TransferStatus` = draft/shipped/received/cancelled; no submit-approval route for transfers (receipts/issues/adjustments have one). `InventoryTransferController::store` (~:109) requires write rights on `from_warehouse_id` (so under `inventory.warehouse_data_scope=assigned` a keeper can't even draft a pull from another warehouse). `ShipTransfer.php:106` removes qty from source, status Shipped :167; while shipped the qty sits in NO warehouse; in-transit only appears as a GL-account value in `InventoryGlReconciliationService.php:191`. `WarehouseType::transit` exists but is unused. Receive takes received qty per line (`InventoryTransferController.php:331-370`, 0..shipped; over-receipt 422 `transfer_receive_above_shipped`); a short receipt is silently written off — the rest stays out of the source (`ReceiveTransfer.php:106`) and its value goes to the adjustment account (:141-143). GL: one entry at receive Dr destination inv account / Cr source inv account. Production `MaterialRequisition` + `FulfilRequisitionByTransfer` (source per line → draft transfers) is the closest precedent — reuse its patterns.

## A. Transfer request («طلب تحويل»)
- New document `inventory_transfer_requests` (+ lines): number (sequence like other inventory docs), requesting (destination) warehouse, optional preferred source warehouse (null = «طلب عام»), needed-by date, notes, lines (product, unit, qty requested, qty routed), status draft → submitted → routed (partially/fully) → fulfilled / rejected / cancelled, requested_by, routed_by/at.
- Requester needs write rights on the DESTINATION warehouse only (respect warehouse_data_scope). New permissions: `inventory.transfer_requests.view|create|route` (route = manager). Bridge migration grants: create/view to storekeeper-type roles, route to inventory manager/admin.
- Manager «توجيه»: per line pick the source warehouse (show available qty per warehouse inline — you may call the cross-warehouse availability endpoint W1-INVRPT adds `GET inventory/stock-availability?product_ids[]=` if present; otherwise compute availability in your own route endpoint), split a line across sources; confirming creates one DRAFT transfer per source warehouse (linked: transfer.transfer_request_id), status updates as transfers ship/receive. Reject with reason.
- Notifications to the requester/manager via the existing notification service if one is used by inventory approvals (keep minimal).
- FE: «طلبات التحويل» list + form + route dialog (one screen, in place: the manager sees per-line source pickers with stock per warehouse, not a separate screen per line). Link from transfer view to its request and back.

## B. In-transit warehouse
- Setting `inventory.transfers.use_transit` (default **true**). On ship (when on): stock moves source → the company's transit warehouse (type `transit`, auto-created per company — or per branch if warehouses are branch-scoped; name «مخزن التحويلات (بضاعة بالطريق)»), never sellable/pickable, excluded from availability for sales/issues. On receive: transit → destination for the received qty. Lots/batches/serials and cost follow the movement (reuse the movement services the transfer already uses; FEFO/lot allocation must carry through transit).
- Each transfer records `via_transit` at ship time; transfers already shipped before this change (via_transit=false) keep the legacy receive path — don't break them.
- GL (only when `inventory.post_to_gl` is on): ship posts Dr transit-inventory account / Cr source inventory account; receive posts Dr destination / Cr transit. Transit account = the transit warehouse's inventory account, defaulting to a new default account «بضاعة بالطريق» (add to inventory default-accounts config/setup). Reversal on cancel of a shipped transfer (return to source).
- Reports: stock balances/product card show the transit warehouse rows; a «بضاعة بالطريق» filter/report listing open shipped transfers with qty/value/age (simple list endpoint + FE tab in the transfers screen is enough).

## C. Discrepancies («فروق التحويل»)
- On receive with received < shipped (or receiver flags damaged qty): create `inventory_transfer_variances` rows (transfer line, shipped, received, difference, reason, status open/resolved, resolution, resolved_by/at). The difference STAYS in the transit warehouse (no silent write-off).
- Over-receipt stays rejected (422) — but allow the receiver to record «زيادة» as a variance note for the manager? Keep: rejected + message telling them to report it in notes.
- Resolution (permission `inventory.transfers.resolve_variance`, manager): (1) «إرجاع للمخزن المرسل» → transit → source; (2) «استلام لاحق» → transit → destination (late receipt); (3) «شطب كعجز» → adjustment issue from transit with reason (posts to the shortage/adjustment account like adjustments do). Each with GL per above.
- Legacy path (use_transit off or via_transit=false): keep current behaviour but ALSO record the variance row (resolution «شطب تلقائي») so there's a discrepancy report either way.
- FE: receive dialog shows shipped vs received per line with the difference highlighted + reason; a «فروق التحويلات» tab (open variances with resolve actions, in place).

## Acceptance + tests
- Pest: request create (dest-only rights) → route → draft transfers; ship with transit moves qty to transit (stock + GL balanced); receive full; receive short → variance + qty remains in transit; each resolution path (stock + GL + reversal); legacy shipped transfer receive still works; transit warehouse excluded from sale availability.
- Run on dev: migrations; create the transit warehouse for existing companies via the migration or lazily on first ship (say which).
