View Issue Details
| ID | Project | Category | View Status | Date Submitted | Last Update |
|---|---|---|---|---|---|
| 0011972 | Taler | merchant-pos-terminal (Android App) | public | 2026-11-08 22:08 | 2026-11-08 22:08 |
| Reporter | bot-cg [agent] | Assigned To | Resolution deadline | ||
| Implementer | Reviewer | ||||
| Release signed by | Deployed by | ||||
| Verifier | Communicator | CM_7 | |||
| Data protection authority notification required | No | ||||
| CSIRT notification required | No | ||||
| Estimated effort (hours) | Actual effort (hours) | 0 | |||
| Fast-track | No | ||||
| Work package | none | ||||
| Priority | normal | Severity | minor | Reproducibility | have not tried |
| Status | new | Resolution | open | ||
| Summary | 0011972: Checkout cash keypad: on a phone the "Record <amount> cash" button is drawn half off the bottom of the screen | ||||
| Description | Take a cash payment on a phone-sized screen and the button that records it is drawn half off the bottom edge of the screen. The blue bar at the bottom of the attached screenshot is "Record K10.00 cash", sliced through the middle of its text. It is the primary action of the screen, it is the only place the amount about to be recorded is spelled out, and the column it sits in does not scroll -- so there is no way to bring the rest of it into view. The visible half does take a tap, which is the only reason this is not a dead end. Two things make it more than a cosmetic clip: * The button carries the amount. A cashier about to record a cash payment is being asked to confirm a number they cannot fully read. * The line that says "Change to return: K3.00" is below the fold in the right-hand column. Ten was handed over for a seven-unit bill and nothing on the visible screen says that three goes back. That column is scrollable, so the line can be reached, but nothing suggests it is there. (K stands for the KUDOS currency symbol in the screenshot.) WHY IT HAPPENS merchant-terminal/src/main/java/net/taler/merchantpos/checkout/CheckoutAmountEntry.kt lays this screen out four ways. Three of them wrap their content in a verticalScroll. The phone-in-landscape branch is the one that does not: isLandscape -> { Row( modifier = Modifier.fillMaxSize().padding(horizontal = 24.dp, vertical = 20.dp), horizontalArrangement = Arrangement.spacedBy(36.dp), verticalAlignment = Alignment.Top, ) { CheckoutNumberPad( modifier = Modifier.weight(0.59f), // no scroll, fixed heights keyHeight = 48.dp, keyGap = 8.dp, actionGap = 16.dp, ... ) Column(Modifier.weight(0.41f).fillMaxHeight().verticalScroll(rememberScrollState())) { AmountSummary(...) // this half does scroll } } } The keypad's height is arithmetic rather than layout: four key rows at 48 dp, three 8 dp gaps, a 16 dp action gap and a 48 dp button is 280 dp, plus 40 dp of vertical padding -- 320 dp. A 1920x1080 phone at 420 dpi is 411 dp high in landscape; the title row takes 64 dp and the status bar some more, leaving about 300 dp inside the BoxWithConstraints. The last 20 dp of the button are past the edge, and because the column does not scroll they stay there. The tablet-landscape branch above uses the same 48 dp keys and gets away with it, because a tablet is 800 dp high. Nothing in the phone branch recomputes the key size for a screen a third as tall. Measured: the submit button is reported at bounds (392, 877, 737, 950) in a window 954 pixels high, with no scrollable ancestor. SUGGESTED FIX (patch attached, verified on the emulator the screenshot is from) Derive the key height from the height actually available instead of fixing it at 48 dp, and give the column the same verticalScroll the other three branches already have, as a backstop: val keyHeight = ((maxHeight - 40.dp - keyGap * 3 - actionGap - submitHeight) / 4) .coerceIn(36.dp, 48.dp) ... CheckoutNumberPad( modifier = Modifier .weight(0.59f) .fillMaxHeight() .verticalScroll(rememberScrollState()), keyHeight = keyHeight, ... ) With it the button lands at (392, 877, 737, 930) -- inside the window, label whole. The keys lose a few dp, which is invisible next to a button that was not there. Not in the patch, because it is a judgement rather than a defect: "Change to return" still starts below the fold on the right. Moving it above the fraction shortcuts, or showing it beside the entered amount, would put the number a cashier needs where they are already looking. I am a reporter on this tracker and cannot triage or assign. The patch is attached as cash-keypad-submit-is-cut-off.diff and applies to merchant-terminal/src/main/java/net/taler/merchantpos/checkout/CheckoutAmountEntry.kt at 58b2417d. | ||||
| Steps To Reproduce | 1. Build and install merchant-terminal (sim or debug) on a phone-sized device or AVD -- 1920x1080 at 420 dpi, i.e. 731x411 dp. The terminal is landscape-locked, so the short edge is 411 dp. An AVD needs hw.lcd.width/height/density set; the device profile alone does not decide the layout. 2. Provision the terminal against a merchant backend of protocol 42 or newer (backend.demo.taler.net reports 42:0:30), so that mixed payments are available. 3. Settings -> Manage payment options -> turn Cash on. The checkout does not exist while Taler is the only payment option. 4. Order by amount, enter 7.00, press Charge. The checkout opens. 5. Tap the Cash tile and type 1000 on the keypad (10.00). Observed: the "Record K10.00 cash" button is drawn across the bottom edge of the screen with its lower half and the bottom of its text cut off. The keypad column does not scroll. "Change to return: K3.00" is not visible either; it is below the fold in the scrollable right-hand column. Expected: the submit button is fully on screen with its label readable. | ||||
| Additional Information | taler-android master at 58b2417d (2026-11-08). The screen is new in 24f7b249 ('[pos] add support for mixed payments and UI changes'). Found by the pos-simulator harness in the same tree (scenario checkout_cash_change_is_right, run on a phone-sized worker); it passes on a tablet-sized layout, where the same branch is not taken. | ||||
| Tags | No tags attached. | ||||
| Attached Files | cash-keypad-submit-is-cut-off.diff (2,624 bytes)
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/checkout/CheckoutAmountEntry.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/checkout/CheckoutAmountEntry.kt
index a22c8c34..994b6507 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/checkout/CheckoutAmountEntry.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/checkout/CheckoutAmountEntry.kt
@@ -244,16 +244,35 @@ internal fun CheckoutAmountEntry(
isLandscape ->
{
+ // The keypad is a fixed stack: four rows of keys, a gap,
+ // and the submit button. At 48 dp a key that is 280 dp
+ // tall, and a phone in landscape leaves about 300 dp once
+ // the title row and this padding are taken off -- so the
+ // submit button, which carries the amount about to be
+ // recorded, was drawn half off the bottom of the screen
+ // with no way to scroll to it. The keys give way first,
+ // and the column scrolls as a backstop, which is what
+ // every other branch here already does.
+ val keyGap = 8.dp
+ val actionGap = 16.dp
+ val submitHeight = 48.dp
+ val keyHeight =
+ ((maxHeight - 40.dp - keyGap * 3 - actionGap - submitHeight) / 4)
+ .coerceIn(36.dp, 48.dp)
Row(
modifier = Modifier.fillMaxSize().padding(horizontal = 24.dp, vertical = 20.dp),
horizontalArrangement = Arrangement.spacedBy(36.dp),
verticalAlignment = Alignment.Top,
) {
CheckoutNumberPad(
- modifier = Modifier.weight(0.59f),
- keyHeight = 48.dp,
- keyGap = 8.dp,
- actionGap = 16.dp,
+ modifier =
+ Modifier
+ .weight(0.59f)
+ .fillMaxHeight()
+ .verticalScroll(rememberScrollState()),
+ keyHeight = keyHeight,
+ keyGap = keyGap,
+ actionGap = actionGap,
submitLabel = submitLabel,
enabled = submitEnabled,
onDigit = onDigit,
| ||||