Mobile And Terminal Architecture
The repository currently supports two React Native apps with overlapping business concerns but different device assumptions.
Roles
apps/mobileis the current POS app for handheld operator workflows.apps/pos-terminalis the dedicated terminal app for larger Android devices, dual-screen scenarios, and tablet-oriented layouts.
App responsibilities
Mobile POS
- Primary operator flow for the existing POS implementation
- Android release packaging already exposed at workspace root through
yarn mobile:apkandyarn mobile:aab - Firebase configuration already present in the Android app project
POS terminal
- Separate operator pane and customer-facing pane shell already exists
- Android native bridge
BsrCustomerDisplaydetects and prepares for external display usage - Tablet and iPad-style layouts are part of the design direction
- Release packaging conventions still need explicit project-level documentation before production rollout
Relationship to shared packages
Notes for future documentation
- Add a dedicated page for peripheral integrations when printer, scanner, and payment-device flows are stable.
- Add a dedicated page for secondary-display rendering once the terminal app moves beyond capability detection into actual presentation rendering.
Shared UI boundary
@bsr/rn-ui is the shared boundary for React Native UI infrastructure used by both mobile and POS terminal. It intentionally shares baseline behavior/context, not screen-specific layout.
What belongs in the shared boundary:
- theme provider and hooks:
ThemeProvider,useTheme,useThemeMode - alert provider and hooks:
AlertProvider,useAlert - loading provider and hooks:
LoadingProvider,useLoading - shared theme types and the font contract
What should stay inside each app:
- screen layout and responsive sizing
- screen-specific colors that are part of that screen's art direction
- mock data or API bindings for individual flows
- native module registration for that app
Impact of this boundary:
- mobile keeps the existing
src/contexts/*import paths, but those files re-export from the shared package to reduce existing-screen churn. - POS terminal uses the shared providers directly and has its own AsyncStorage-backed theme storage.
- Behavior changes in
@bsr/rn-uishould be checked against both@bsr/mobileand@bsr/pos-terminal.
Shared i18n boundary
@bsr/i18n is the shared boundary for translation resources used by both mobile and POS terminal.
Current structure:
- shared resources live under
packages/i18n/src/locales - supported languages are currently
thanden - the default language is
th i18nResourcesis exported for React i18next initialization in each app
App responsibilities:
- each app owns its runtime i18next initialization because language detection and provider placement are app bootstrap concerns
apps/mobile/src/i18n/index.tsinitializes i18n from@bsr/i18napps/pos-terminal/src/i18n/index.tsinitializes i18n from@bsr/i18n- old mobile locale files remain as compatibility re-exports, but new translation keys should be added to
packages/i18n
Impact of this boundary:
- translation key changes can affect both apps, so both workspaces should be type-checked before merging
- screen-specific copy can still be namespaced by app or workflow inside the shared resource object when the text should not be reused
- runtime language switching can be added later without moving translation resources again
Shared asset boundary
@bsr/assets is the shared boundary for image assets used by both mobile and POS terminal.
Current structure:
- image files live under
packages/assets/images - app-specific font and animation assets remain inside each app for now
- imports should use group subpaths to avoid pulling every asset group through the package root:
@bsr/assets/logos@bsr/assets/icons@bsr/assets/menus@bsr/assets/utils
Compatibility notes:
apps/mobile/src/constants/images.tsremains as a compatibility re-export, but new code should import from@bsr/assets/*directly.apps/mobileandapps/pos-terminalboth declare@bsr/assetsas a workspace dependency and map@bsr/assets/*in TypeScript config.- Release bundles should only include assets that Metro sees through static
require(...); keeping imports split by group helps avoid unnecessary asset inclusion.
Logo behavior:
logos.logoBSRpoints topackages/assets/images/logos/logo.png.logos.logoBSRWhiteTextpoints topackages/assets/images/logos/logo-white-text.png.- Mobile login and branch screens use
logoBSRWhiteTextin dark mode andlogoBSRin light mode. - Mobile QR share capture intentionally keeps using
logoBSRso shared QR images stay unchanged. - POS terminal
UserSelectionScreenuses the same logo pair for dark/light mode.
Shared printing boundary
@bsr/printing is the shared boundary for HTML print templates used by both mobile and POS terminal.
Current structure:
- pure print helpers and templates live under
packages/printing/src/templates - shared templates currently cover order pack/storage, borrow pack/storage, invoice pack/storage, and full invoice receipt printing
LOGO_STICKER_DATA_URIlives in@bsr/printingbecause it is a print-template asset used by receipt output in both apps- mobile keeps compatibility wrapper files under
apps/mobile/src/printing/templateswhere existing imports need to remain stable - POS terminal print helpers call shared templates directly from
@bsr/printing - Metro config in both apps maps
@bsr/printingtopackages/printing/srcso bundling does not depend only on the workspace symlink innode_modules
Operational behavior:
- full invoice receipt printing should use the shared
buildInvoiceTemplate - invoice receipt options include tax data, branch id, print count, claim QR, payment QR, and an optional
logoStickerUrioverride - POS terminal invoice receipt printing builds QR claim/payment assets and reads
branch,tax, anduserfrom local storage before rendering the shared template - changes to templates in
@bsr/printingaffect both apps, so run bothyarn workspace @bsr/mobile tsc --noEmitandyarn workspace @bsr/pos-terminal typecheck
POS Terminal Home split-pane code organization
The large-screen terminal Home surface is intentionally being split by responsibility as more menu pages are added.
Current structure:
apps/pos-terminal/src/screens/HomeScreen/index.tsxowns the Home route state, effects, handlers, menu selection, and top-level split-pane composition- panel 1 owns the Home menu and the settings entry; the settings dropdown is intentionally rendered outside the menu ScrollView so it can overlay adjacent panes without being clipped
helpers.tsowns pure helpers for booking/customer draft creation, booking-price payload conversion, recalculation, id resolution, list search, and shared action definitionsListPane.tsxowns panel 2 rendering, including list header controls, FlashList rendering, booking-price cards, invoice cards, invoice tabs, and swipe actionsDetailPane.tsxowns panel 3 rendering for booking price, invoice detail, and generic detail viewsInvoicePanels.tsxowns invoice-specific list/detail/card/modal UIDepositPanels.tsxowns deposit-specific panel-2 UI such as the retry filter, list card body, and swipe actionsDepositDetailForm.tsxowns the reusable deposit create/detail form used by both the deposit menu and transport embedded deposit flowBookingPriceDetailForm.tsxowns the booking-price detail formBookingProductPicker.tsxowns booking-price product picker and add/edit item overlay behaviorShippingRouteDetailForm.tsxowns the POS terminal shipping-route detail/create flow for theใบจัดสินค้าสายส่งmenustyles.tsremains the aggregate Home style export, while menu-owned styles can be split into focused files such asdepositStyles.ts
Refactor direction:
- keep API calls and stateful screen orchestration close to
index.tsxuntil each menu flow has a stable boundary - keep future extractions focused on stable responsibilities such as per-menu handlers or service orchestration after the menu flow is proven
- avoid changing behavior during extraction; each refactor pass should type-check both POS Terminal and Mobile when shared package imports or Metro aliases are involved
- the next likely extraction is
ShippingRouteDetailForm.tsx, which should be split into header/list, invoice actions, product/Lot-Batch editor, sort planner, and print/action sections before more shipping behavior is added
POS Terminal shipping-route parity notes
The POS terminal ใบจัดสินค้าสายส่ง menu is intended to mirror Mobile shippingListScreen and shippingDetailScreen while fitting the three-panel Home workspace.
Implemented behavior:
- panel 2 renders the shipping-route list with mobile-aligned card colors, search, status selection filter, FlashList pagination, swipe delete, and permission-gated create/delete behavior
- creating a shipping-route document follows the Mobile flow:
- select route
- preload available invoices into local state
- call
POST /api/deliveries/noto create the delivery header - use the returned
deliveryIdto callPOST /api/deliveries/{deliveryId}/invoices - refresh panel 2 and load panel 3 from the created
deliveryId
- adding invoices in create mode only updates local state until the delivery header exists; adding invoices in detail mode posts directly to the existing
deliveryId - panel 3 renders shipping detail/create as a FlashList-backed screen; the route/meta header and section header are
ListHeaderComponentcontent for both invoice and product tabs to avoid nested outer scroll views - panel 3 includes mobile-style actions for accept, dock confirmation, loaded confirmation, co-worker assignment, add invoice, delete invoice, cancel invoice, sort customer order, send B1, and save product location/Lot data
- document-level shipping actions (
รับงาน,จัดสินค้าที่ลาน,ขึ้นรถแล้ว,ร่วมจัด) live in the panel-3 header three-dot dropdown, while product location saving lives in the product-tab FlashList footer so it scrolls with the content - POS terminal opens an automatic accept-work Sweet Alert when Mobile's accept condition is met: finished delivery, assigned to transportation, missing
acceptedBy, and an accepting user department - the product summary tab groups invoice products, supports Lot/Batch selection, auto-selects a single sufficient lot, validates duplicate/over-quantity lots, and posts location data before updating the delivery status
- visible shipping print UI matches current Mobile: swipe-print a single invoice row; invoice-batch and product-summary print helpers remain available but their UI entry points stay hidden while Mobile hides print-all and delivery-print actions
- dark mode readability has been adjusted by using terminal theme colors for shipping detail headers, cards, modals, batch editor, and sort planner content
Known follow-up work:
- test the full shipping-route flow on real iPad/tablet hardware, especially swipe actions, modal behavior, FlashList headers, keyboard behavior, and dark mode
- test API behavior with real data for create, add/delete invoice, cancel invoice, accept/dock/loaded/assign, sort order, send B1, and Lot/Batch save
- test physical printer output for the visible invoice row print action
- refactor
ShippingRouteDetailForm.tsxinto smaller components once the UI and behavior are accepted
POS Terminal transport parity notes
The POS terminal ขนส่ง menu is intended to mirror Mobile transportListScreen and transportDetailScreen while fitting the three-panel Home workspace.
Implemented behavior:
- panel 2 renders the transport list with mobile-aligned card colors, displayed data, search, FlashList pagination, pull-to-refresh, swipe delete, and permission-gated create/delete behavior
- panel 2 card colors follow Mobile transport state:
- finished transports use the confirmed background color
- started transports use the new/in-progress background color
- draft transports use the draft background color
- panel 3 is owned by
TransportDetailForm.tsxand helper logic inTransportDetailHelpers.ts - panel 3 uses two FlashList-backed steps and avoids nested outer scroll views:
- step 1 lists delivery sheets attached to the transport, with transport meta in
ListHeaderComponent - step 2 lists delivery customers, with collection/payment meta in
ListHeaderComponent
- step 1 lists delivery sheets attached to the transport, with transport meta in
- panel 3 bottom actions such as save, start, finish, and finalize-all are rendered as FlashList footer content instead of an absolute bottom bar so they scroll naturally with each step
- transport create/detail behavior supports:
- create/update transport header
- select vehicle/license plate with Mobile-style vehicle cards showing status, vehicle type, and max payload; vehicle search is remote but intentionally non-paginated to match Mobile
- add delivery sheets to a transport
- create the transport header first when adding a delivery before
transportIdexists - keep the add-delivery modal open after a successful row add, show row-level loading, prevent duplicate row presses, and remove the added row from the modal list
- remove delivery sheets from a transport before the transport starts
- finalize a delivery sheet one-by-one
- finalize all pending delivery sheets from the bottom action
- load customer groups for the delivery step
- manually sort delivery customer order in the step 2 sort planner
- optimize delivery customer order with Google Routes API using current GPS location and customer coordinates, then save through
sortTransportCustomers - start and finish transport with mileage validation
- customer actions for call, navigation, GPS save, payment type selection, cash/transfer/other payment, photo/slip upload, signature capture, QR generation, and submit payment
- oil/deposit skip actions that update
flagGasandflagBank - print all invoices and print product summary after all delivery sheets are finalized
- added POS terminal transport service boundaries:
apps/pos-terminal/src/services/transport.ts- vehicle and mileage helpers in
apps/pos-terminal/src/services/common.ts - customer call/profile/GPS helpers in
apps/pos-terminal/src/services/customers.ts - Google route optimization helper in
apps/pos-terminal/src/services/googleRoutes.ts generateTransportPaymentQrCodeinapps/pos-terminal/src/services/qr.ts
Known follow-up work:
- test the full transport flow on real iPad/tablet hardware, especially FlashList headers, modal overlays, camera/signature, GPS permission, swipe actions, and dark mode
- test API behavior with real data for create/update transport, add/remove delivery, finalize delivery, load customer groups, manual/Google route sorting, payment, GPS save, start/finish, and print actions
- test physical printer output for transport invoice batch and product summary
POS Terminal deposit parity notes
The POS terminal ฝากเงิน menu follows Mobile depositListScreen and depositDetailScreen, with the detail form intentionally built as a reusable component for both the normal deposit menu and the transport deposit flow.
Implemented behavior:
- panel 2 renders the deposit list with Mobile card colors, displayed data, search placeholder, FlashList pagination, pull-to-refresh, and retry-only filter:
DELETEDdeposits use the canceled background- deposits with empty
RefKeyuse the error background - normal deposits use the confirmed background
- the generic status filter is hidden for this menu so only the Mobile-style
ทั้งหมด/รอส่งใหม่retry filter remains
- panel 2 supports swipe actions matching Mobile:
- retry for deposits with empty
RefKey, with owner/override validation in the handler - delete by the Mobile deposit delete department-code rule
- retry for deposits with empty
- panel 3 uses
DepositDetailForm.tsxas a reusable create/detail form:- create mode loads branch/user context, companies, cash accounts, and bank accounts
- detail mode loads persisted deposit data and renders the form read-only
- company selection reloads cash/bank account options
- invoice selection is available only in create mode outside transport and hidden for department codes 14 and 30
- invoice selection uses FlashList with search and load-more pagination; the same pagination behavior was added to Mobile
depositDetailScreen - selecting invoices makes amount and notes system-derived/read-only
- send-slip-later is available only in create mode outside transport and shifts the deposit date to yesterday
- save validates company, cash account, bank account, and either amount/notes or selected invoices, then confirms before
createDeposit - after a normal deposit create, panel 2 refreshes and panel 3 closes without selecting the created deposit, avoiding an unnecessary
GET /deposits/:id
- transport integration design:
- transport opens the same
DepositDetailFormas an embedded state in transport panel 3 rather than a nested modal on iPad/iOS - transport passes
DepositTransportContextwithtransportId,transportPayload, branch/warehouse/company/date/notes defaults - after
createDeposit, the form updates transport with the passed payload soflagBankis set, then returns control to the transport flow
- transport opens the same
- shared account option behavior:
- Mobile and POS Terminal both dedupe cash-account options by
acctCode, keeping the first item when the API returns the same account code for multiple companies
- Mobile and POS Terminal both dedupe cash-account options by
Known follow-up work:
- test create/detail deposit behavior on iPad/tablet, including account selection modals, invoice selection, date picker, keyboard avoidance, and transport handoff
- test normal deposit create, invoice-based deposit create, transport deposit handoff, retry, and delete with real API data
POS Terminal expense-bill parity notes
The POS terminal บิลน้ำมัน menu follows Mobile expenseBillListScreen and expenseBillDetailScreen, with the detail form built as the reusable oil/expense-bill component for both the normal menu and the transport oil flow.
Implemented behavior:
- panel 2 renders the expense-bill list with Mobile card colors, displayed data, search placeholder, FlashList pagination, pull-to-refresh, and permission-gated create behavior:
- draft bills with
branchIdas0use the draft background - bills with empty
RefKeyuse the error background and expose retry - normal bills use the confirmed background
- the generic status filter is hidden for this menu
- draft bills with
- panel 2 supports swipe actions matching Mobile:
- retry for bills with empty
RefKeywhen the bill is not a draft branch row - delete through
DELETE /bills/:billId
- retry for bills with empty
- panel 3 uses
ExpenseBillDetailForm.tsxas a reusable create/detail form:- create mode loads branch/user context, companies, gas partners, vehicles, and mileage
- detail mode loads persisted bill data from
GET /bills/:billId - draft branch rows can be updated through
PATCH /bills/:billIdby accounting users - company selection can reload mileage for the selected vehicle
- vehicle selection loads old mileage and balance range
- send-slip-later is available only in create mode outside transport and shifts the bill date to yesterday
- save validates company, vehicle, dealer, amount, and notes before create/update
- transport integration design:
- transport opens
ExpenseBillDetailFormas an embedded state in transport panel 3 rather than a nested modal on iPad/iOS - transport passes
ExpenseBillTransportContextwithtransportId,transportPayload, branch/warehouse/company/vehicle/date/notes defaults - after
createExpenseBill, the form updates transport with the passed payload soflagGasis set, then returns control to the transport flow
- transport opens
Known follow-up work:
- test expense-bill list/detail behavior on iPad/tablet, including selection modals, date picker, keyboard avoidance, mileage loading, retry/delete, and transport handoff with real API data
POS Terminal user selection
The current POS terminal first screen is UserSelectionScreen at apps/pos-terminal/src/screens/UserSelectionScreen/index.tsx.
Behavior to remember:
- The screen uses a gradient background, BOONSIRI logo, and horizontal FlashList for user selection.
- The BOONSIRI brand image comes from
@bsr/assets/logosand switches between the normal logo and white-text logo by theme mode. - User cards show the full
imagewhen available and fall back tofirstnameplususerCodewhen no image is available. - Fallback user-card typography uses the shared app font contract; the card name is medium-weight white text, and the user code remains at the standard card size.
- The user list is loaded from
GET {POS_HOST}/api/users/sale-persons?whsGrpCode={storedBranch.whsGrpCode}. - That sale-person request sends
x-api-key: X_API_KEY_TERMINAL_MAIN. - The screen reads the selected branch/warehouse from AsyncStorage key
branch, logs the stored value, and useswhsGrpCodeto load users. - Pull-to-refresh reloads the app version, rereads the stored branch, and reloads the sale-person list.
- If the user count is small, the horizontal user list is padded so the visible cards stay centered; when the list is wider than the viewport, it keeps normal horizontal scrolling.
- Selecting a user opens the login modal with password input, loading state, and shared alert feedback.
- The alternate employee-code login modal lets an operator enter employee code and password without selecting a user card first.
- Both login entry points call the POS
/loginAPI. - Successful login stores
auth_token, serializeduser, andlogin_dateinDD-MM-YYYY, then enters the terminal Home route. - Safe-area and notch handling use
SafeAreaProvider,SafeAreaView, anduseSafeAreaInsets. - The top-left version label comes from
apps/pos-terminal/src/native/appVersion.ts. - The top-right settings dropdown contains
Light mode,Dark mode, andBranch settings.
Native dependency note
The current stable react-native-screens version in the workspace is 4.24.0.
Operational notes:
- As of 2026-05-03, npm marks
4.24.0as the latest stable release. 4.25.0-beta.1exists under thenextdist-tag, and nightlies exist, but they are not stable releases.react-native-screens@4.24.0release notes say it is the last release tested with legacy architecture; both mobile and POS terminal currently havenewArchEnabled=true.- Kotlin warnings from
node_modules/react-native-screens/android/...during Android builds are dependency warnings, not app-source errors. Do not patchnode_modules; wait for a stable upgrade unless a runtime/navigation bug appears.
POS Terminal branch and startup flow
POS terminal startup is controlled in apps/pos-terminal/App.tsx.
Startup behavior:
- On app launch, POS terminal first checks
auth_token,user, andlogin_date. - If all login session values exist and
login_dateis the current day, the app opens Home. - If the login session is missing or stale, POS terminal clears only the login session keys:
auth_token,user, andlogin_date. - After login-session cleanup, POS terminal checks AsyncStorage keys
branchandtax. - If either branch setup value is missing, the app opens
BranchScreenimmediately as a required setup step. - In required setup mode,
BranchScreendoes not show a close button; it shows a settings gear instead so operators can switch dark/light mode before setup is complete. - If both
branchandtaxexist, the app opensUserSelectionScreen. - From
UserSelectionScreen, operators can reopen branch setup through the top-right settings menu itemตั้งค่าสาขา. - When branch setup is opened from settings,
BranchScreenshows a close button because the terminal is already configured.
Branch setup behavior:
BranchScreenloads branches fromGET {MST_HOST}/api/branchesthrough the terminal MST client.- The terminal MST client sends
x-api-key: X_API_KEY_TERMINAL. - The branch API currently returns branch fields such as
branchId,whGrpCode, andwhGrpName; the screen normalizes bothwhGrp*and the olderwhsGrp*field names. - After selecting a branch, the app loads warehouses with:
GET {MST_HOST}/api/warehouses?pageLimit=40&page=1GET {MST_HOST}/api/warehouses/{branchCode}/sub
- After selecting a warehouse, the app fetches tax data before completing setup.
- Setup only completes after both
branchandtaxare saved.
Stored local data:
// AsyncStorage key: branch
{
BranchId: string | number;
BranchCode: string;
whsGrpName: string;
whsGrpCode: string;
isMain: boolean;
}
// AsyncStorage key: tax
JSON.stringify(taxData ?? []);
// AsyncStorage key: user
JSON.stringify(authenticatedUser);
// AsyncStorage key: auth_token
token;
// AsyncStorage key: login_date
DD - MM - YYYY;
API config note:
- Shared API constants now live in
@bsr/api. apps/mobile/src/constants/config.tsre-exports from@bsr/apiso mobile and POS terminal use the same config source.- POS terminal uses
X_API_KEY_TERMINALfor MST requests andX_API_KEY_TERMINAL_MAINfor the sale-person POS request.
POS Terminal Home split-pane workspace
The POS terminal Home screen is a large-screen workspace rather than the mobile stack sequence.
Panel responsibilities:
- panel 1 is the permission-gated menu
- panel 2 is the selected menu list
- panel 3 is the selected detail or draft-document form
Visibility rules:
- if no menu is selected, panel 2 and panel 3 are hidden
- if a menu is selected but no list item or draft document is selected, panel 3 is hidden
- selecting a menu clears the previous list/detail state unless an unsaved-change guard blocks navigation
- selecting a list item opens panel 3
Responsive behavior:
- landscape and wide screens use the three-pane layout
- narrower landscape screens can collapse the menu pane into an icon-only rail
- compact layouts keep the same information hierarchy but wrap panes as needed
- terminal work should prefer maintaining the split-pane model instead of reintroducing mobile stack navigation
Panel 2 header convention:
- the list header row is the shared header direction for future POS terminal menu pages
- the small fixed create button sits in the same row as the list title
- the title is vertically centered with the create button
- the manually tuned
listHeaderRowspacing, includingmarginBottom, should be preserved when extending other menu lists
Shared behavior in Home:
- panel 2 list loading uses the shared
LoadingProvideroverlay instead of an inline loading card - destructive or unsaved transitions use the shared alert provider
- the unsaved-change alert supports cancel, discard, and save-and-exit actions
- panel 3 adjusts around the keyboard and hides the top settings area while typing
- panel 3 should not own a generic outer
ScrollView; each detail surface should own its own scroll/list container so FlashList-heavy details do not become nested-scroll surfaces
Mobile and terminal parity notes
Mobile is the behavior source of truth for shared POS workflows. When a shared workflow differs, first check whether POS terminal is only adapting the same behavior to the split-pane workspace. If not, align terminal back to the mobile service boundary and flow.
Service boundary rules:
- invoice creation belongs in
apps/*/src/services/invoices.ts. Do not add or keep duplicatecreateInvoicehelpers in order services. - delivery status updates belong in
apps/*/src/services/deliveries.ts. Transport screens may call that delivery service, but transport services should not duplicateupdateDeliveryStatus. - transfer-in list loading should call
getTransferIns()by default, matching mobile. If terminal later needs warehouse tabs such asNF-{whsGrpCode}, expose that as visible UI state instead of silently merging warehouses in a terminal-only helper. - shared warehouse exclusion rules should use
shouldExcludeWarehouseCode,filterWarehousesByCode, andsortWarehousesByCodeconsistently in both apps. - product list helpers are the main intentional service difference today: mobile product screens own subscription/list state directly, while POS terminal needs one-shot list helpers for split-pane panel 2. Terminal-only
getProductInventoryListandgetBookingProductListare UI adapters over the same Firestore inventory data, not separate product business rules.
Known intentional terminal-only helpers:
apps/pos-terminal/src/services/common.tsincludes branch and sale-person helpers used by terminal branch/user selection, such asgetBranches,getBranchSettingsByUserCode, andgetSalePersonsByWhsGrpCode.apps/pos-terminal/src/services/products.tsincludes split-pane list helpers for product and booking product selection.apps/pos-terminal/src/storage/session.tsincludesclearSessionStoragePreservingBranchfor terminal session behavior.
When adding or changing a mobile workflow, check the matching terminal entry points:
- mobile order detail:
apps/mobile/src/screens/orderStack/orderDetailScreen/index.tsx; terminal order detail:apps/pos-terminal/src/screens/HomeScreen/OrderDetailForm.tsx - mobile transport detail:
apps/mobile/src/screens/transportStack/transportDetailScreen/index.tsx; terminal transport detail:apps/pos-terminal/src/screens/HomeScreen/TransportDetailForm.tsx - mobile shipping detail:
apps/mobile/src/screens/shippingStack/shippingDetailScreen/index.tsx; terminal shipping detail:apps/pos-terminal/src/screens/HomeScreen/ShippingRouteDetailForm.tsx - mobile transfer-in list/detail:
apps/mobile/src/screens/transferInStack; terminal transfer-in list/detail:apps/pos-terminal/src/services/splitMenuData.tsandapps/pos-terminal/src/screens/HomeScreen/TransferInDetailForm.tsx - mobile product list/detail:
apps/mobile/src/screens/productStack; terminal product list/detail:apps/pos-terminal/src/services/splitMenuData.tsandapps/pos-terminal/src/screens/HomeScreen/ProductDetailForm.tsx
POS Terminal booking price parity notes
The booking price flow in POS terminal should stay close to mobile behavior while using the split-pane surface.
Important parity rules:
- booking-price list cards use background color to express status, not a visible status badge
- booking-price cards show booking number, customer name, creator, and amount like mobile
- booking-price creation starts by selecting a customer
- customer selection happens in a modal with list, filters, route selection, detail view, actions, swipe support, and pagination-oriented loading behavior
- panel 3 uses
รายละเอียดใบจองราคาas the detail header text with no space betweenรายละเอียดandใบจองราคา - the booking detail title uses the medium font family
- booking-price metadata, remark, and product-list title are rendered as the FlashList header for the product item list instead of being wrapped by an outer panel ScrollView
- booking item rows follow the mobile order-item style:
- press row to edit
- swipe row to delete
- show step-price and weight icons
- show quantity, amount, and weight-based formula text
- booking item add/edit runs inside the product picker modal host on iOS instead of opening a nested native modal
- product picker is a full-screen modal host:
- left panel contains product search, filters, and the product list
- add/edit item form is an overlay inside the same modal
- add mode uses the product name as the overlay title and does not show a price field
- edit mode keeps the mobile-style edit header and price field
- selecting an existing item opens edit mode
- selecting a new product opens add mode
- quick quantities are
1,5,10,50, and100 - step price must be fetched from the real MST API through the terminal MST client
- step-price row labels must follow mobile wording with unit text, for example
ไม่เกิน X {UOM}andเกิน X ไม่เกิน Y {UOM} - active step-price rows use accent border and translucent accent background like mobile
- editing a booking item refreshes inventory from Firestore by
productCodebefore showing stock and flags - available stock in the edit overlay comes from refreshed inventory
onHand - booking - editing a step-price item loads step price from the MST API;
currentStepPriceis kept as saved JSON/fallback data, not as the primary price source - existing booking prices are editable only on the document created day, matching mobile. POS terminal resolves the current business date from
GET /api/healthzand falls back to the device date if the server date is unavailable. - when an existing booking price is outside its editable created day, panel 3 must disable adding items, editing items, deleting items, editing remark/header fields, and saving.
API notes:
- booking price create/update is handled by the terminal booking-price service.
branchIdmust be sent as a number forPOST /api/booking-price.- adding an item follows the mobile persistence order: ensure/create booking header, post item, update totals, reload detail.
- editing an existing item immediately patches
PATCH /api/booking-price/{bookingPriceId}/items/{itemId}, updates totals, and reloads detail. - booking item payloads must send
weightBaseFlagandstepPriceFlagas booleans. - booking item payloads must preserve product company fallback rules:
companyCodefalls back tocompany, andcompanyNamefalls back throughcompanyName,ComName,company, andcomName. - POS terminal uses
x-api-keyfor MST APIs; do not add auth token handling to the terminal MST client.
Native app version and app-update boundaries
Both apps read native app version/build data through their own AppVersionModule, but JavaScript access is normalized under src/native/appVersion.ts.
Current structure:
apps/mobile/src/native/appVersion.tswrapsNativeModules.AppVersionModulefor mobile.apps/pos-terminal/src/native/appVersion.tswrapsNativeModules.AppVersionModulefor POS terminal.apps/mobile/src/constants/appVersion.tsandapps/pos-terminal/src/constants/appVersion.tsremain as compatibility re-exports only.apps/mobile/src/services/appUpdate.tsandapps/pos-terminal/src/services/appUpdate.tsown required-update checks against Firestoreversion/vapp.- Both implementations compare native
versionCodewith the remoteversionNumberafter numeric conversion and support platform-specific Android/iOS fields.
Mobile UI usage:
- mobile Login shows the version label and required-update modal
- mobile Home header shows the version label and required-update modal
POS terminal UI usage:
- POS terminal
UserSelectionScreenshows the version label - POS terminal
UserSelectionScreenandHomeScreenshow the same blocking required-update modal when Firestore requires a newer app version - POS terminal Home reruns the check when the screen regains focus
Operational notes:
- Android must register
AppVersionPackageinMainApplication. - iOS must keep the Swift/Objective-C bridge in the Xcode project.
- Fresh iOS setup should run
pod install, andapps/pos-terminal/ios/Podfile.lockshould be kept. - The existing iOS
react-native-view-shotbuild error involvingRCTScrollViewis handled by the existing postinstall patch and is not caused byAppVersionModule.