Skip to content

Pre-production verification of native Sale for Cashier core paths (discounts, multiple payments, print formats, concurrency) #22475

Description

@buddhika75

Part of master issue #22442 (pharmacy retail sale entity → native-SQL migration).
Follows #20261 (native Sale for Cashier, PR #22461, merged).
Blocks Phase 3 (production testing gate), which in turn blocks #22444.

Why this issue exists

The native Sale for Cashier page shipped with its happy path verified end-to-end: single bill, zero Payment rows at pharmacy time, exact stock deduction, BFD/BIFD written, token linked, and settle through the downstream cashier path.

Four core cashier functions were never executed even once — not in E2E, not manually:

  1. Discounts
  2. Multiple payment methods
  3. The 10 print formats (2 of 10 were seen rendering incidentally)
  4. Double-click / concurrent settle

These are not edge cases. They are what a cashier does all day. The original plan was to observe them during a staged production rollout; that is the wrong order. A production QA window is only meaningful once the core paths are known to work — otherwise the first real customer becomes the test case, and the failure mode is a wrong receipt or a duplicate bill.

Important for whoever picks this up: all four are implemented. This is verification and defect-fixing, not new development. Code references below.

Scope

1. Discounts

Implemented via DiscountSchemeValidationService (validation at settle), recalculateDiscountsForAll(), and per-item lookup through priceMatrixController.getPaymentSchemeDiscountPercent(...). Persisted by native SQL as discount / discountRate on the bill item and DISCOUNT on the bill, plus setDiscount / setDiscountPercentPharmacy on the finance details.

  • Discount scheme applied → per-item discountPercent / discountValue correct
  • Bill-level DISCOUNT equals the sum of item discounts
  • netTotal = gross − discount, on the bill and in the finance details
  • Item with discountAllowed = false receives no discount
  • Changing payment scheme after items are added recalculates all rows before settle
  • Discount survives the settle → DB values match what the screen showed

2. Multiple payment methods

Implemented — paymentMethodData with MultiplePaymentMethods branches, persisted through billBean.setPaymentMethodData(...). Note the deliberate design: the breakdown goes onto the bill's own columns, not into Payment rows, because the pharmacy-side bill creates zero payments.

  • Two-component split (e.g. cash + card) settles with the correct breakdown on the bill
  • Three or more components
  • Component total not matching the bill net is rejected with a clear message
  • Credit component records the owing institution
  • Staff / staff-welfare components resolve the correct staff member
  • Breakdown renders correctly on the printed bill
  • Still exactly zero Payment rows at pharmacy time; payment is created only at the cashier step

3. All 10 print formats

Each is behind its own config key. Exact key → composite mapping:

Token papers

Config key Composite Default
Pharmacy Retail Sale Token Paper is FiveFivePaper With Blank Space For Printed Heading saleBill_five_five_token_native true
Pharmacy Retail Sale Token Paper is POS Paper saleBillToken_native true
Pharmacy Retail Sale Token Paper is POS Paper Custom 1 saleBillToken_Custom_1_native false

Bill papers

Config key Composite Default
Pharmacy Retail Sale Bill Paper is POS Paper saleBill_for_Cashier_native true
Pharmacy Retail Sale Bill Paper is POS Paper Custom 1 saleBill_for_Cashier_Pos_paper_Custom_1_native false
Pharmacy Retail Sale Bill Paper is FiveFive Paper without Blank Space for Header saleBill_five_five_for_Cashier_native true
Pharmacy Retail Sale Bill Paper is POS paper with header saleBill_Header_Inward_native true
Pharmacy Retail Sale Bill Paper is Custom 1 retail_sale_for_cashier_custom_1_native false
Pharmacy Retail Sale Bill Paper is Custom 2 retail_sale_for_cashier_custom_2_native false
Pharmacy Retail Sale Bill Paper is Custom 3 retail_sale_for_cashier_custom_3_native false

For each: enable it alone, settle one bill, print, and compare against the same bill printed from the legacy page.

  • All 10 verified on paper against legacy output

Check specifically item rows, quantity / rate / total, discount line, net total, Age/Sex, Billed By, token number, department header. The Phase 2 fixes touched exactly those fields, so they are the regression-prone ones. Also confirm each format behaves with the token system both ON and OFF.

4. Double-click / concurrent settle

Guarded by the session-scoped billSettlingStarted latch. There is a prior production incident of two pre-bills created within the same second, so this needs real adversarial testing, not a single careful click.

  • Rapid double-click on Settle produces exactly one bill
  • Rapid triple-click likewise
  • Settle rejected by validation, then corrected and re-settled — the latch must release (regression: this previously deadlocked the page for the whole session)
  • Two browser tabs on the same page, settling near-simultaneously
  • Same stock item settled concurrently from two windows — stock deducted exactly once per bill, never oversold
  • Latch released on the exception path so a failed settle does not lock the page

5. Token system OFF

The page defaults assume the token system is on.

  • Full settle with Enable token system in sale for cashier = false — no NPE, no orphan token, bill and print both correct

Acceptance criteria

Out of scope

Cancellation, refunds and reprint — real gaps, but they exercise pre-existing controllers rather than the native settle path. They stay in the Phase 3 production QA plan unless testing here turns up a native-path interaction.

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions