Skip to main content

Mobile And Terminal Architecture

The repository currently supports two React Native apps with overlapping business concerns but different device assumptions.

Roles

  • apps/mobile is the current POS app for handheld operator workflows.
  • apps/pos-terminal is 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:apk and yarn 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 BsrCustomerDisplay detects 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-ui should be checked against both @bsr/mobile and @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 th and en
  • the default language is th
  • i18nResources is 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.ts initializes i18n from @bsr/i18n
  • apps/pos-terminal/src/i18n/index.ts initializes 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.ts remains as a compatibility re-export, but new code should import from @bsr/assets/* directly.
  • apps/mobile and apps/pos-terminal both declare @bsr/assets as 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.logoBSR points to packages/assets/images/logos/logo.png.
  • logos.logoBSRWhiteText points to packages/assets/images/logos/logo-white-text.png.
  • Mobile login and branch screens use logoBSRWhiteText in dark mode and logoBSR in light mode.
  • Mobile QR share capture intentionally keeps using logoBSR so shared QR images stay unchanged.
  • POS terminal UserSelectionScreen uses 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_URI lives in @bsr/printing because it is a print-template asset used by receipt output in both apps
  • mobile keeps compatibility wrapper files under apps/mobile/src/printing/templates where existing imports need to remain stable
  • POS terminal print helpers call shared templates directly from @bsr/printing
  • Metro config in both apps maps @bsr/printing to packages/printing/src so bundling does not depend only on the workspace symlink in node_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 logoStickerUri override
  • POS terminal invoice receipt printing builds QR claim/payment assets and reads branch, tax, and user from local storage before rendering the shared template
  • changes to templates in @bsr/printing affect both apps, so run both yarn workspace @bsr/mobile tsc --noEmit and yarn 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.tsx owns 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.ts owns pure helpers for booking/customer draft creation, booking-price payload conversion, recalculation, id resolution, list search, and shared action definitions
  • ListPane.tsx owns panel 2 rendering, including list header controls, FlashList rendering, booking-price cards, invoice cards, invoice tabs, and swipe actions
  • DetailPane.tsx owns panel 3 rendering for booking price, invoice detail, and generic detail views
  • InvoicePanels.tsx owns invoice-specific list/detail/card/modal UI
  • DepositPanels.tsx owns deposit-specific panel-2 UI such as the retry filter, list card body, and swipe actions
  • DepositDetailForm.tsx owns the reusable deposit create/detail form used by both the deposit menu and transport embedded deposit flow
  • BookingPriceDetailForm.tsx owns the booking-price detail form
  • BookingProductPicker.tsx owns booking-price product picker and add/edit item overlay behavior
  • ShippingRouteDetailForm.tsx owns the POS terminal shipping-route detail/create flow for the ใบจัดสินค้าสายส่ง menu
  • styles.ts remains the aggregate Home style export, while menu-owned styles can be split into focused files such as depositStyles.ts

Refactor direction:

  • keep API calls and stateful screen orchestration close to index.tsx until 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/no to create the delivery header
    • use the returned deliveryId to call POST /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 ListHeaderComponent content 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.tsx into 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.tsx and helper logic in TransportDetailHelpers.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
  • 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 transportId exists
    • 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 flagGas and flagBank
    • 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
    • generateTransportPaymentQrCode in apps/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:
    • DELETED deposits use the canceled background
    • deposits with empty RefKey use 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
  • panel 3 uses DepositDetailForm.tsx as 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 DepositDetailForm as an embedded state in transport panel 3 rather than a nested modal on iPad/iOS
    • transport passes DepositTransportContext with transportId, transportPayload, branch/warehouse/company/date/notes defaults
    • after createDeposit, the form updates transport with the passed payload so flagBank is set, then returns control to the transport flow
  • 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

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 branchId as 0 use the draft background
    • bills with empty RefKey use the error background and expose retry
    • normal bills use the confirmed background
    • the generic status filter is hidden for this menu
  • panel 2 supports swipe actions matching Mobile:
    • retry for bills with empty RefKey when the bill is not a draft branch row
    • delete through DELETE /bills/:billId
  • panel 3 uses ExpenseBillDetailForm.tsx as 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/:billId by 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 ExpenseBillDetailForm as an embedded state in transport panel 3 rather than a nested modal on iPad/iOS
    • transport passes ExpenseBillTransportContext with transportId, transportPayload, branch/warehouse/company/vehicle/date/notes defaults
    • after createExpenseBill, the form updates transport with the passed payload so flagGas is set, then returns control to the transport flow

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/logos and switches between the normal logo and white-text logo by theme mode.
  • User cards show the full image when available and fall back to firstname plus userCode when 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 uses whsGrpCode to 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 /login API.
  • Successful login stores auth_token, serialized user, and login_date in DD-MM-YYYY, then enters the terminal Home route.
  • Safe-area and notch handling use SafeAreaProvider, SafeAreaView, and useSafeAreaInsets.
  • The top-left version label comes from apps/pos-terminal/src/native/appVersion.ts.
  • The top-right settings dropdown contains Light mode, Dark mode, and Branch 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.0 as the latest stable release.
  • 4.25.0-beta.1 exists under the next dist-tag, and nightlies exist, but they are not stable releases.
  • react-native-screens@4.24.0 release notes say it is the last release tested with legacy architecture; both mobile and POS terminal currently have newArchEnabled=true.
  • Kotlin warnings from node_modules/react-native-screens/android/... during Android builds are dependency warnings, not app-source errors. Do not patch node_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, and login_date.
  • If all login session values exist and login_date is 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, and login_date.
  • After login-session cleanup, POS terminal checks AsyncStorage keys branch and tax.
  • If either branch setup value is missing, the app opens BranchScreen immediately as a required setup step.
  • In required setup mode, BranchScreen does not show a close button; it shows a settings gear instead so operators can switch dark/light mode before setup is complete.
  • If both branch and tax exist, the app opens UserSelectionScreen.
  • From UserSelectionScreen, operators can reopen branch setup through the top-right settings menu item ตั้งค่าสาขา.
  • When branch setup is opened from settings, BranchScreen shows a close button because the terminal is already configured.

Branch setup behavior:

  • BranchScreen loads branches from GET {MST_HOST}/api/branches through 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, and whGrpName; the screen normalizes both whGrp* and the older whsGrp* field names.
  • After selecting a branch, the app loads warehouses with:
    • GET {MST_HOST}/api/warehouses?pageLimit=40&page=1
    • GET {MST_HOST}/api/warehouses/{branchCode}/sub
  • After selecting a warehouse, the app fetches tax data before completing setup.
  • Setup only completes after both branch and tax are 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.ts re-exports from @bsr/api so mobile and POS terminal use the same config source.
  • POS terminal uses X_API_KEY_TERMINAL for MST requests and X_API_KEY_TERMINAL_MAIN for 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 listHeaderRow spacing, including marginBottom, should be preserved when extending other menu lists

Shared behavior in Home:

  • panel 2 list loading uses the shared LoadingProvider overlay 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 duplicate createInvoice helpers 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 duplicate updateDeliveryStatus.
  • transfer-in list loading should call getTransferIns() by default, matching mobile. If terminal later needs warehouse tabs such as NF-{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, and sortWarehousesByCode consistently 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 getProductInventoryList and getBookingProductList are UI adapters over the same Firestore inventory data, not separate product business rules.

Known intentional terminal-only helpers:

  • apps/pos-terminal/src/services/common.ts includes branch and sale-person helpers used by terminal branch/user selection, such as getBranches, getBranchSettingsByUserCode, and getSalePersonsByWhsGrpCode.
  • apps/pos-terminal/src/services/products.ts includes split-pane list helpers for product and booking product selection.
  • apps/pos-terminal/src/storage/session.ts includes clearSessionStoragePreservingBranch for 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.ts and apps/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.ts and apps/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, and 100
    • 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 productCode before 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; currentStepPrice is 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/healthz and 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.
  • branchId must be sent as a number for POST /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 weightBaseFlag and stepPriceFlag as booleans.
  • booking item payloads must preserve product company fallback rules: companyCode falls back to company, and companyName falls back through companyName, ComName, company, and comName.
  • POS terminal uses x-api-key for 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.ts wraps NativeModules.AppVersionModule for mobile.
  • apps/pos-terminal/src/native/appVersion.ts wraps NativeModules.AppVersionModule for POS terminal.
  • apps/mobile/src/constants/appVersion.ts and apps/pos-terminal/src/constants/appVersion.ts remain as compatibility re-exports only.
  • apps/mobile/src/services/appUpdate.ts and apps/pos-terminal/src/services/appUpdate.ts own required-update checks against Firestore version/vapp.
  • Both implementations compare native versionCode with the remote versionNumber after 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 UserSelectionScreen shows the version label
  • POS terminal UserSelectionScreen and HomeScreen show 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 AppVersionPackage in MainApplication.
  • iOS must keep the Swift/Objective-C bridge in the Xcode project.
  • Fresh iOS setup should run pod install, and apps/pos-terminal/ios/Podfile.lock should be kept.
  • The existing iOS react-native-view-shot build error involving RCTScrollView is handled by the existing postinstall patch and is not caused by AppVersionModule.