feat(machine): apply the settle_transaction operator op
ADR-005 §6: the operator paid the customer by hand and recorded it in spirekeeper. The op carries the txid and the note; the machine flips its own dispense_error/partial row to remediated via remediateTransaction, which only touches rows still in an error state, so re-delivery is a no-op. Closes the machine side of the ledger for an owed-cash sale without dispensing anything.
This commit is contained in:
parent
23bd54738d
commit
651c43d7b0
1 changed files with 31 additions and 2 deletions
|
|
@ -65,7 +65,21 @@ type CassetteOp = {
|
||||||
* machine" gesture and already clears counts-uncertain.
|
* machine" gesture and already clears counts-uncertain.
|
||||||
*/
|
*/
|
||||||
type ResumeCashOutOp = { id: string; at: number; type: 'resume_cash_out' }
|
type ResumeCashOutOp = { id: string; at: number; type: 'resume_cash_out' }
|
||||||
type OperatorOp = CassetteOp | ResumeCashOutOp
|
/**
|
||||||
|
* ADR-005 §6: the operator paid the customer by hand, off-machine, for a
|
||||||
|
* cash-out this machine recorded as dispense_error / partial. Flips that row
|
||||||
|
* to `remediated` with the note as provenance so both ledgers close on one
|
||||||
|
* act. Idempotent by nature — remediateTransaction only touches rows still
|
||||||
|
* in an error state — so re-delivery is harmless.
|
||||||
|
*/
|
||||||
|
type SettleTransactionOp = {
|
||||||
|
id: string
|
||||||
|
at: number
|
||||||
|
type: 'settle_transaction'
|
||||||
|
txid: string
|
||||||
|
note?: string
|
||||||
|
}
|
||||||
|
type OperatorOp = CassetteOp | ResumeCashOutOp | SettleTransactionOp
|
||||||
|
|
||||||
/** Accept operator events stamped up to this many seconds in the future. */
|
/** Accept operator events stamped up to this many seconds in the future. */
|
||||||
const MAX_FUTURE_SKEW_S = 60
|
const MAX_FUTURE_SKEW_S = 60
|
||||||
|
|
@ -249,7 +263,22 @@ async function handleOperatorConfigEvent(
|
||||||
const resumeOps = allOps.filter(
|
const resumeOps = allOps.filter(
|
||||||
(o): o is ResumeCashOutOp => !!o && o.type === 'resume_cash_out'
|
(o): o is ResumeCashOutOp => !!o && o.type === 'resume_cash_out'
|
||||||
)
|
)
|
||||||
const ops = allOps.filter((o): o is CassetteOp => !!o && o.type !== 'resume_cash_out')
|
const settleOps = allOps.filter(
|
||||||
|
(o): o is SettleTransactionOp =>
|
||||||
|
!!o && o.type === 'settle_transaction' && typeof (o as SettleTransactionOp).txid === 'string'
|
||||||
|
)
|
||||||
|
for (const op of settleOps) {
|
||||||
|
const provenance = `settled-off-machine:${op.id}${op.note ? `:${op.note}` : ''}`
|
||||||
|
const changed = await api.remediateTransaction(op.txid, provenance)
|
||||||
|
console.log(
|
||||||
|
`[OperatorConfig] settle_transaction ${op.id} for ${op.txid}: ` +
|
||||||
|
(changed ? 'row marked remediated' : 'no row in an error state (already closed, or unknown)')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const ops = allOps.filter(
|
||||||
|
(o): o is CassetteOp =>
|
||||||
|
!!o && o.type !== 'resume_cash_out' && o.type !== 'settle_transaction'
|
||||||
|
)
|
||||||
if (holdBefore && resumeOps.some((o) => typeof o.at === 'number' && o.at > holdBefore.since)) {
|
if (holdBefore && resumeOps.some((o) => typeof o.at === 'number' && o.at > holdBefore.since)) {
|
||||||
await api.clearCashOutHold()
|
await api.clearCashOutHold()
|
||||||
console.log(
|
console.log(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue