refunds v1

What is left to refund on each order is an account of its own: the sale adds to it, a refund takes from it, and it never goes below 0. A refund is held while it waits for approval

examples/refunds/refunds.book, as chobo doc writes it. Each account keeps a balance: what came in, less what went out. Every transfer keeps the bounds of the accounts: one that would break a bound is refused with the name the book gives that bound, and nothing of it moves. A transfer either moves at once (do), or first holds what it moves (hold); a hold is then posted (post, all of it or part), voided (void), or expires.

Accounts

AccountOne for eachUnitBoundsWhat it is
refundableorderUSDat least 0.00; a transfer that would go below is refused with refund_exceeds_salewhat is left to refund on the order
salesone accountUSDoutside the book: no bounds, and it may go below 0
refundedone accountUSDoutside the book: no bounds, and it may go below 0

How things move

How things move between the accounts of refunds sale refund refundable(order)USDat least 0.00 (refund_exceeds_sale) salesUSD · outside refundedUSD · outside

A box is an account, and an arrow a move of a transfer. A rounded, dashed box is an account outside the book, which has no bounds. A dashed arrow belongs to a transfer that holds first, and moves when the hold is posted.

Transfers

sale

  • Moves amount from sales to refundable(order).
  • Key: once per order. The same call again does nothing, and answers done_before; one that differs only in amount is refused with key_conflict.
  • Moves at once (do).
OperationMay be refused withWhen
sale.dokey_conflicta call with the same order and other arguments came before
How each refusal comes about

sale.do: key_conflict

 1  sale.do(order: order-1, amount: 0.01)  done
 2  sale.do(order: order-1, amount: 0.02)  refused: key_conflict

refund

held while it waits for approval: posted when it is approved, voided when it is turned down

  • Moves amount from refundable(order) to refunded.
  • Key: once per request. The same call again does nothing, and answers done_before; one that differs only in order or amount is refused with key_conflict; holding again with the same key after the hold has ended answers done_before, and holds nothing.
  • Holds first: the caller posts or voids the hold. It expires 7 days after it was made, and what it holds goes back.
refund.hold held post posted void is refused: already_posted post for other amounts: key_conflict void voided post is refused: already_voided after 7 days expired post and void are refused: expired
The holdpostvoid
heldposts it: the amounts given, or all of it, and the rest goes back; more than it holds is refused with over_hold; once 7 days have passed since it was held, refused with expiredvoids it: what it holds goes back; once 7 days have passed since it was held, refused with expired
posteddone_before with the same amounts; refused with key_conflict with othersrefused with already_posted
voidedrefused with already_voideddone_before
expiredrefused with expiredrefused with expired
no hold with the keyrefused with no_such_holdrefused with no_such_hold
OperationMay be refused withWhen
refund.holdrefund_exceeds_salerefundable(order) would go below 0.00
refund.holdkey_conflicta call with the same request and other arguments came before
refund.holdalready_refuseda call with the same request was refused by a bound before; a key a bound refused stays refused, even once there is enough
refund.postkey_conflictthe hold was posted before, for other amounts
refund.postalready_voidedthe hold is voided already
refund.postexpiredthe hold has expired
refund.postover_holdmore than the hold holds
refund.postno_such_holdthere is no hold with that request
refund.voidalready_postedthe hold is posted already
refund.voidexpiredthe hold has expired
refund.voidno_such_holdthere is no hold with that request
How each refusal comes about

refund.hold: refund_exceeds_sale

 1  refund.hold(request: request-1, order: order-2, amount: 0.01)  refused: refund_exceeds_sale (move 1 takes 0.01 from refundable(order-2): posted 0.00, held out 0.00)

refund.hold: key_conflict

 1  sale.do(order: order-1, amount: 0.01)                          done
 2  refund.hold(request: request-2, order: order-1, amount: 0.01)  done
 3  refund.hold(request: request-2, order: order-1, amount: 0.02)  refused: key_conflict

refund.hold: already_refused

 1  refund.hold(request: request-1, order: order-2, amount: 0.01)  refused: refund_exceeds_sale (move 1 takes 0.01 from refundable(order-2): posted 0.00, held out 0.00)
 2  refund.hold(request: request-1, order: order-2, amount: 0.01)  refused: already_refused

refund.post: key_conflict

 1  sale.do(order: order-1, amount: 0.02)                          done
 2  refund.hold(request: request-2, order: order-1, amount: 0.02)  done
 3  refund.post(request: request-2)                                done
 4  refund.post(request: request-2, amount: 0.01)                  refused: key_conflict

refund.post: already_voided

 1  sale.do(order: order-1, amount: 0.02)                          done
 2  refund.hold(request: request-2, order: order-1, amount: 0.02)  done
 3  refund.void(request: request-2)                                done
 4  refund.post(request: request-2)                                refused: already_voided

refund.post: expired

 1  sale.do(order: order-1, amount: 0.02)                          done
 2  refund.hold(request: request-2, order: order-1, amount: 0.02)  done
 3  pass 7 days                                                    refund(request-2) expired
 4  refund.post(request: request-2)                                refused: expired

refund.post: over_hold

 1  sale.do(order: order-1, amount: 0.02)                          done
 2  refund.hold(request: request-2, order: order-1, amount: 0.02)  done
 3  refund.post(request: request-2, amount: 0.03)                  refused: over_hold

refund.post: no_such_hold

 1  refund.post(request: request-1)  refused: no_such_hold

refund.void: already_posted

 1  sale.do(order: order-1, amount: 0.02)                          done
 2  refund.hold(request: request-2, order: order-1, amount: 0.02)  done
 3  refund.post(request: request-2)                                done
 4  refund.void(request: request-2)                                refused: already_posted

refund.void: expired

 1  sale.do(order: order-1, amount: 0.02)                          done
 2  refund.hold(request: request-2, order: order-1, amount: 0.02)  done
 3  pass 7 days                                                    refund(request-2) expired
 4  refund.void(request: request-2)                                refused: expired

refund.void: no_such_hold

 1  refund.void(request: request-1)  refused: no_such_hold

Scenarios

19 scenarios, which chobo scenarios makes from the book. Pick one and step through it: after each step come the balances and the holds it left, and on the chart the arrows of the transfers it called light up (red when refused).

    AccountPostedHeld outHeld in
    HoldState
    How things move between the accounts of refunds sale refund refundable(order)USDat least 0.00 (refund_exceeds_sale) salesUSD · outside refundedUSD · outside