Specs › shop · version 1 ·
feature
shop
features
shop-catalog, shop-suppliers, shop-customers, shop-orders, shop-paid, shop-cancelled

Shop: the model and its rules✎ edit

Products in categories, the suppliers they come from, customers and their orders. When an order is paid its quantities come off stock; if a paid order is cancelled they go back.

$enum OrderStatus (new, paid, shipped, cancelled)
$class Category (
  @key id: String,
  @label name: String,
  parent: <>Category?,
) @tooltip("A shelf in the shop; a category can sit inside another")
$class Supplier (
  @key id: String,
  @label name: String,
  email: String?,
  leadDays: Int = 7,
) @tooltip("Who we buy a product from")
$class Product (
  @key sku: String,
  @label name: String,
  category: <>Category,
  supplier: <>Supplier?,
  price: Number,
  cost: Number,
  stock: Int = 0,
  reorderAt: Int = 5,
  @calc(price - cost) margin: Number,
  @calc(stock <= reorderAt) low: Boolean,
) @tooltip("Something we sell")
$class Customer (
  @key id: String,
  @label name: String,
  email: String?,
  since: Date?,
) @tooltip("Someone who buys from us")
$class OrderLine (
  product: <>Product,
  qty: Int,
  price: Number,
)
$class Order (
  @key id: String,
  placed: Date,
  customer: <>Customer,
  status: OrderStatus = new,
  lines: OrderLine*,
  stockTaken: Boolean = false,
  @calc((lines ?? []) fold (acc = 0) (l => acc + l.qty * l.price)) total: Number,
) @tooltip("A customer's order: what they bought, and where it stands")

Rules✎ edit

An order that is made or changed is checked: paid, and its stock not yet taken, takes it; cancelled after it was taken puts it back. stockTaken makes sure each happens once.

$when diesel.entity.on.created (category, key) if (category is "Order") {
  shop.orderChanged (key = key)
}
$when diesel.entity.on.updated (category, key) if (category is "Order") {
  shop.orderChanged (key = key)
}
$when shop.orderChanged (key: String) {
  o = dom.find (cls = "Order", key = key)
  lines = o.lines ?? []
  if (o.status is "paid" and not (o.stockTaken ?? false)) {
    if (sizeOf(lines) > 0) {
      shop.restock (lines = lines, sign = -1)
    }
    dom.upsert (cls = "Order", entity = o + {stockTaken: true})
  }
  if (o.status is "cancelled" and (o.stockTaken ?? false)) {
    if (sizeOf(lines) > 0) {
      shop.restock (lines = lines, sign = 1)
    }
    dom.upsert (cls = "Order", entity = o + {stockTaken: false})
  }
}
$when shop.restock (lines, sign: Int) {
  l = lines[0]
  p = dom.find (cls = "Product", key = l.product)
  dom.upsert (cls = "Product", entity = p + {stock: p.stock + sign * l.qty})
  rest = slice(x = lines, from = 1)
  if (sizeOf(rest) > 0) {
    shop.restock (lines = rest, sign = sign)
  }
}

Declares 6 classes: Category, Supplier, Product, Customer, OrderLine, Order.