"""Coffer sells its own goods for prepaid credit and pays suppliers by invoice.""" from __future__ import annotations from dataclasses import dataclass, field from coffer.ledger import Ledger from coffer.units import ACCOUNT_COFFER, agent_account, human_account, to_subunits ACCOUNT_ESCROW = "company:coffer-escrow" # held credit for orders waiting for review MAX_PENDING_ORDERS_PER_ACCOUNT = 2 ORDER_REVIEWERS = frozenset({"agent:leora"}) # the operator; only it accepts or rejects orders def order_reviewers() -> frozenset[str]: """The operator account(s). A deployment with a different operator sets COFFER_ORDER_REVIEWERS.""" import os extra = {a.strip() for a in os.environ.get("COFFER_ORDER_REVIEWERS", "").split(",") if a.strip()} return frozenset(extra) if extra else ORDER_REVIEWERS ORDER_REVIEW_NOTICE = ("Your order is waiting for confirmation. We review every order and accept or reject it " "based on stock and volume. If it's rejected, your credits are released in full.") class UnknownGood(Exception): """No good with that sku is sold by Coffer.""" class CannotSupply(Exception): """Coffer has no stock and no supplier it can order this good from.""" class DuplicateOrder(Exception): """This idempotency key was already used for a different order.""" class TooManyPendingOrders(Exception): """This account already has the maximum number of orders waiting for review.""" class NotAReviewer(Exception): """Only the operator can accept or reject orders.""" class OrderNotPending(Exception): """The order was already accepted or rejected.""" @dataclass class Supplier: supplier_id: str name: str @dataclass class Good: sku: str name: str credit_price: int supplier_id: str | None = None supplier_unit_cost: int = 0 stock: int = 0 @dataclass class Invoice: invoice_id: str supplier_id: str sku: str quantity: int dollars: int status: str = "payable" @dataclass class Order: order_id: str buyer_id: str sku: str quantity: int credits_charged: int draws: list[dict[str, int | str]] from_stock: int procured: int status: str invoice_id: str | None = None tx_seq: int | None = None held: int = 0 # centicredits held in escrow while pending review hold_tx_seq: int | None = None reviewed_by: str | None = None reviewed_at: str | None = None review_note: str | None = None @dataclass class Store: ledger: Ledger = field(default_factory=Ledger) goods: dict[str, Good] = field(default_factory=dict) suppliers: dict[str, Supplier] = field(default_factory=dict) orders: dict[str, Order] = field(default_factory=dict) invoices: dict[str, Invoice] = field(default_factory=dict) merchant_account: str = ACCOUNT_COFFER _seq: int = 0 _idempotency: dict[str, str] = field(default_factory=dict) def add_supplier(self, supplier_id: str, name: str) -> Supplier: if not supplier_id or not name: raise ValueError("a supplier needs an id and a name") supplier = Supplier(supplier_id, name) self.suppliers[supplier_id] = supplier return supplier def add_good( self, sku: str, name: str, credit_price: int, supplier_id: str | None = None, supplier_unit_cost: int = 0, stock: int = 0, ) -> Good: if type(credit_price) is not int or credit_price <= 0: raise ValueError("credit price must be a positive whole number of credits") if type(supplier_unit_cost) is not int or supplier_unit_cost < 0: raise ValueError("supplier cost must be a whole number of dollars") if type(stock) is not int or stock < 0: raise ValueError("stock must be a whole number") if supplier_id is not None and supplier_id not in self.suppliers: raise ValueError(f"unknown supplier {supplier_id}") good = Good(sku, name, credit_price, supplier_id, supplier_unit_cost, stock) self.goods[sku] = good return good def receive_stock(self, sku: str, quantity: int) -> Invoice | None: """Take goods into Coffer's own inventory. Pay the supplier by invoice, if there is one.""" if type(quantity) is not int or quantity <= 0: raise ValueError("quantity must be a positive whole number") good = self.goods.get(sku) if good is None: raise UnknownGood(sku) good.stock += quantity if good.supplier_id and good.supplier_unit_cost: return self._invoice(good.supplier_id, sku, quantity, good.supplier_unit_cost * quantity) return None def place_order( self, buyer_id: str, sku: str, quantity: int = 1, idempotency_key: str | None = None, ) -> Order: """Submit an order. The price is HELD (escrow, not charged) and the order waits for manual review: the operator accepts (charge + print) or rejects (release in full).""" buyer = human_account(buyer_id) if str(buyer_id).startswith("human:") else agent_account(buyer_id) if idempotency_key: existing = self._idempotency.get(idempotency_key) if existing is not None: order = self.orders[existing] if order.buyer_id != buyer or order.sku != sku or order.quantity != quantity: raise DuplicateOrder(idempotency_key) return order if type(quantity) is not int or quantity <= 0 or quantity > 10: raise ValueError("quantity must be a whole number from 1 to 10") good = self.goods.get(sku) if good is None: raise UnknownGood(sku) if not good.stock and (not good.supplier_id or good.supplier_id not in self.suppliers): raise CannotSupply(sku) if len(self.pending_orders(buyer)) >= MAX_PENDING_ORDERS_PER_ACCOUNT: raise TooManyPendingOrders(buyer) credits = good.credit_price * quantity self._seq += 1 oid = f"ord-{self._seq}" draws_sub, tx = self.ledger.hold(buyer, ACCOUNT_ESCROW, to_subunits(credits), reason=f"order-hold:{sku}", meta={"sku": sku, "quantity": quantity, "order": oid}) order = Order(order_id=oid, buyer_id=buyer, sku=sku, quantity=quantity, credits_charged=0, draws=draws_sub, from_stock=0, procured=0, status="pending_review", held=to_subunits(credits), hold_tx_seq=tx.seq) self.orders[oid] = order if idempotency_key: self._idempotency[idempotency_key] = oid return order def pending_orders(self, buyer_id: str | None = None) -> list[Order]: return [o for o in self.orders.values() if o.status == "pending_review" and (buyer_id is None or o.buyer_id == buyer_id)] def held_for(self, buyer_id: str) -> int: return sum(o.held for o in self.pending_orders(buyer_id)) def _review(self, order_id: str, actor: str) -> Order: if actor not in order_reviewers(): raise NotAReviewer(actor) order = self.orders.get(order_id) if order is None: raise KeyError(order_id) if order.status != "pending_review": raise OrderNotPending(order_id) return order def accept_order(self, order_id: str, actor: str, note: str = "", at: str | None = None) -> Order: """Operator accepts: the held credit is charged and the goods are taken from stock or queued for printing (supplier invoice).""" order = self._review(order_id, actor) good = self.goods[order.sku] from_stock = min(good.stock, order.quantity) procured = order.quantity - from_stock if procured and (not good.supplier_id or good.supplier_id not in self.suppliers): raise CannotSupply(order.sku) tx = self.ledger.settle_hold(ACCOUNT_ESCROW, self.merchant_account, order.held, reason=f"purchase:{order.sku}", meta={"sku": order.sku, "quantity": order.quantity, "order": order.order_id, "buyer": order.buyer_id, "reviewed_by": actor}) good.stock -= from_stock if procured: order.invoice_id = self._invoice(good.supplier_id, order.sku, procured, good.supplier_unit_cost * procured).invoice_id order.credits_charged = order.held // 100 order.held = 0 order.from_stock, order.procured = from_stock, procured order.status = "fulfilled" if procured == 0 else "procuring" order.tx_seq = tx.seq order.reviewed_by, order.reviewed_at, order.review_note = actor, at, (note or "")[:200] return order def reject_order(self, order_id: str, actor: str, note: str = "", at: str | None = None) -> Order: """Operator rejects: every held credit goes back to the buyer, as the same kinds.""" order = self._review(order_id, actor) tx = self.ledger.release_hold(ACCOUNT_ESCROW, order.buyer_id, order.draws, reason=f"order-released:{order.sku}", meta={"order": order.order_id, "reviewed_by": actor}) order.held = 0 order.status = "rejected" order.tx_seq = tx.seq order.reviewed_by, order.reviewed_at, order.review_note = actor, at, (note or "")[:200] return order def _invoice(self, supplier_id: str, sku: str, quantity: int, dollars: int) -> Invoice: self._seq += 1 invoice = Invoice( invoice_id=f"inv-{self._seq}", supplier_id=supplier_id, sku=sku, quantity=quantity, dollars=dollars, ) self.invoices[invoice.invoice_id] = invoice return invoice def new_store(ledger: Ledger | None = None) -> Store: """The opening catalog: a Coffer shirt at 20 credits.""" store = Store(ledger=ledger or Ledger()) store.add_supplier("printer", "Printer") store.add_good( sku="coffer-shirt", name="Coffer shirt", credit_price=20, supplier_id="printer", supplier_unit_cost=8, ) return store