Skip to content

Examples

Four books, each in English with its Japanese version beside it (<name>.ja.book), whose names are all Japanese and whose units are 個, pt and 円. Each passes chobo check without a warning, and the tests run every scenario chobo scenarios writes for it through every client (How it is checked). Beside each book in the repository are the pages chobo doc writes of it, doc.md and doc.ja.md, which GitHub shows.

Example What it shows The book Drawn
the stock of an order, held for 30 minutes, posted when it ships and voided when it is cancelled a hold that expires, a key of two parameters inventory · Japanese page
loyalty points, earned on a hold that waits out the return period, spent on one that waits for the payment, and lapsing a lower and an upper bound, a hold that never expires points · Japanese page
refunds that never exceed the sale a condition over two accounts, written as the bound of one; two refunds at the same time refunds · Japanese page
a marketplace that splits what a buyer paid between the shop and its fee two moves, all or none; amounts the caller works out marketplace · Japanese page

Inventory

The stock of each SKU, which a delivery fills and an order holds for 30 minutes: shipping posts the hold and cancelling voids it, and a return puts the goods back on the shelves.

account stock(sku: string) : pcs
  description "what is on the shelves"
  at least 0 refused as out_of_stock
…
transfer reserve(order: string, sku: string, qty: pcs)
  description "posted when the order ships, voided when it is cancelled"
  key order, sku
  pending expires after 30 minutes
  move qty from stock(sku) to customers

The key is the order and the SKU, so an order of three SKUs holds three times, once for each. Two orders after the last piece of a SKU at the same moment come out as one holding it and the other refused with out_of_stock, and a hold left alone for 30 minutes expires and puts the piece back.

Points

A member's points, between 0 and a million. Points a purchase earns are held until its return period is over, with no expiry of their own; points a checkout spends are held for 15 minutes while the payment goes through; and what is left unused at the end of a period lapses.

account points(member: string) : pt
  at least 0 refused as not_enough_points
  at most 1000000 refused as points_cap
…
transfer earn(purchase: string, member: string, pts: pt)
  description "posted when the return period is over, voided when the purchase is returned"
  key purchase
  pending never expires
  move pts from issued to points(member)
…
transfer lapse(member: string, period: string, pts: pt)
  description "what the member did not use in the period"
  key member, period
  move pts from points(member) to lapsed

Earning is refused at the upper bound, spending and lapsing at the lower one. Points held coming in are counted against the upper bound and not as points to spend, so a purchase's points cannot be spent before its return period is over and the hold is posted.

Refunds

What is left to refund on each order is an account of its own: the sale fills it, a refund takes from it, and it never goes below 0. A refund is held while it waits for approval, for 7 days at most. The home page shows the whole book.

transfer refund(request: string, order: string, amount: USD)
  description "held while it waits for approval: posted when it is approved, voided when it is turned down"
  key request
  pending expires after 7 days
  move amount from refundable(order) to refunded

Beside it is a scenario written by hand, refunds.more.json: two refunds of 20.00 at the same time on a sale of 30.00, then one of 10.00, which Run scenarios runs. The tests run it on every target too.

Marketplace

A balance for each shop. A sale splits what the buyer paid between the shop's balance and the marketplace's fee, in amounts the caller works out; a refund takes both parts back, both or neither; and a payout is held until the bank confirms it.

transfer refund(refund_id: string, shop: string, to_shop: USD, fee: USD)
  description "gives back the fee and the shop's part, both or neither"
  key refund_id
  move fee from fees to buyers
  move to_shop from shop_balance(shop) to buyers
…
transfer payout(payout_id: string, shop: string, amount: USD)
  description "held when the bank transfer starts: posted when the bank confirms it, voided when it fails"
  key payout_id
  pending never expires
  move amount from shop_balance(shop) to bank

A refund that would take the shop's balance below 0 is refused at its second move, and the fee its first move gave back is undone with it. Write a book reads this book line by line.

The test books

The books under tests/books try what the examples do not: a lower bound above 0 (safety_stock), a credit line below 0 with a cap on what is paid ahead (credit), a wallet with an upper bound (wallet), moves between two accounts of one kind (to_itself), and a sale of three moves beside an exchange from yen to dollars (allocation), each in English and under a Japanese name. stock_reservation has scenarios written by hand beside it. The books under tests/fixtures each show one diagnostic or a few, in English and in Japanese.