Dispatch & Delivery¶
Book: Sales & Revenue | Module: Dispatch & Delivery Scope: How a confirmed order/invoice physically gets loaded from the store, dispatched out the gate, delivered to the customer, and confirmed delivered. Nav home: Sidebar → Sales & Receivables → Dispatch & Delivery (
resources/views/admin/includes/sidebar_includes/sales_and_receivables.blade.php:1690).
1. Purpose¶
Once a customer's order has been taken and turned into an invoice/requisition (that happens in the Order Taking & Sales Invoicing chapter), the goods still have to physically leave the warehouse and reach the shop. This module covers that whole physical journey:
- Loading sheet dispatch — a store keeper picks the goods for a route/delivery off the shelf and stages them at a loading bay, then a second person validates (double-counts) them, then they get loaded onto a vehicle. Any shortfall becomes a return.
- Delivery schedules — the loaded goods become a delivery (a trip). The system tracks the vehicle, driver, turnboy/operator, the customers to visit, tonnage, and route stops. Deliveries can be merged (combine trips) or split (break one trip into two), including across branches.
- Gate pass / gateman verification — before a loaded truck leaves the yard, a gate pass is issued and a gateman at the gate verifies the driver's identity (with a photo), scans the gate-pass code, and checks the truck is leaving within an allowed time window.
- Delivery confirmation — out on the route, the driver/delivery man marks each customer delivered on the mobile app, usually confirmed by a one-time code (OTP) sent to the customer. Deliveries that cannot be completed are logged as delivery failures for back-office review.
The guiding idea is a chain of custody: every hand-off (picked → staged → validated → loaded → out the gate → delivered) is recorded with who did it and when, and most hand-offs enforce that a different person does the next step (segregation of duties).
What this chapter does NOT cover (handed off elsewhere): - Creating the order/invoice → Order Taking & Sales Invoicing. - Vehicle/fleet master data, fuel, tyres, garage management → Fleet & Logistics. - Inbound goods coming IN from suppliers → Logistics & Inbound Deliveries. (This chapter is strictly outbound to customers.) - Route definitions themselves → Route Management.
2. Users & roles¶
Access is gated two ways in the code:
- Backoffice (web): Controller::mypermissionsforAModule() returns the literal string 'superadmin' when role_id == 1, otherwise the user's permission array (app/Http/Controllers/Controller.php:1115). Menu items and actions check isset($my_permissions['<model>___<action>']) or the can('<action>','<model>') helper.
- Mobile (Flutter app bizwizorderanddelivery): role determines the home screen (gateman, delivery man/driver, POS dispatcher, salesman/route roles) — lib/app/app.router.dart.
| Role / actor | Where | Does what | Key permissions (slugs) |
|---|---|---|---|
| Store keeper / dispatcher | Web + POS dispatcher mobile | Picks & stages loading-sheet items to the bay; scoped to their own bin (wa_unit_of_measures_id) |
store-loading-sheet___view, store-loading-sheet___view-undispatched, store-loading-sheet___validate, loading-sheet___preparation |
| Validator | Web | Double-counts staged items before loading; must not be the same person who prepared | store-loading-sheet___validate, loading-sheet___validation |
| Loader | Web | Assigns vehicle & completes loading (3‑stage flow) | loading-sheet___loading |
| Delivery man / Driver | Mobile | Drives the trip, marks each customer delivered (OTP), reports failures | (route middleware; mobile role config('app.delivery_role', 184)) |
| Turnboy / Operator | Mobile | Assists driver; an operator can be assigned exclusive app control of a delivery | delivery-schedule___... |
| Gateman | Mobile | Verifies driver identity + photo at the gate, scans gate pass, enforces time window | (gate flows; see gateman_verified_by) |
| Dispatch/Delivery supervisor | Web | Views schedules, merges/splits, reviews delivery failures, issues standalone gate passes, clears reports | dispatch-and-delivery___view, delivery-schedule___view, delivery-schedule___clear-delivery-report, delivery-schedule___view-action-log, delivery-merging-and-splitting___view/add/reverse, cross-branch-delivery-merging___view/add/reverse, delivery-failures___view, standalone-gate-passes___view/add/edit/delete, dispatched-loading-sheets___view, dispatched-loading-sheets___view-all, shift_delivery_report___view, pending-returns-allocation___view, interbranch-loading-sheets___view/dispatch, credit-sales___dispatch |
The whole menu block is wrapped in dispatch-and-delivery___view OR superadmin (sales_and_receivables.blade.php:1690).
Menu → destination map (sales_and_receivables.blade.php:1713–1839):
| Menu label | Route name | Controller |
|---|---|---|
| Dispatch Loading Sheet | store-loading-sheets.index |
ParkingListController@index |
| Pending Returns Allocation | pending-returns.index |
PendingReturnsController@index |
| User Activity | user-activity.index |
UserActivityController@index |
| Dispatched Loading Sheets | store-loading-sheets.dispatched |
ParkingListController@getDispatched |
| Interbranch Loading Sheets | interbranch-loading-sheets.index |
InterbranchLoadingSheetController@index |
| Deliveries | delivery-schedules.index |
DeliveryScheduleController@index |
| Merging & Splitting | delivery-merging-splitting.index |
DeliverySplittingAndMergingController@index |
| Cross-Branch Merging | cross-branch-delivery-merging.index |
CrossBranchDeliveryMergingController@index |
| Delivery Failures | deliveries.failures.index |
DeliveryFailureController@index |
| Shift Delivery Report | dispatch-reports.shift-delivery-report |
DeliveryReportController@index |
| POS Dispatch / POS Dispatch Log / POS Cash Sales Returns | pos-cash-sales.dispatch etc. |
PosCashSalesController (POS cash-sale dispatch — light touch here) |
| Credit Sales Dispatch | credit-sales-dispatch |
CreditSalesController@dispatchScreen |
| Delivery Action Logs | delivery-schedules.logs |
DeliveryScheduleController@logs |
| Gate Passes | standalone-gate-passes.index |
GatePassController@index |
Routes live in routes/web.php:5175–5279 (web) and routes/modules/api.php:493–506 + routes/api.php:661–765 (mobile). Interbranch is in routes/modules/logistics.php:181–184.
3. Processes¶
There are two loading-sheet dispatch flows in the code, selected by a global setting:
- Sales-order flow (identify-sales-orders-from-normal-orders setting = 1): a strict 3-stage state machine (prepare → validate → load).
- Legacy "normal" flow (setting off): a simpler path using just a dispatched boolean, or the two-step bay/validate flow.
3.1 Loading sheet dispatch — the 3-stage state machine (sales-order flow)¶
The core record is SalesmanShiftStoreDispatch (SSSD) with child SalesmanShiftStoreDispatchItem (SSSDI). Its status column drives everything. All state names, stage numbers, and access rules are defined in app/SalesmanShiftStoreDispatch.php (getCurrentStageNumber() :233, canUserAccessStage() :288).
stateDiagram-v2
[*] --> pending: sheet created (from order/invoice)
pending --> preparing: startPreparation (Stage 1)
preparing --> prepared: completePreparation (all items picked)
prepared --> validating: startValidation (Stage 2, must be a DIFFERENT user)
validating --> validated: completeValidationStage (all counted, returns allocated)
validated --> loading: startLoading (Stage 3)
loading --> dispatched: completeLoading (vehicle assigned, goods on truck)
dispatched --> [*]
note right of preparing
Stage 1: Preparation
perm loading-sheet___preparation
writes SSSDI.dispatched_quantity, is_dispatched
end note
note right of validating
Stage 2: Validation
perm loading-sheet___validation
cannot be preparation_completed_by
writes SSSDI.validated_quantity, variance_quantity, is_validated
end note
note right of loading
Stage 3: Loading
perm loading-sheet___loading
assignVehicle -> DeliverySchedule.vehicle_id
end note
Each transition is also mirrored onto the linked SalesOrderLoadingSheet rows (updateLinkedSalesOrderLoadingSheets(['status' => ...])) and, at completion, onto DeliverySchedule.status.
Stage-by-stage (all in app/Http/Controllers/StoreKeeper/ParkingListController.php):
- Stage 1 – Preparation (pick from shelf to bay):
startPreparation:2212 — requirespending, permloading-sheet___preparation; setsstatus='preparing',preparation_started_at/_by.prepareItemWorkflow:2717 — per item:dispatched_quantity,is_dispatched=true.completePreparation:2275 — requires all items prepared; setsstatus='prepared',preparation_completed_at/_by.- Stage 2 – Validation (independent recount):
startValidation:2341 — requiresprepared, permloading-sheet___validation; blocks if caller ==preparation_completed_by(:2367); setsstatus='validating'.validateItemWorkflow:2771 — per item:validated_quantity,variance_quantity = max(0, dispatched − validated),is_validated=true,returns_allocated = !hasVariance.completeValidationStage:2409 — requires all validated; ifSalesmanShiftStoreDispatch::requiresReturnsAllocation()(a hard-coded model toggle, currentlytrue, :27) then all variances must have returns allocated; setsstatus='validated',validator_id,validated_at.- Stage 3 – Loading (onto vehicle):
startLoading:2486 — requiresvalidated, permloading-sheet___loading; setsstatus='loading'.assignVehicle:2549 — writesDeliverySchedule.vehicle_id; updates linked SO sheets.completeLoading:2598 — requires vehicle set; sets this sheetstatus='dispatched'+dispatched=true, bulk-updates sibling SSSD rows for the same delivery todispatched(:2655), setsDeliverySchedule.status='consolidated'(:2698), and rolls the shift's deliveriesconsolidating → consolidatedwhen nothing is left.
Audit & locking: every stage writes UserActivityLog::logCompletion(...) and toggles UserActivityStatus busy/idle so two users can't edit the same sheet at once (returns HTTP 423 if locked). Cross-branch merges gate each transition through CrossBranchLoadStopService::assertDispatchWorkAllowed().
3.2 Loading sheet dispatch — two-step web flow & legacy¶
An alternative simpler path (also ParkingListController, and the mobile equivalents) uses statuses pending → dispatched_to_bay → validated:
stateDiagram-v2
[*] --> pending
pending --> dispatched_to_bay: dispatchToBay / completeDispatchToBay
dispatched_to_bay --> validated: processValidation / completeDispatch (dispatched=true)
validated --> [*]
- Web:
dispatchToBayView:757,dispatchItem:817,completeDispatchToBay:881 (→dispatched_to_bay);validateLoadingSheet:1000,validateItem:1397,processValidationWeb:1238 /completeDispatch:1465 (→validated). - Mobile:
POST store-loading-sheets/dispatch-to-bay→dispatchToBay;POST store-loading-sheets/validate→processValidation; single-stepPOST store-loading-sheets/dispatch→processDispatch(setsdispatched=truedirectly). - Returns: shortfalls found at validation are turned into
WaInventoryLocationItemReturnrows withapproval_status='pending_approval',is_auto_return=true(viacreateReturnsFromUserSelections(),ParkingListController.php:1327). These flow to the returns/approvals module and surface under Pending Returns Allocation. - Legacy "normal" flow: uses only the
dispatchedboolean on SSSD (no status enum). Listing filters onwhere('dispatched', false/true).
3.3 Delivery schedule lifecycle & gate pass¶
Once loading completes, the DeliverySchedule carries the trip. Its status progresses (string literals — no PHP constants; comment on delivery_schedules migration: "consolidating, consolidated, loaded, in_progress, finished"):
flowchart TD
A["consolidating / consolidated<br/>(loading in store)"] --> B["loaded<br/>(all cross-branch stops done)"]
B --> C["Gate pass initiated<br/>gate_pass_status = initiated"]
C --> D["Gateman verifies driver + scans code<br/>gateman_verification_status: pending -> approved<br/>gate_pass_status: initiated -> verified"]
D --> E["in_progress<br/>(truck on route, driver delivering)"]
E --> F["finished<br/>(shift ended, report generated)"]
E -.->|per customer fails| G["Delivery Failure logged<br/>status = pending"]
A -.->|merge/split| A2["merged / reversed<br/>+ is_merge_result / is_split_result flags"]
Two parallel state columns ride alongside status:
- gate_pass_status: pending → initiated → verified (default pending; migration 2024_04_18_114904).
- gateman_verification_status: pending → approved | rejected (migration 2026_01_19_154148; also stores gateman_rejection_reason, gateman_verified_at, gateman_verified_by, driver_verification_image).
Gate pass issuance (web):
- DeliveryScheduleController@createGatePass (:3680, primary path) sets gate_pass_status='initiated', gate_pass_initiated_at/_by, and when a valid date is given gateman_verification_status='pending'; it blocks issuance if the vehicle still has an uncleared finished report (sales-order flow). initiateGatePass (:3663) and downloadGatePass (:3647) set the issued_gate_pass/has_gatepass boolean flags. Clearing a stale report: clearDeliveryReport (:2827) sets report_cleared_at/_by (perm delivery-schedule___clear-delivery-report).
- Standalone gate passes (not tied to a delivery) are created via GatePassController@store (:300) with status='initiated'.
Gate verification (mobile gateman):
sequenceDiagram
participant G as Gateman (mobile)
participant API as DeliveryScheduleController (API)
participant DB as delivery_schedules / gate_passes
G->>API: GET /gateManTrips (getPendingGatePassVerifications :4052)
API-->>G: trips where gate_pass_status=initiated, gateman_verification_status in (pending,approved)
G->>API: POST /gatemanVerification (approve/reject + driver photo) :4313
API->>DB: gateman_verification_status = approved|rejected, driver_verification_image, gateman_verified_by/_at
G->>API: POST /gatePassValidate (scan code) :4184
API->>API: match code "{delivery_number}-{routes}" + time window 04:50-18:00
API->>DB: gate_pass_status = verified, record shift-start mileage
API-->>G: OK (+ SMS overstay warning to driver)
3.4 Delivery confirmation (mobile driver)¶
Out on the route, the driver confirms each customer via DeliveryScheduleController API methods:
sequenceDiagram
participant D as Driver (mobile)
participant API as DeliveryScheduleController
participant Cust as Customer
D->>API: POST /initiate-order-delivery (:3271)
API->>API: geo-fence check (order_location_logs.driver_status = passed|failed)
alt REQUEST_DELIVERY_OTP on
API->>Cust: SMS 4-digit delivery_otp
API-->>D: show_delivery_otp = true
D->>API: POST /verify-delivery-otp (:3467)
API->>API: OTP match?
API->>API: delivery_code_status=approved, visited=true, order.status=Delivered, delivery_date=now
API->>API: dispatch NotifySeviOrderDeliveredJob
else OTP off
API->>API: delivery_code_status = approved immediately (:3350)
end
completeDelivery(:3221) finalizes with a payment reference: customerdelivery_code_status='approved', ordersstatus='COMPLETED'.- Customer-level display status:
DeliveryScheduleCustomer::status()mapsdelivery_code_status=='approved'→ "Delivered", else "Not Delivered" (app/DeliveryScheduleCustomer.php:74). - The schedule reaches
finishedviaDeliveryScheduleFinishTracker::recordFinish()(app/Services/DeliveryScheduleFinishTracker.php:31) when the shift ends — this also writes abackend_delivery_actionsaudit row.
3.5 Delivery failures¶
When a customer can't be served, the driver files a failure (mobile) via DeliveryFailureController@reportDeliveryFailure (:235): creates a DeliveryFailure with status='pending', attaches field values + customers, and (if setting ALLOW_REPORT_DELIVERY_FAILURE_VIA_CRM) dispatches CreateCaseTicketJob. Back office reviews under Delivery Failures:
stateDiagram-v2
[*] --> pending: driver reports (mobile)
pending --> approved: approve() :116 (SMS to driver)
pending --> rejected: reject() :130 (reason required, SMS to driver)
approved --> [*]
rejected --> [*]
3.6 Merging & splitting deliveries¶
- Same-branch (
DeliverySplittingAndMergingController): eligible schedules are those inconsolidating/consolidated.mergeDeliveries(:338) creates a new schedule (is_merge_result=true) and sets each sourcestatus='merged'(:427).splitDelivery(:672) creates a child (is_split_result=true,split_source_delivery_schedule_id). Both record aDeliveryOperationReversal(OPERATION_MERGE/OPERATION_SPLIT) so they can be reversed (reverseMerge:832,reverseSplit:877, perm...___reverse). - Cross-branch (
CrossBranchDeliveryMergingController): merges schedules from ≥2 branches into a multi-stop delivery (is_cross_branch_merge=true); sources →status='merged'. The truck then works sequential load stops (DeliveryScheduleRouteStop, statusespending → in_progress → completed,app/Models/DeliveryScheduleRouteStop.php:15); when all stops complete the schedule flips tostatus='loaded'(CrossBranchLoadStopService.php:369).
4. Tables touched & key data¶
| Process | Table (migration) | Key columns / states |
|---|---|---|
| Loading sheet dispatch (3-stage & bay) | salesman_shift_store_dispatches (2023_11_30_074725 + 2026_01_15_100000) |
status enum-ish (pending, preparing, prepared, validating, validated, loading, dispatched, also legacy dispatched_to_bay); dispatched bool; dispatch_time, dispatcher_id, validator_id, validated_at; stage timestamps preparation_/validation_/loading_started_at/_completed_at/_by; dispatched_to_bay_time/_by; delivery_schedule_id, shift_id, store_id, bin_location_id |
| Loading sheet items | salesman_shift_store_dispatch_items |
dispatched_quantity, validated_quantity, variance_quantity, total_quantity, is_dispatched, is_validated, returns_allocated, wa_inventory_item_id, dispatch_id |
| Delivery schedule | delivery_schedules (2023_11_22_170401 + many alters) |
status (consolidating, consolidated, loaded, in_progress, finished, reversed, merged); gate_pass_status (pending, initiated, verified); gateman_verification_status (pending, approved, rejected); gate_pass_valid_date, gate_pass_initiated_at/_by, gateman_verified_at/_by, gateman_rejection_reason, driver_verification_image; vehicle_id, driver_id, turnboy_id, operator_id; mileage fields; is_merge_result, merged_source_deliveries (json), is_split_result, split_source_delivery_schedule_id, result_delivery_schedule_id, is_cross_branch_merge; sales_order_loading_sheet_id(_ids); finished_by, report_cleared_at/_by, issued_gate_pass, has_gatepass |
| Delivery customers | delivery_schedule_customers (2023_11_23_180335) |
customer_id, order_id (comma-sep requisition IDs), delivery_code, delivery_code_status (pending, sent, approved), visited, tonnage_kg, arrival_time, departure_time |
| Delivery items | delivery_schedule_items |
wa_inventory_item_id, total_quantity, received_quantity, uom cols |
| Gate pass | gate_passes (2026_01_23_090630 + alters) |
status (pending, initiated, verified, rejected, also cancelled in code); gate_pass_valid_date, time_window_start (05:00), time_window_end (18:00); vehicle_id, driver_id, route_id, delivery_schedule_id, inbound_delivery_id, issued_by, verified_by, verified_at, driver_image; soft deletes |
| Delivery failures | delivery_failures (2025_06_19_114041 + alters) |
branch_id, driver_id, delivery_schedule_id, reason, status (pending, approved, rejected), rejection_reason, approved_by, approved_at; pivot customer_delivery_failure |
| Route stops (cross-branch) | delivery_schedule_route_stops (2026_07_13_160100) |
status (pending, in_progress, completed), route_sequence, merged_delivery_schedule_id |
| Reversals | delivery_operation_reversals (2026_07_30_120000) |
result_delivery_schedule_id, reversed_at, operation type |
| Analytics / logs | delivery_center_logs, customer_offloading_time_logs, backend_delivery_actions, order_location_logs |
travel speed, offloading time, action audit, geo-fence result (driver_status) |
| Interbranch dispatch | interbranch_loading_sheet_dispatches / _items (2025_08_10_145018) |
dispatch_status (pending, dispatched), dispatched_at, loaded_quantity, inbound_delivery_id |
| Legacy loading sheets / small packs | loading_sheet_dispatches / _items (2023_10_24_105243); delivery_man_shifts (status open); parking_list_items, parking_list_item_dispatches |
delivery_status (not_started, in_progress, finished) |
Note on model naming:
ParkingListItem/ParkingListItemDispatchmodels exist but are imported-but-unused inParkingListController; the real workhorse tables aresalesman_shift_store_dispatches(+items).LoadingSheetDispatch/LoadingSheetDispatchItemback the small packs / legacy dispatch (SmallPacksContoller), not the current loading-sheet UI.
5. Interactions with other modules¶
Inbound (into this module):
- Order Taking & Sales Invoicing — confirmed requisitions/invoices (wa_internal_requisitions) and their loading sheets (SalesOrderLoadingSheet) are the source that seeds SalesmanShiftStoreDispatch rows and delivery_schedule_customers.order_id.
- Route Management — routes, route customers, and delivery centres define who is on the trip and where.
- Fleet & Logistics — vehicles, drivers, turnboys, garage/idle status; assignVehicle writes DeliverySchedule.vehicle_id; createFuelLpo() (DeliverySchedule.php:428) creates a draft NewFuelEntry fuel LPO for the trip.
Outbound (out of this module):
- Returns / Approvals — validation shortfalls create WaInventoryLocationItemReturn (pending_approval) → surfaced as Pending Returns Allocation.
- Inventory — dispatch decrements stock at the store/bin; interbranch stock moves are created at sheet creation time.
- Order status — delivery confirmation writes back wa_internal_requisitions.status = Delivered/COMPLETED and delivery_date, feeding sales/AR reporting.
- SEVI (settlement) — OTP-verified delivery dispatches NotifySeviOrderDeliveredJob (DeliveryScheduleController ~:3561).
- CRM — delivery failures can spawn a case ticket (CreateCaseTicketJob).
- SMS — OTPs to customers, overstay warnings and failure approve/reject notices to drivers (SmsService).
- Logistics & Inbound Deliveries — shares the GatePass model (inbound passes via inbound_delivery_id) and InterbranchLoadingSheet* (interbranch is inbound-adjacent; documented here only for the outbound gate).
6. Alternatives & variants¶
- Sales-order flow vs. legacy normal flow — the single most important switch.
Setting slug 'identify-sales-orders-from-normal-orders' == 1turns on the 3-stagepreparing/prepared/validating/validated/loading/dispatchedstate machine; off falls back to thedispatchedboolean / two-step bay flow. Checked pervasively inParkingListControllerandDeliveryScheduleController. - Returns-allocation gate —
SalesmanShiftStoreDispatch::requiresReturnsAllocation()is a hard-coded PHP toggle (currentlytrue,SalesmanShiftStoreDispatch.php:27) rather than a DB setting; whentrue, validation cannot complete with unallocated variances. - Delivery OTP — setting
REQUEST_DELIVERY_OTPdecides whether the driver must enter a customer OTP or can mark delivered immediately. - Geo-fence enforcement — setting
DELIVERY_DISTANCE(with a 400 m grace) governs whether an out-of-range delivery is blocked (order_location_logs.driver_status = failed). - CRM failure ticketing — setting
ALLOW_REPORT_DELIVERY_FAILURE_VIA_CRMgates automatic case-ticket creation on failure. - UOM-based inventory —
AdministrationSetting INVENTORY_SETUP == 'UOM BASED'changes quantity breakdowns on interbranch/credit-sales dispatch and delivery sheets. - Turnboy read-only mobile —
isTurnboyReadOnlyMobileAccessEnabled()blocks assigning such a turnboy as delivery operator. - Time window on gate pass — defaults
time_window_start=05:00,time_window_end=18:00;validateGatePassenforces ~04:50–18:00. - Driver undispatched workflow — an alternative driver-driven prepare/verify/dispatch workflow exists (columns
driver_workflow_status,driver_prep_*,driver_verification_*on SSSD; settingENABLE_DRIVER_ACCESS_TO_UNDISPATCHED_ITEMS) but is currently disabled — routes and menu are commented out (sales_and_receivables.blade.php:1722,routes/web.php:5194), reverted to pre-KB-1434 behavior. - Standalone vs. delivery-linked gate passes —
GatePassControllermanages standalone passes (editable/deletable) and read-only aggregates passes derived from delivery schedules and inbound deliveries; standalone Gate Passes menu is only shown when the sales-order setting is on. - POS cash-sale dispatch & Credit-sales dispatch — separate, lighter dispatch screens (
pos-cash-sales.dispatch,CreditSalesController@dispatchScreen) for cash/credit sales rather than route loading sheets; credit-sales dispatch lists backend requisitions inCOMPLETED/PAIDstatus with undispatched items for the user's bin. - Small packs / legacy loading sheets —
SmallPacksContoller(routes/modules/sales_and_receivables.php:250) uses the olderloading_sheet_dispatchestables; not surfaced in the current Dispatch & Delivery menu. - Flavor/tenant differences — all behavioral differences above are driven by per-tenant
Setting/AdministrationSettingrows and permission assignment; no hard-coded tenant branches were observed in the traced files.
7. Open questions to confirm (inferences flagged)¶
- [Inference] The
dispatched_to_baytwo-step web flow appears to be an earlier version of the 3-stage workflow (both live inParkingListController). Confirm which flow is active per tenant, and whether both can be enabled simultaneously. - [Inference]
delivery_schedules.statusvaluesconsolidating/consolidated/loaded/in_progress— the transition intoin_progress(truck starts route) was not located in the four controllers traced (the finish→finishedtransition is confirmed inDeliveryScheduleFinishTracker). Confirm wherein_progressis set (likely a driver-app "start delivery" endpoint such asShiftController@endDelivery/start, orSalesAndDeliveryShiftController). - [Inference] Small packs (
SmallPacksContoller) and the legacyloading_sheet_dispatchestables: confirm whether any live tenant still uses them, or if they are fully superseded bysalesman_shift_store_dispatches. - [Inference]
DeliveryManShift(status='open') andDeliveryManShiftCustomerare bare models queried fromController.php:694; they look like an older driver-app shift concept parallel toDeliverySchedule. Confirm whether they are still written to or are dead schema. - [Unconfirmed] Exact permission-slug seeding — slugs are inferred from
@if isset($my_permissions['<slug>'])andcan()usage in the sidebar/controllers; the authoritative permission list (seeder/table) was not located by grep. Confirm the canonical slug names and default role grants. - [Inference]
gate_pass_statuson a delivery vs. the separategate_passesrow:initiateGatePasssets only boolean flags whilecreateGatePasssetsgate_pass_status='initiated'. Confirm which is the current UI entry point and whetherinitiateGatePassis legacy. - [Unconfirmed] POS cash-sale dispatch (
pos-cash-sales.dispatch) sits in this menu but is owned largely by the POS module; confirm the boundary — this chapter treats it as a light reference only. - [Inference] The
requiresReturnsAllocation()hard-coded toggle: confirm it is intended to stay code-level (not a per-tenant setting) given the "CHANGE THIS LINE" comment.
8. Source references¶
Nav / IA
- resources/views/admin/includes/sidebar_includes/sales_and_receivables.blade.php:1690 (Dispatch & Delivery menu block)
Routes
- routes/web.php:5175–5279 (loading sheets, delivery schedules, merging/splitting, gate pass, failures)
- routes/modules/api.php:493–506 (mobile store-loading-sheets + delivery-schedules)
- routes/api.php:661–765 (mobile delivery confirmation, OTP, gateman gate-pass endpoints)
- routes/modules/logistics.php:181–184 (interbranch loading sheets)
- routes/creditSales.php:85 (credit-sales dispatch)
- routes/modules/sales_and_receivables.php:250 (small packs — legacy)
Controllers
- app/Http/Controllers/StoreKeeper/ParkingListController.php (loading-sheet dispatch, 3-stage workflow, bay/validate, returns)
- app/Http/Controllers/Admin/DeliveryScheduleController.php (schedules, gate pass issue/verify, driver delivery API, reports)
- app/Http/Controllers/Admin/GatePassController.php (standalone gate passes)
- app/Http/Controllers/Admin/DeliveryFailureController.php (failure review + mobile reporting)
- app/Http/Controllers/Admin/DeliverySplittingAndMergingController.php (same-branch merge/split)
- app/Http/Controllers/Admin/CrossBranchDeliveryMergingController.php (cross-branch merge + load stops)
- app/Http/Controllers/Admin/InterbranchLoadingSheetController.php (interbranch dispatch)
- app/Http/Controllers/Admin/CreditSales/CreditSalesController.php:36 (dispatchScreen)
- app/Http/Controllers/Api/DeliveryController.php (legacy deliver/return endpoints)
Models
- app/SalesmanShiftStoreDispatch.php (state machine, stage access rules — :233, :288), app/SalesmanShiftStoreDispatchItem.php
- app/DeliverySchedule.php (statuses, scopes, merge/split, tonnage, fuel LPO, center logging)
- app/DeliveryScheduleCustomer.php (delivery status accessor :74, tonnage), app/DeliveryScheduleItem.php
- app/GatePass.php, app/Models/DeliveryFailure.php, app/Models/DeliveryScheduleRouteStop.php (:15), app/Models/DeliveryOperationReversal.php
- app/DeliveryManShift.php, app/DeliveryManShiftCustomer.php (legacy driver-shift)
- app/LoadingSheetDispatch.php, app/LoadingSheetDispatchItem.php, app/ParkingListItem.php, app/ParkingListItemDispatch.php (legacy / unused-in-current-flow)
Services
- app/Services/DeliveryScheduleFinishTracker.php:31 (recordFinish → finished)
- CrossBranchLoadStopService.php:369 (all stops done → loaded), CrossBranchDeliveryMergeService.php:121
Key migrations
- 2023_11_30_074725_create_salesman_shift_store_dispatches_table.php, 2026_01_15_100000_add_status_to_salesman_shift_store_dispatches.php
- 2023_11_22_170401_create_delivery_schedules_table.php, 2024_04_18_114904_add_gate_pass_status_to_delivery_schedules.php, 2026_01_19_154148_add_gateman_verification_to_delivery_schedules_table.php
- 2026_01_23_090630_create_gate_passes_table.php, 2026_01_23_121353_add_time_window_to_gate_passes_table.php
- 2025_06_19_114041_create_delivery_failures_table.php, 2026_07_13_160100_create_delivery_schedule_route_stops_table.php
Mobile app (bizwizorderanddelivery, Flutter)
- lib/app/app.router.dart (role home views: gateman, delivery man, POS dispatcher)
- lib/utils/constants/api_endpoints.dart:108,109,182,183,184 (delivery/gateman endpoint constants)
- lib/ui/views/gateman/, lib/ui/views/delivery/, lib/ui/views/pos/pos_dispatcher_home, lib/services/gate_man_verification_service.dart:43