# order

Placing an order: check the stock, price the lines, pick a discount, confirm. Each step is its own message, so each can be extended or replaced with rules of its own. The arithmetic is in expressions; the rules decide what happens next.

## Stock

$when order.checkStock (items: Array, stock: Object) {
  short = items filter (i => (stock[i.sku] ?? 0) < i.qty) map (i => i.sku)
  if (short is not empty) {
    list = short mkString ", "
    diesel.throw (code = "E_STOCK", msg = "out of stock: ${list}")
  }
}

## Pricing

$when order.price (items: Array) {
  payload = items map (i => i.qty * i.price) fold (s = 0.0) (x => s + x)
}

## Discounts

Guards pick exactly one rule: members get 10%, anyone else spending over 100 gets 5%, the rest pay full price.

$when order.discount (subtotal: Float, member: Boolean) if (member) {
  payload = subtotal * 0.10
}

$when order.discount (subtotal: Float, member: Boolean) if (not member and subtotal > 100.0) {
  payload = subtotal * 0.05
}

$when order.discount (subtotal: Float, member: Boolean) if (not member and subtotal <= 100.0) {
  payload = 0.0
}

## Placing it

Each step's variables (`subtotal`, `discount`) belong to this rule only; the caller sees just the payload it returns.

$when order.place (items: Array, member: Boolean, stock: Object) {
  order.checkStock (items, stock)
  subtotal = order.price(items = items)
  discount = order.discount(subtotal = subtotal, member = member)
  payload = {status: "confirmed", subtotal: subtotal, discount: discount, total: subtotal - discount}
}

`order.submit` is what a shop would call: the same, but a stock problem becomes a rejected order instead of an error.

$when order.submit (items: Array, member: Boolean, stock: Object) {
  try {
    order.place (items, member, stock)
  } catch (e) {
    payload = {status: "rejected", reason: e.message}
  }
}

Try it: [[Story:order]], or in the [Fiddle](/fiddle?tab=stories&topic=Story:order).
