Skip to main content

Transport

Transport is the operational screen for a route after route-picking delivery sheets exist. It creates the route header, attaches route-picking sheets, confirms that each sheet is ready, prepares the loading step, starts the trip, lets the driver process every customer stop, records shipping/COD payments and evidence, then closes the route after bank and oil actions are complete.

The Mobile app is the behavior reference. POS Terminal uses the same service contract and should keep the same business rules; differences are mainly layout and navigation.

App Entry Points​

AppTargetNotes
MobileTransportList -> TransportDetailStack-based list/detail screen.
POS TerminalHome menu transport -> TransportDetailFormSplit-pane menu under the Home screen.

End-to-End Workflow​

Route Header​

Field / behaviorRule
Transport dateDefaults to today when creating a new transport.
Branch / warehouse groupTaken from the selected branch context and sent as branchId, whGrpCode, and whGrpName.
DriverDefaults from current user first/last name when creating.
VehicleRequired before saving and before pulling delivery sheets. Selecting a vehicle fills the license plate and driver-related context. The picker shows the two test vehicles assigned to the selected branch above API vehicles.
CompanyUses the selected branch company code where available.
RouteCan be copied from the first selected delivery sheet if the transport is created while selecting a sheet.
Bank / oil flagsflagBank and flagGas are stored on the transport header and are required before finishing.

Validation before save:

ConditionError behavior
Missing license plateBlocks save/open delivery picker.
Missing driver nameBlocks save.
Missing ship dateBlocks save.

Test vehicles in the picker​

Mobile and POS Terminal share branch-scoped READY test vehicles. Each supported branch sees only its own two plates: Dummy-ACR001/002, Dummy-UBN001/002, Dummy-BRM001/002, Dummy-KKN001/002, Dummy-MDH001/002, Dummy-SRN001/002, Dummy-NMA001/002, Dummy-PLK001/002, Dummy-SSK001/002, Dummy-UDN001/002, or Dummy-ROI001/002. They are shown before real vehicles in the picker and are filtered by the same search query as real vehicles. A search that does not match a branch dummy-vehicle field does not show it; unsupported branches do not receive dummy vehicles.

Test vehicles are not valid for an operational trip. If a dummy license plate is selected, pressing Start transport is blocked with Please select a real vehicle first. The license plate remains editable until the transport has started (startedAt is set); it is then locked.

Delivery Sheet Pulling​

Delivery sheets are route-picking documents created from the Shipping Route menu. The transport screen attaches them to a transport round.

Rules:

ActionRule
Add delivery sheetAllowed only before the transport leaves draft. A new transport is created automatically if the user selects a sheet before saving.
Remove delivery sheetAllowed only in draft, before startedAt, and only for a user who can edit the transport. See the detailed delete rules below.
Finalize one sheetMarks that delivery sheet as FINISHED by calling the delivery status update.
Finalize all pending sheetsLoops through all sheets with empty or NULL delivery status and marks them FINISHED. The transport owner or override user code 999999 can run this action.
Prepare transportEnabled only after at least one delivery sheet exists and every attached sheet is finalized.

Removing A Delivery Sheet From Transport​

Deleting here means detaching a route-picking delivery sheet from the transport round. It does not delete the original Shipping Route delivery document itself.

Rules:

ConditionBehavior
User is not the transport owner/editorRemove button is not shown. Existing transport edit is owner-based through userCode.
Transport is not draftRemove button is hidden; handler also blocks with the message that loading/transport already started.
startedAt existsTerminal hides the remove button; Mobile is already blocked because the workflow is no longer draft.
Missing transportId or deliveryIdNo delete request is sent.
User confirms deleteCalls deleteTransportDelivery and refreshes the transport detail.
Delivery sheet was already finalizedIt can still be removed only while the transport is still draft and editable. After prepare/start, it is locked.

Status Model​

StatusMeaningMain allowed actions
NewUnsaved route header.Save or select a delivery sheet, which creates the header first.
DraftTransport exists but loading has not started.Edit header, add/remove delivery sheets, finalize sheets, prepare.
PreparingGoods are being loaded onto the vehicle.Start transport or unlock back to draft by authorized departments.
In progressstartedAt exists.Process customer stops, add shipping rows before payment, retry integration rows, handle oil/bank, finish.
FinishedfinishedAt exists.Read-only operational history.
CanceledCanceled transport.Read-only.

Prepare And Unlock​

ActionRequired conditionsResult
Prepare transportTransport owner or override user code 999999; status is draft; all delivery sheets are finalized; not canceled. Before confirmation, the app checks for eligible, unassigned delivery sheets in the transport route.Calls POST /transports/{id}/prepare, moves workflow to loading/preparing.
Unlock transportTransport is preparing and not canceled; user department is in 8, 12, 22, 28, 29, 4, 2; note is required.Calls /cancel-prepare with note and returns the route to an editable draft-style state.

Unlock Flow​

Unlock is used when a transport has already entered the loading/preparing step, but the team needs to correct the route before starting the trip. It does not cancel the transport; it cancels the prepare/loading state.

Unlock rules:

ConditionBehavior
Transport is not PREPARINGUnlock action is not available.
Transport is canceledUnlock action is not available.
User department is not allowedShows "no unlock permission" error and does not open the note modal.
Note is blankBlocks submit and asks for a note.
Submit succeedsCalls unprepareTransport(transportId, { note }), clears the modal state, refreshes detail, and lets the owner edit the route again.
Submit failsKeeps the route unchanged and shows the API error.

Start Transport​

Starting transport is separate from preparing/loading. It records mileage and creates shipping/COD rows for each customer when enabled.

In PRD, before the starting-mileage validation, the app calls GET https://api-checklistv2.boonsiri.co.th/api/v1/transportation with the logged-in user's userCode, status=false, and the configured x-api-key. An empty data array permits the start flow to continue. A non-empty data array stops the flow and shows: ยังมีงานที่ไม่เสร็จบนระบบ Check List กรุณาไปดำเนินการให้เสร็จสิ้นทั้งหมดก่อน. This check is not called in UAT. If the Checklist request fails or has an invalid response shape, the start flow is stopped and the API error is shown.

Start payload:

FieldSource
transportIdCurrent transport.
licensePlateTransport header.
mileageStarting mileage modal.
userCodeTransport owner/current user.
shippingsOne row per customer with customerCode, customer lat/lng, and shippingAmount. Mobile sends amount only when COD is enabled; POS Terminal currently sends the calculated shipping amount.

If SAP returns row errors, the transport still starts, and the user gets an error list so the failed shipping rows can be retried later.

Customer Stop Workflow​

Customer stops are loaded from the attached delivery sheets through /transports/{id}/customers. The stop list shows payment totals, delivery progress, and special footer actions for oil and bank after the trip has started. A customer card remains the draft light-blue color until its status is FINISHED; only then does its color reflect the recorded payment type.

Card Colors​

CardConditionColor
Customerstatus is not FINISHEDDraft light blue, regardless of payment type.
Customerstatus is FINISHED, paymentType: CASHCash green.
Customerstatus is FINISHED, paymentType: TRANSFERTransfer purple.
Customerstatus is FINISHED, paymentType: BILL_PAYMENTBill-payment yellow.
Customerstatus is FINISHED, paymentType: OTHEROther-payment red/pink.
Customerstatus is FINISHED, paymentType: SHIPPINGShipping purple.
Oil / Bank footer cardRespective flagGas / flagBank is 0Draft light blue.
Oil / Bank footer cardRespective flagGas / flagBank is 1Confirmed green.

Customer Card Amount Formula​

One card represents one delivery stop, and can therefore contain several invoices even for the same customerCode. The client first derives the product amount from all invoices assigned to that stop:

net invoice amount = amount - discount - useCreditNote
reference product amount = sum of net invoice amounts at the stop
shipping/fee amount = sum of shippingAmount across shipping-invoice rows
(or customer.shippingAmount when no shipping invoice exists)
card total = reference product amount + shipping amount

Every shipping-invoice row on the customer card uses its own shippingAmount for the displayed amount. docType: FEE is labelled Fee and docType: SHIPPING is labelled Shipping; both are displayed even when COD is disabled. If an older API response omits shippingAmount, the client falls back to that row's amount.

When the API provides grandAmount, the payable amount and the amount passed to the payment flow use grandAmount + shipping amount instead. grandAmount: 0 is a valid, deliberate total used by the zero-value delivery-confirmation/bill-payment flow; it is not a missing value.

The values at the right of the card mean:

Visible valueCalculation / meaning
Muted amount (when shown)Card total: net invoice amounts plus shipping.
Primary amount for CASH, TRANSFER, BILL_PAYMENT, OTHERActual collected amount, cash + transfer, capped at the card/payable total.
Primary amount for other statesPayable amount: grandAmount when supplied, otherwise the reference product amount, plus shipping.
Amount on an invoice rowinvoice.amount before its discount and useCreditNote, retained as a source-document reference.

Consequently, invoice-row values need not add up to the card total when discounts or credit notes exist. They must not be treated as the collection amount without those deductions.

Data contract: the transport-customer endpoint should supply grandAmount explicitly whenever it is the source-system total, and distinguish an absent value from 0 so a client does not unintentionally interpret a missing product total as zero.

Available customer actions:

ActionBehavior
Call customerLogs the customer call if customer reference exists, then opens the phone dialer.
Confirm delivery / paymentOpens the payment type step. Hidden when limited access or customer is already finished.
Save locationLoads customer addresses and lets the user select or update GPS-related delivery location data.
NavigateOpens navigation using the customer coordinates/address.
View productsBuilds a product summary from invoices assigned to the customer stop.
Add shipping amountCreates an extra transport shipping invoice for that customer. Blocked after the customer is paid/finished.

Refreshing An Unfinished Customer Stop​

When an unfinished customer card is opened, the app shows loading and requests GET /api/transports/{transportId}/customers?customerCode={customerCode} before opening the customer modal. The returned row is matched to the card by all three fields: customerCode, U_BFP_Latitude, and U_BFP_Longitude. This prevents a response for one delivery location from updating another stop that has the same customer code. The matched row's current grandAmount and paymentType update that card and are then used by the existing modal flow.

For a matched row where grandAmount is 0:

Current paymentTypeStep-2 choiceSubmitted paymentType
Empty / nullOnly Confirm deliverySHIPPING
BILL_PAYMENTOnly Confirm deliveryBILL_PAYMENT

All other payment cases retain the existing choices and validation. A zero-value BILL_PAYMENT confirmation is allowed for this specific grandAmount: 0 case.

Customer payment access is normally restricted to the transport owner. The debug user code 999999 may also open an unpaid customer card and complete its payment flow after the transport has started; this exception does not grant permission to edit the transport header, delivery sheets, or route actions. | Generate QR | Generates QR for product amount and, when present, shipping amount. Limited access can still generate QR for transfer-paid stops. |

Shipping Amount Rows​

ActionRule
Auto shipping rows at startBuilt from each customer and sent with startTransport.
Add shipping amount manuallyRequires customer code, positive integer amount, lat/lng, user code, and user name.
Delete shipping rowAllowed only before the customer is paid/finished.
Retry shipping to SAPCalls /shipping/{shippingInvoiceId}/retry for failed B1 posting.
Retry incomingService exists as /retry-incoming; use for incoming-side integration retry where surfaced by UI/API.
Retry cancel to SAPCalls /shipping/{shippingInvoiceId}/retry-cancel for failed cancellation posting.

Payment And Evidence​

Payment submits to /transports/{id}/payment. It updates the customer stop state; the old explicit customer-status update is no longer used by the screen.

Payment types:

TypeInput behavior
CASHCash amount must be positive. The screen auto-fills/locks the cash field for normal cash collection. Requires drop-off fee check.
TRANSFERAfter generating QR, the user must select the transfer-collection outcome described below.
BILL_PAYMENTSame validation pattern as transfer, with bill payment slip.
OTHERRequires notes. Used for partial receive, not receive, split payment, mixed cash/transfer, or exceptional cases. Requires drop-off fee check unless configured off.
SHIPPINGUsed when payable amount is zero and the user only confirms delivery.

QR Transfer Collection Choice​

After choosing TRANSFER / QR Code, the screen requires one of these radio choices before submit:

ChoiceScreen behaviorSubmitted payment data
Customer already transferred to the delivery staffShows an editable transfer-amount input. The amount must be greater than zero and the normal TRANSFER flow is used.paymentType: TRANSFER with the entered transfer amount.
Customer will transfer to Sales laterHides the transfer-amount input and uses the full payable amount as its transfer value.paymentType: OTHER, cash: 0, transfer equal to the full payable amount, and a system note: ลูกค้าจะโอนชำระกับฝ่ายขายเองภายหลัง.

If neither choice is selected, the screen blocks submit and asks the user to choose the transfer payment status. Every transfer choice requires a positive transfer amount.

Required evidence and validations:

RequirementRule
Delivery photoRequired for every payment/delivery confirmation.
Shipping slipRequired when there is a shipping amount and the product transfer/bill slip exists.
SignatureRequired unless payment type is TRANSFER or BILL_PAYMENT.
GPSCurrent device coordinates are captured at submit time. Customer lat/lng is also sent in the payload.
Drop-off feeRequired check for CASH and OTHER; added to cash when fee config is enabled.
Other payment amountCash + transfer cannot exceed payable amount. If not marked partial receive, cash + transfer must cover the payable amount.
Not receiveFor OTHER, not receive clears cash/transfer to zero and still records details.
Sales-later transferThe special QR choice submits OTHER with cash at zero, transfer equal to the full payable amount, and the Sales-later system note.

Evidence Upload​

The delivery photo, transfer/bill-payment slip, shipping slip, and customer signature use the same endpoint:

POST /api/transports/{transportId}/files
Content-Type: multipart/form-data

The request body is multipart/form-data and includes the customer context currently open in the customer modal:

FieldTypeSource / meaning
filesFileThe evidence file being attached; the current UI uploads one file/image at a time.
whGrpCodestringWarehouse group from the transport detail.
customerCodestringcustomerCode of the selected customer stop.
U_BFP_LatitudestringLatitude of the selected customer stop.
U_BFP_LongitudestringLongitude of the selected customer stop.

transportId is in the URL, not the body. The response returns the uploaded file path, which is then placed into the payment payload field that matches the evidence type: photo, slipPath, shippingSlipPath, or signature.

customerCode, U_BFP_Latitude, and U_BFP_Longitude must all exist and be non-empty for every upload. If any is missing, the app shows:

ไม่พบ customerCode หรือพิกัดลูกค้าสำหรับอัปโหลดไฟล์

The upload is stopped and the file API is not called. A missing whGrpCode is also blocked with a missing-warehouse upload alert.

Payment payload fields:

FieldMeaning
customerCodeCustomer stop being processed.
paymentTypeOne of cash, transfer, bill payment, other, shipping confirmation.
amountPayable amount including shipping amount.
cash, transferResolved collection amounts; cash can include drop-off fee.
notesUser notes plus system summary for OTHER.
U_BFP_Latitude, U_BFP_LongitudeCustomer destination coordinates.
currentLatitude, currentLongitudeDevice coordinates at payment time.
signature, slipPath, shippingSlipPath, photoUploaded file paths.

Oil And Bank Flags​

After the trip starts, the stop list adds two special action items: oil and bank.

ActionBehavior
OilUser can open Expense Bill prefilled from the transport or mark "no oil". Either path sets flagGas = 1.
BankUser can open Deposit prefilled from the transport cash total or mark "no bank". Either path sets flagBank = 1. In the current Mobile modal, the deposit button is hidden and only "no bank" is active; POS Terminal still has the context builder for deposit.
Finish dependencyFinish transport is blocked until both flagGas and flagBank are 1.

Finish Transport​

Finish payload contains transportId, licensePlate, and ending mileage.

Permissions And Guards​

TopicMobilePOS Terminal
Menu visibilitygetTransportAccess().canOpenTransportMenu allows deptCode -2, 1, 2, 3, 4, 7, 29, 21, 8, 9, 12, 13, 16, 14, 22, 27, 28, 30.MENU_TRANSPORT uses the same department set.
Create transportMobile create button allows deptCode -2, 1, 2, 3, 4, 7, 29, 21, 8, 9, 13, 16, 14, 30, or full non-main warehouse access.canCreateTransport uses the create dept set plus full non-main warehouse access.
Branch ruleUses selected branch context. Non-main behavior depends on shared warehouse access helpers.Menu config sets requiresMainBranch = true for transport; create helper still supports full non-main access where invoked.
Edit routeExisting transport can be edited only by the transport owner (userCode).
Delete from listDraft transport only, owner only, and create permission/full warehouse access required.
Unlock loadingPreparing transport only; deptCode 8, 12, 22, 28, 29, 4, 2; note required.
Customer action after finishFinished/canceled transport is read-only for operational actions.

Mobile vs POS Terminal​

AreaMobilePOS Terminal
LayoutStack screen with list, detail, modals, and bottom action bar.Split-pane Home screen detail form.
Core APIapps/mobile/src/services/transport.ts.apps/pos-terminal/src/services/transport.ts.
Core behaviorReference behavior for guards and customer workflow.Should mirror Mobile business rules.
Start shipping amountSends shipping amount when COD is enabled; otherwise 0.Sends the calculated shipping amount. Keep this difference intentional or align with Mobile if COD gating is required.
Error displayMobile shows a SAP error modal after start.Terminal shows an alert with formatted failed rows.
Bank modalMobile currently hides the deposit action in the bank modal and allows "no bank".Terminal keeps deposit context and skip flag action.

Developer Handoff Map​

AreaCode
Mobile listapps/mobile/src/screens/transportStack/transportListScreen/index.tsx
Mobile detailapps/mobile/src/screens/transportStack/transportDetailScreen/index.tsx
Mobile customer modalapps/mobile/src/screens/transportStack/transportDetailScreen/TransportCustomerActionModal.tsx
Mobile helpersapps/mobile/src/screens/transportStack/transportDetailScreen/helpers.ts
POS Terminal detailapps/pos-terminal/src/screens/HomeScreen/TransportDetailForm.tsx
POS Terminal helpersapps/pos-terminal/src/screens/HomeScreen/TransportDetailHelpers.ts
Transport servicesapps/mobile/src/services/transport.ts, apps/pos-terminal/src/services/transport.ts
Menu/permission configapps/mobile/src/utils/transport.ts, apps/pos-terminal/src/screens/HomeScreen/config.ts
Shipping Route source docsShipping Route
Related operational docsDeposit, Fuel Bill / Expense Bill, Vehicles