fix(state-machine): prevent cancel after bills accepted in cash-in

Once bills are stacked in the cash box they cannot be returned.
Cancel in displayingQR now goes to confirmAbandon warning state.
Error state retries to generatingNdebit instead of idle when bills
are present. CANCEL from error only goes to idle if no bills inserted.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Patrick Mulligan 2026-02-25 19:12:24 -05:00
commit 2eda5959b9

View file

@ -431,6 +431,7 @@ export function createATMMachine(
}, },
}, },
generatingNdebit: { generatingNdebit: {
// No CANCEL here — bills are already in the cash box
invoke: { invoke: {
src: 'generateNdebit', src: 'generateNdebit',
input: ({ context }) => context, input: ({ context }) => context,
@ -457,8 +458,15 @@ export function createATMMachine(
target: 'error', target: 'error',
actions: ['setError', 'setPaymentFailed'], actions: ['setError', 'setPaymentFailed'],
}, },
TIMEOUT: '#atm.idle', CANCEL: 'confirmAbandon',
CANCEL: '#atm.idle', TIMEOUT: 'confirmAbandon',
},
},
confirmAbandon: {
// Warning: cash is in the machine, abandoning forfeits it
on: {
CANCEL: '#atm.idle', // User confirms they want to leave
RETRY: 'generatingNdebit', // Go back and try again
}, },
}, },
askForReceipt: { askForReceipt: {
@ -489,10 +497,20 @@ export function createATMMachine(
on: { on: {
RETRY: { RETRY: {
guard: 'canRetry', guard: 'canRetry',
target: 'fetchingRate', target: 'generatingNdebit',
actions: 'incrementRetry', actions: 'incrementRetry',
}, },
CANCEL: '#atm.idle', CANCEL: [
{
// No bills inserted: safe to cancel
guard: ({ context }) => context.billsInserted.length === 0,
target: '#atm.idle',
},
{
// Bills inserted: show abandon warning first
target: 'confirmAbandon',
},
],
}, },
}, },
}, },