Compare commits
No commits in common. "5a4f70c90ea52ff06b720d81e772903f5566bda7" and "23bd54738d2a8d6f175d80293518d3183fe0870b" have entirely different histories.
5a4f70c90e
...
23bd54738d
2 changed files with 10 additions and 45 deletions
|
|
@ -65,21 +65,7 @@ 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
|
||||||
|
|
@ -263,22 +249,7 @@ 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 settleOps = allOps.filter(
|
const ops = allOps.filter((o): o is CassetteOp => !!o && o.type !== 'resume_cash_out')
|
||||||
(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(
|
||||||
|
|
|
||||||
|
|
@ -246,13 +246,9 @@ rather than pausing it.
|
||||||
|
|
||||||
Server: `cash_owed` and `dispense_unreported` are two new buckets on
|
Server: `cash_owed` and `dispense_unreported` are two new buckets on
|
||||||
`StuckSettlementsResponse`. They are the only buckets whose meaning is *a customer is owed
|
`StuckSettlementsResponse`. They are the only buckets whose meaning is *a customer is owed
|
||||||
money*, and they render first. Arrival in `cash_owed` or `partial_pending` sends the operator
|
money*, and they render first. Arrival in either bucket triggers the operator notification
|
||||||
a **NIP-17 gift-wrapped DM** (kind 14 → 13 → 1059) to their own LNbits-account pubkey, or to
|
path (whatever `notifyOperator` equivalent spirekeeper grows; at minimum the dashboard banner
|
||||||
`super_config.alerts_pubkey` when set — a note to self any NIP-46 client renders. It is signed
|
— but the push is the point, and it belongs in the same transaction that writes the row).
|
||||||
through the operator's signer (no key at rest), is best-effort (a failed publish is logged and
|
|
||||||
the report is still acked — the worklist is the durable record), and sets
|
|
||||||
`operator_notified_at` so a report resend never re-alerts. Not email, not NIP-04.
|
|
||||||
*(Implemented: spirekeeper `notify.py`, slice 2.)*
|
|
||||||
|
|
||||||
Resolution closes **both** ledgers:
|
Resolution closes **both** ledgers:
|
||||||
|
|
||||||
|
|
@ -260,13 +256,11 @@ Resolution closes **both** ledgers:
|
||||||
to `remediated` via `remediateTransaction`. The machine sends a `report_dispense` for the
|
to `remediated` via `remediateTransaction`. The machine sends a `report_dispense` for the
|
||||||
remediation with `remediates_txid`, and the server moves the settlement from `cash_owed` to
|
remediation with `remediates_txid`, and the server moves the settlement from `cash_owed` to
|
||||||
`pending` and distributes.
|
`pending` and distributes.
|
||||||
- **Off-machine settlement.** The operator paid the customer by hand.
|
- **Off-machine settlement.** The operator paid the customer by hand. A new `settle_cash_owed`
|
||||||
`POST /settlements/{id}/settle-cash-owed` records provenance (free text, author, time) on the
|
operator action records provenance (free text, author, time) on the settlement, moves it to
|
||||||
settlement, moves it to `pending` and distributes **at the full amount** (the customer is
|
`pending`, and publishes a `settle_transaction { txid, note }` operator op; the machine
|
||||||
whole), and publishes a machine-wide `settle_transaction { id, at, txid, note }` operator op;
|
applies it by setting `remediated_by` to the note and `status = 'remediated'`. Today there is
|
||||||
the machine applies it through `remediateTransaction(txid, "settled-off-machine:<op id>:<note>")`,
|
no way to record this at all, and the machine's ledger asserts the debt forever.
|
||||||
which only touches rows still in an error state, so re-delivery is harmless. Before slice 2
|
|
||||||
there was no way to record this at all, and the machine's ledger asserted the debt forever.
|
|
||||||
|
|
||||||
`PartialDispenseData` is pre-filled from the report's `bills` so the operator confirms a
|
`PartialDispenseData` is pre-filled from the report's `bills` so the operator confirms a
|
||||||
number the hardware produced rather than typing one.
|
number the hardware produced rather than typing one.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue