Skip to main content

Invoice

The Invoice menu is used to track invoices created from the sales order flow, inspect customer/product/status details, send invoice data to B1, manage invoice cancellation requests, reopen a sales order from an existing invoice, and view today's sales summary for the current user and the whole branch.

In this system, invoices are not the main entry point for creating sales. The primary sales entry point is Sales Order. The invoice menu is the review, B1 handoff, cancellation, and summary surface after the order/invoice/payment flow has started.

Open workflowOpen sales summary notesOpen permissions

App Entry Points​

AppEntry pointMain files
MobileHome -> InvoiceList -> InvoiceDetailinvoiceListScreen, invoiceDetailScreen
POS Terminalinvoice menu in split homeHomeScreen/index.tsx, ListPane.tsx, InvoicePanels.tsx

Both apps use the same service boundary:

JobEndpoint / service
Load invoice listGET /invoices via getInvoices(...)
Load pending B1 listGET /invoices?toB1=0 via getInvoicesPendingB1(...)
Load invoice detailGET /invoices/{invoiceId}?includeLot=true
Send to B1POST /payment/to-b1 with an array of invoiceId values
Retry payment/incoming sendPOST /payment/{invoiceId}/retry
Sales summaryGET /invoices/summary
Approve cancel requestPOST /invoices/{invoiceId}/approve
Reject cancel requestPOST /invoices/{invoiceId}/reject
Request invoice cancellationPOST /invoices/{invoiceId}/cancel
Direct invoice cancellationPOST /invoices/{invoiceId}/cancel-direct, payload { notes: string }

Main Workflow​

Invoice List​

The main list loads through getInvoices(...) with the selected branch context:

  • branchId from selected branch
  • whGrpCode from selected warehouse group
  • page, pageLimit, search
  • base filters: shippingType, invoiceStatus, toB1, inStock, withOutCN, dateFrom, dateTo
  • sorted DESC by invoiceId

List behavior:

  • search covers invoice number, customer, or sales order number
  • tabs are All and Pending B1
  • rows are deduped by invoiceId or invoiceStorageId
  • tapping a row in normal mode opens invoice detail
  • tapping a row in manual payment mode selects or deselects the invoice for sending

Invoice Detail​

Detail loads GET /invoices/{invoiceId}?includeLot=true and renders:

  • invoice number, customer, sales order number, invoice owner, and invoice date
  • invoice summary through InvoiceSummarySection
  • product item lines and item count
  • shipping invoices attached to the order, with retry buttons for shipping/incoming when retry is available
  • Open sales order from invoice when the user can create sales orders

Opening a sales order from an invoice passes these values into order detail:

{
duplicateInvoiceId,
duplicateInvoiceNo,
duplicateDocumentMove,
duplicateVehicleRelease,
}

Main branches can open the duplicated order draft directly. Non-main branches must first choose vehicleRelease and documentMove in the duplicate configuration modal.

Send To B1​

B1 sending can run from a per-row action or from multi-select manual payment mode. The API call is POST /payment/to-b1.

Before sending, the app checks:

GuardDetail
Has invoiceIdUses invoiceId or fallback invoiceStorageId
Has refKeyMissing refKey blocks sending
toB1 = 0Already-sent invoices show a disabled action
orderStatus = CONFIRMEDUnpaid/unconfirmed sales orders show an error
No pending cancellation requestcanceledNote without rejectCanceledAt blocks sending
Invoice dateNormal users can send only today's/yesterday's invoices; dept 3 bypasses the date guard

Sales Summary Button​

The Sales Summary button is available in the invoice header:

AppButton location
Mobileheader menu item named สรุปยอดขาย
POS Terminalbar-chart-2 icon beside the Invoice menu header in ListPane

When pressed, the app calls /invoices/summary twice for the current day:

The modal groups BFP and NFF and then renders a total block:

ValueCalculation
Invoice countinvCount
Sales amountamountTotal
point/coupon discountdiscount
credit note discountuseCreditNote
net remainingamountTotal - discount - useCreditNote
total weightweightTotal
COD transaction countcodCount
COD fee totalcodTotal

For each company and for the grand total, the modal renders the COD metrics as ค่าธรรมเนียม COD : codCount @ codTotal. These are additional summary values; the net remaining calculation remains amountTotal - discount - useCreditNote and does not subtract codTotal.

Manual Payment Status Mode​

This mode opens from the same header menu area as the sales summary:

  • Mobile label: ปรับสถานะการชำระเงินด้วยตนเอง
  • Terminal icon: check-square; once active, it changes to x for cancelling the mode
  • the list switches to the Pending B1 tab
  • normal users are limited to today/yesterday and their own userCode
  • dept 3 sees every user and bypasses the manual-mode date guard
  • users select multiple invoices and press ส่งข้อมูล
  • selected invoices must not be canceled, must have refKey, and their order must be CONFIRMED

Cancellation Requests​

Mobile and Terminal use the same cancellation rules in the normal invoice list. Swipe the invoice card to reveal the available action. The list supports both pending-request approval and direct cancellation without a request.

Pending requests​

Requests originate from Storefront Picking or Shipping Route, where staff enter a reason and send POST /invoices/{invoiceId}/cancel with { notes }.

A request is pending when canceledNote has a value and rejectCanceledAt is empty. The invoice owner (matching userCode) or override users 012265 and 999999 can open the approval modal. Approve calls /approve; Reject calls /reject. Both refresh the invoice list. CANCELED invoices cannot be canceled again.

Pending requests take priority over direct cancellation. Login isAdmin = 1 alone does not grant permission to manage an existing request.

Direct cancellation without a request​

All of these conditions must hold:

  • There is no pending cancellation request.
  • The API explicitly returns hasDelivery: null. Missing values, 0, and 1 do not qualify.
  • The invoice is not CANCELED.
  • The current user owns the invoice or has isAdmin = 1 in the successful login data. The flag also accepts the string "1"; override user codes alone do not grant this permission.

The action opens ยกเลิกใบแจ้งหนี้โดยไม่มีคำขอ, showing the invoice number, a multiline หมายเหตุ field, ปิด (Close), and ตกลง (OK).

  1. Enter a note and press OK. Leading and trailing whitespace is removed.
  2. Empty or whitespace-only notes show กรุณาระบุหมายเหตุการยกเลิก in the global Sweet Alert and do not call the API.
  3. A valid note is sent as follows (the field name is notes):
POST /api/invoices/{invoiceId}/cancel-direct
Content-Type: application/json
{
"notes": "Customer requested cancellation"
}

While sending, duplicate submissions, editing, and dismissal are blocked. Success closes the modal and refreshes the current invoice list. API errors show the returned message in the global Sweet Alert; the fallback is ไม่สามารถยกเลิกใบแจ้งหนี้ได้. The modal retains the note for correction or retry. Close dismisses the form without submitting; reopening starts with an empty note.

Swipe action visibility​

The following table applies in normal list mode, not manual payment mode:

ConditionSwipe action
Eligible pending request and userActive cancellation button opens approval/rejection
Eligible direct cancellation and userActive cancellation button opens the note modal
Cancellation unavailable and hasDelivery !== nullInactive ยังไม่มีคำขอยกเลิก action keeps the card swipeable for debugging
Cancellation unavailable and hasDelivery === nullCancellation action hidden

The inactive label is a fallback for any failed cancellation condition, including permissions or a canceled invoice. It does not prove that a request has never existed. An omitted hasDelivery also follows this inactive-action branch, because it is not explicit null.

Permissions And Guards​

AreaCondition
See Invoice menuMENU_INVOICE.has(deptCode) or admin/full non-main warehouse access
menu dept codes-2, 1, 2, 3, 4, 7, 8, 9, 13, 16, 20, 21, 22, 23, 27, 29
Send one invoice to B1invoice owner, dept 3, or override user 012265, 999999
Send older than today/yesterdaydept 3 only
Manage cancel requestinvoice owner or override user 012265, 999999
Cancel without a requestinvoice owner or login isAdmin = 1; no pending request, explicit hasDelivery = null, not CANCELED
Open sales order from invoicemust pass canCreateSalesOrder
main canCreateSalesOrder dept codesadmin, full non-main warehouse access, or dept -2, 1, 2, 4, 7, 8, 9, 13, 16, 21, 29

Important: “can see the menu” and “can perform an action” are separate gates. For example, dept 20 can see the Invoice menu and some Home shortcuts, but Open sales order from invoice still uses the detail-screen canCreateSalesOrder gate.

Mobile vs POS Terminal​

TopicMobilePOS Terminal
UI shapestack screen: list -> detailsplit pane: menu, list, detail
sales summary buttonheader menu itemicon beside invoice menu title
invoice detailInvoiceDetailScreenInvoicePanels detail panel
manual payment modebottom rail with ยกเลิก / ส่งข้อมูลheader action plus list-pane actions
duplicate ordernavigate to OrderDetailcreate/open order detail in split panel with progress
summary modalInvoiceSummaryModal componentInvoiceSummaryModal in InvoicePanels.tsx

Developer Handoff Map​

JobMobilePOS Terminal
invoice listapps/mobile/src/screens/invoiceStack/invoiceListScreen/index.tsxapps/pos-terminal/src/screens/HomeScreen/index.tsx, ListPane.tsx
invoice detailapps/mobile/src/screens/invoiceStack/invoiceDetailScreen/index.tsxapps/pos-terminal/src/screens/HomeScreen/InvoicePanels.tsx
invoice serviceapps/mobile/src/services/invoices.tsapps/pos-terminal/src/services/invoices.ts, splitMenuData.ts
summary modalapps/mobile/src/components/InvoiceSummaryModal/index.tsxInvoicePanels.tsx
Home menu permissionapps/mobile/src/screens/homeScreen/index.tsxapps/pos-terminal/src/screens/HomeScreen/config.ts
open order from invoice permissionapps/mobile/src/utils/salesOrders.tscanCreateSalesOrder(...) in Terminal config

Shared cancellation rules: packages/utils/src/invoiceCancellation.ts. Shared direct-cancellation modal and global alert integration: packages/rn-ui/src/InvoiceDirectCancelModal.tsx. Contract and modal tests: apps/mobile/__tests__/invoiceCancelDirectService.test.ts, invoiceCancellation.test.ts, and invoiceDirectCancelModal.test.tsx.