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:
- Discounts
- Multiple payment methods
- The 10 print formats (2 of 10 were seen rendering incidentally)
- 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.
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.
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.
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.
5. Token system OFF
The page defaults assume the token system is on.
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.
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
Paymentrows 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:
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 throughpriceMatrixController.getPaymentSchemeDiscountPercent(...). Persisted by native SQL asdiscount/discountRateon the bill item andDISCOUNTon the bill, plussetDiscount/setDiscountPercentPharmacyon the finance details.discountPercent/discountValuecorrectDISCOUNTequals the sum of item discountsnetTotal= gross − discount, on the bill and in the finance detailsdiscountAllowed = falsereceives no discount2. Multiple payment methods
Implemented —
paymentMethodDatawithMultiplePaymentMethodsbranches, persisted throughbillBean.setPaymentMethodData(...). Note the deliberate design: the breakdown goes onto the bill's own columns, not intoPaymentrows, because the pharmacy-side bill creates zero payments.Paymentrows at pharmacy time; payment is created only at the cashier step3. All 10 print formats
Each is behind its own config key. Exact key → composite mapping:
Token papers
Pharmacy Retail Sale Token Paper is FiveFivePaper With Blank Space For Printed HeadingsaleBill_five_five_token_nativePharmacy Retail Sale Token Paper is POS PapersaleBillToken_nativePharmacy Retail Sale Token Paper is POS Paper Custom 1saleBillToken_Custom_1_nativeBill papers
Pharmacy Retail Sale Bill Paper is POS PapersaleBill_for_Cashier_nativePharmacy Retail Sale Bill Paper is POS Paper Custom 1saleBill_for_Cashier_Pos_paper_Custom_1_nativePharmacy Retail Sale Bill Paper is FiveFive Paper without Blank Space for HeadersaleBill_five_five_for_Cashier_nativePharmacy Retail Sale Bill Paper is POS paper with headersaleBill_Header_Inward_nativePharmacy Retail Sale Bill Paper is Custom 1retail_sale_for_cashier_custom_1_nativePharmacy Retail Sale Bill Paper is Custom 2retail_sale_for_cashier_custom_2_nativePharmacy Retail Sale Bill Paper is Custom 3retail_sale_for_cashier_custom_3_nativeFor each: enable it alone, settle one bill, print, and compare against the same bill printed from the legacy page.
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
billSettlingStartedlatch. 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.5. Token system OFF
The page defaults assume the token system is on.
Enable token system in sale for cashier = false— no NPE, no orphan token, bill and print both correctAcceptance 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.