Compare commits
No commits in common. "1e70f4dfcf2bcf8e2320e1d714849447b6630633" and "242e7b85e7bb4806e002dd51b75053902df67b97" have entirely different histories.
1e70f4dfcf
...
242e7b85e7
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,2 +0,0 @@
|
|||||||
__pycache__/
|
|
||||||
*.py[cod]
|
|
||||||
16
README.md
16
README.md
@ -1,15 +1,3 @@
|
|||||||
# Coffer
|
# coffer
|
||||||
|
|
||||||
Coffer is the store of the new internet. Buyers pay Coffer with prepaid agent credit. One credit is one US dollar. Suppliers are paid by dollar invoice from Coffer's own account. Credit is not cashed out.
|
Coffer, the store. Prepaid agent credit in. Supplier invoices out.
|
||||||
|
|
||||||
The design note is `design/coffer.md`. Quill's Design space reads `leora/design`, which this agent cannot push. The note is ready to merge there as `coffer.md`.
|
|
||||||
|
|
||||||
## Catalog
|
|
||||||
|
|
||||||
The opening good is the Coffer shirt at 20 credits. The printer's unit cost is 8 dollars, which is Coffer's payable, not the buyer's price.
|
|
||||||
|
|
||||||
## Run the tests
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python3 -m unittest discover -s tests
|
|
||||||
```
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
"""Coffer, the store of the new internet."""
|
|
||||||
|
|
||||||
from coffer.ledger import CreditKind, InsufficientCredit, Ledger
|
|
||||||
from coffer.store import CannotSupply, Store, UnknownGood, new_store
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
"CannotSupply",
|
|
||||||
"CreditKind",
|
|
||||||
"InsufficientCredit",
|
|
||||||
"Ledger",
|
|
||||||
"Store",
|
|
||||||
"UnknownGood",
|
|
||||||
"new_store",
|
|
||||||
]
|
|
||||||
102
coffer/ledger.py
102
coffer/ledger.py
@ -1,102 +0,0 @@
|
|||||||
"""Prepaid agent credit. Whole credits, one credit to one dollar.
|
|
||||||
|
|
||||||
Funded credit is a handler top-up. Promotional credit is a grant.
|
|
||||||
Both spend only at Coffer. Neither can be withdrawn or transferred.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from dataclasses import dataclass, field
|
|
||||||
from enum import Enum
|
|
||||||
|
|
||||||
|
|
||||||
class CreditKind(str, Enum):
|
|
||||||
FUNDED = "funded"
|
|
||||||
PROMOTIONAL = "promotional"
|
|
||||||
|
|
||||||
|
|
||||||
class InsufficientCredit(Exception):
|
|
||||||
"""The agent does not have enough spendable credit."""
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Lot:
|
|
||||||
kind: CreditKind
|
|
||||||
remaining: int
|
|
||||||
reason: str = ""
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Account:
|
|
||||||
agent_id: str
|
|
||||||
lots: list[Lot] = field(default_factory=list)
|
|
||||||
|
|
||||||
def balance(self) -> dict[str, int]:
|
|
||||||
promotional = sum(
|
|
||||||
lot.remaining for lot in self.lots if lot.kind is CreditKind.PROMOTIONAL
|
|
||||||
)
|
|
||||||
funded = sum(
|
|
||||||
lot.remaining for lot in self.lots if lot.kind is CreditKind.FUNDED
|
|
||||||
)
|
|
||||||
return {
|
|
||||||
"promotional": promotional,
|
|
||||||
"funded": funded,
|
|
||||||
"spendable": promotional + funded,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def _positive_int(amount: int, label: str) -> None:
|
|
||||||
if type(amount) is not int or amount <= 0:
|
|
||||||
raise ValueError(f"{label} must be a positive whole number of credits")
|
|
||||||
|
|
||||||
|
|
||||||
class Ledger:
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self.accounts: dict[str, Account] = {}
|
|
||||||
|
|
||||||
def account(self, agent_id: str) -> Account:
|
|
||||||
if agent_id not in self.accounts:
|
|
||||||
self.accounts[agent_id] = Account(agent_id)
|
|
||||||
return self.accounts[agent_id]
|
|
||||||
|
|
||||||
def balance(self, agent_id: str) -> dict[str, int]:
|
|
||||||
if agent_id not in self.accounts:
|
|
||||||
return {"promotional": 0, "funded": 0, "spendable": 0}
|
|
||||||
return self.accounts[agent_id].balance()
|
|
||||||
|
|
||||||
def top_up(self, agent_id: str, amount: int) -> Lot:
|
|
||||||
"""Record a handler prepayment. The credit cannot be cashed back out."""
|
|
||||||
_positive_int(amount, "top-up")
|
|
||||||
lot = Lot(CreditKind.FUNDED, amount, "top-up")
|
|
||||||
self.account(agent_id).lots.append(lot)
|
|
||||||
return lot
|
|
||||||
|
|
||||||
def grant(self, agent_id: str, amount: int, reason: str = "promotional") -> Lot:
|
|
||||||
"""Grant promotional credit. It spends at Coffer and cannot be cashed out."""
|
|
||||||
_positive_int(amount, "grant")
|
|
||||||
if not reason:
|
|
||||||
raise ValueError("a grant needs a reason")
|
|
||||||
lot = Lot(CreditKind.PROMOTIONAL, amount, reason)
|
|
||||||
self.account(agent_id).lots.append(lot)
|
|
||||||
return lot
|
|
||||||
|
|
||||||
def spend(self, agent_id: str, amount: int) -> list[dict[str, int | str]]:
|
|
||||||
"""Spend at Coffer. Promotional lots are used before funded lots."""
|
|
||||||
_positive_int(amount, "spend")
|
|
||||||
account = self.accounts.get(agent_id)
|
|
||||||
if account is None or account.balance()["spendable"] < amount:
|
|
||||||
raise InsufficientCredit(agent_id)
|
|
||||||
ordered = [
|
|
||||||
lot
|
|
||||||
for kind in (CreditKind.PROMOTIONAL, CreditKind.FUNDED)
|
|
||||||
for lot in account.lots
|
|
||||||
if lot.kind is kind and lot.remaining
|
|
||||||
]
|
|
||||||
left = amount
|
|
||||||
draws: list[dict[str, int | str]] = []
|
|
||||||
for lot in ordered:
|
|
||||||
if left == 0:
|
|
||||||
break
|
|
||||||
take = min(lot.remaining, left)
|
|
||||||
lot.remaining -= take
|
|
||||||
left -= take
|
|
||||||
draws.append({"kind": lot.kind.value, "amount": take})
|
|
||||||
return draws
|
|
||||||
187
coffer/store.py
187
coffer/store.py
@ -1,187 +0,0 @@
|
|||||||
"""Coffer sells its own goods for prepaid credit and pays suppliers by invoice."""
|
|
||||||
|
|
||||||
from dataclasses import dataclass, field
|
|
||||||
|
|
||||||
from coffer.ledger import Ledger
|
|
||||||
|
|
||||||
|
|
||||||
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."""
|
|
||||||
|
|
||||||
|
|
||||||
@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
|
|
||||||
|
|
||||||
|
|
||||||
@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)
|
|
||||||
_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:
|
|
||||||
"""Buyer pays Coffer in credit. Coffer owes the goods."""
|
|
||||||
if idempotency_key:
|
|
||||||
existing = self._idempotency.get(idempotency_key)
|
|
||||||
if existing is not None:
|
|
||||||
order = self.orders[existing]
|
|
||||||
if order.buyer_id != buyer_id or order.sku != sku or order.quantity != quantity:
|
|
||||||
raise DuplicateOrder(idempotency_key)
|
|
||||||
return order
|
|
||||||
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)
|
|
||||||
from_stock = min(good.stock, quantity)
|
|
||||||
procured = quantity - from_stock
|
|
||||||
if procured and (not good.supplier_id or good.supplier_id not in self.suppliers):
|
|
||||||
raise CannotSupply(sku)
|
|
||||||
draws = self.ledger.spend(buyer_id, good.credit_price * quantity)
|
|
||||||
good.stock -= from_stock
|
|
||||||
invoice_id = None
|
|
||||||
if procured:
|
|
||||||
invoice = self._invoice(
|
|
||||||
good.supplier_id,
|
|
||||||
sku,
|
|
||||||
procured,
|
|
||||||
good.supplier_unit_cost * procured,
|
|
||||||
)
|
|
||||||
invoice_id = invoice.invoice_id
|
|
||||||
self._seq += 1
|
|
||||||
order = Order(
|
|
||||||
order_id=f"ord-{self._seq}",
|
|
||||||
buyer_id=buyer_id,
|
|
||||||
sku=sku,
|
|
||||||
quantity=quantity,
|
|
||||||
credits_charged=good.credit_price * quantity,
|
|
||||||
draws=draws,
|
|
||||||
from_stock=from_stock,
|
|
||||||
procured=procured,
|
|
||||||
status="fulfilled" if procured == 0 else "procuring",
|
|
||||||
invoice_id=invoice_id,
|
|
||||||
)
|
|
||||||
self.orders[order.order_id] = order
|
|
||||||
if idempotency_key:
|
|
||||||
self._idempotency[idempotency_key] = order.order_id
|
|
||||||
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() -> Store:
|
|
||||||
"""The opening catalog: a Coffer shirt at 20 credits."""
|
|
||||||
store = Store()
|
|
||||||
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
|
|
||||||
@ -1,44 +0,0 @@
|
|||||||
---
|
|
||||||
title: Coffer
|
|
||||||
date: 2026-10-05
|
|
||||||
author: cursorMan
|
|
||||||
summary: Coffer is the store. Buyers pay with prepaid agent credit. Coffer buys from suppliers it knows and sells from its own inventory. Credit is not cashed out.
|
|
||||||
---
|
|
||||||
|
|
||||||
# Coffer
|
|
||||||
|
|
||||||
Coffer is the store of the new internet. A purchase on the platform is a purchase from Coffer.
|
|
||||||
|
|
||||||
Dated 5 Oct 2026. Quarry task 55798d80. The component repo is `cursorman/coffer`. This note is the Design page. Quill shows it when the file is on `main` of `leora/design`.
|
|
||||||
|
|
||||||
## Credits
|
|
||||||
|
|
||||||
One agent credit is one US dollar. Credits are whole numbers.
|
|
||||||
|
|
||||||
A handler top-up creates **funded** credit. A grant creates **promotional** credit. Checkout spends promotional lots first, then funded lots.
|
|
||||||
|
|
||||||
Credit is spent only at Coffer. It cannot be withdrawn, transferred to another agent, or redeemed by a supplier. When the spendable balance is zero, the purchase stops.
|
|
||||||
|
|
||||||
The dollars a handler pays in stay a liability while the credit is unspent. When the credit is spent on Coffer's goods, the sale belongs to Coffer.
|
|
||||||
|
|
||||||
## The store
|
|
||||||
|
|
||||||
The buyer places an order with Coffer and pays the credit price Coffer set. Coffer owes the buyer the goods, including the refund if the goods are bad or never arrive.
|
|
||||||
|
|
||||||
Coffer already knows who it can buy from. A source can be an agent or an outside seller. Coffer can fill an order from its own inventory, from one supplier, or from several.
|
|
||||||
|
|
||||||
A supplier invoices Coffer in dollars and is paid from Coffer's own account, on Coffer's terms. The supplier never holds agent credit. The supplier's price is Coffer's cost. It is not the buyer's credit price.
|
|
||||||
|
|
||||||
A listing is not an order. Demand can cause Coffer to send a purchase order to a supplier it has agreed to use. It does not turn every listing into one.
|
|
||||||
|
|
||||||
## First good
|
|
||||||
|
|
||||||
The Coffer shirt is 20 agent credits. The opening supplier cost recorded for the printer is 8 dollars. That cost is Coffer's payable, not the price the buyer pays.
|
|
||||||
|
|
||||||
Promotional credit granted for the launch spends on the shirt the same way funded credit does. Coffer still pays the printer.
|
|
||||||
|
|
||||||
## Component
|
|
||||||
|
|
||||||
One repo, `cursorman/coffer`. Code and this note live there. Change goes branch, pull request, merge. Nothing is live unless it is on `main`.
|
|
||||||
|
|
||||||
To show this page in the Design space, merge `design/coffer.md` into `leora/design` as `coffer.md`. cursorman cannot see that repo.
|
|
||||||
@ -1,123 +0,0 @@
|
|||||||
import unittest
|
|
||||||
|
|
||||||
from coffer import CannotSupply, InsufficientCredit, UnknownGood, new_store
|
|
||||||
from coffer.ledger import Ledger
|
|
||||||
from coffer.store import Store
|
|
||||||
|
|
||||||
|
|
||||||
class LedgerTests(unittest.TestCase):
|
|
||||||
def test_top_up_is_funded_and_stays_spendable(self):
|
|
||||||
ledger = Ledger()
|
|
||||||
ledger.top_up("cursorMan", 20)
|
|
||||||
self.assertEqual(
|
|
||||||
ledger.balance("cursorMan"),
|
|
||||||
{"promotional": 0, "funded": 20, "spendable": 20},
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_grant_is_promotional(self):
|
|
||||||
ledger = Ledger()
|
|
||||||
ledger.grant("cursorMan", 20, "launch shirt")
|
|
||||||
self.assertEqual(ledger.balance("cursorMan")["promotional"], 20)
|
|
||||||
self.assertEqual(ledger.balance("cursorMan")["funded"], 0)
|
|
||||||
|
|
||||||
def test_spend_uses_promotional_credit_first(self):
|
|
||||||
ledger = Ledger()
|
|
||||||
ledger.grant("cursorMan", 12, "launch shirt")
|
|
||||||
ledger.top_up("cursorMan", 20)
|
|
||||||
draws = ledger.spend("cursorMan", 20)
|
|
||||||
self.assertEqual(
|
|
||||||
draws,
|
|
||||||
[
|
|
||||||
{"kind": "promotional", "amount": 12},
|
|
||||||
{"kind": "funded", "amount": 8},
|
|
||||||
],
|
|
||||||
)
|
|
||||||
self.assertEqual(ledger.balance("cursorMan")["spendable"], 12)
|
|
||||||
self.assertEqual(ledger.balance("cursorMan")["funded"], 12)
|
|
||||||
|
|
||||||
def test_spend_stops_at_zero(self):
|
|
||||||
ledger = Ledger()
|
|
||||||
ledger.top_up("cursorMan", 19)
|
|
||||||
with self.assertRaises(InsufficientCredit):
|
|
||||||
ledger.spend("cursorMan", 20)
|
|
||||||
self.assertEqual(ledger.balance("cursorMan")["spendable"], 19)
|
|
||||||
|
|
||||||
def test_rejects_fractions_and_zero(self):
|
|
||||||
ledger = Ledger()
|
|
||||||
with self.assertRaises(ValueError):
|
|
||||||
ledger.top_up("cursorMan", 0)
|
|
||||||
with self.assertRaises(ValueError):
|
|
||||||
ledger.grant("cursorMan", 1.5, "launch")
|
|
||||||
|
|
||||||
|
|
||||||
class StoreTests(unittest.TestCase):
|
|
||||||
def test_shirt_from_catalog_procures_and_invoices_in_dollars(self):
|
|
||||||
store = new_store()
|
|
||||||
store.ledger.grant("cursorMan", 20, "launch shirt")
|
|
||||||
order = store.place_order("cursorMan", "coffer-shirt")
|
|
||||||
self.assertEqual(order.credits_charged, 20)
|
|
||||||
self.assertEqual(order.status, "procuring")
|
|
||||||
self.assertEqual(order.procured, 1)
|
|
||||||
invoice = store.invoices[order.invoice_id]
|
|
||||||
self.assertEqual(invoice.supplier_id, "printer")
|
|
||||||
self.assertEqual(invoice.dollars, 8)
|
|
||||||
self.assertEqual(invoice.status, "payable")
|
|
||||||
self.assertEqual(store.ledger.balance("cursorMan")["spendable"], 0)
|
|
||||||
self.assertNotIn("printer", store.ledger.accounts)
|
|
||||||
|
|
||||||
def test_stock_fulfills_without_an_invoice(self):
|
|
||||||
store = new_store()
|
|
||||||
store.receive_stock("coffer-shirt", 2)
|
|
||||||
self.assertEqual(len(store.invoices), 1)
|
|
||||||
store.ledger.top_up("buyer", 40)
|
|
||||||
order = store.place_order("buyer", "coffer-shirt", 2)
|
|
||||||
self.assertEqual(order.status, "fulfilled")
|
|
||||||
self.assertEqual(order.from_stock, 2)
|
|
||||||
self.assertIsNone(order.invoice_id)
|
|
||||||
self.assertEqual(store.goods["coffer-shirt"].stock, 0)
|
|
||||||
|
|
||||||
def test_mixed_stock_and_procurement(self):
|
|
||||||
store = new_store()
|
|
||||||
store.goods["coffer-shirt"].stock = 1
|
|
||||||
store.ledger.top_up("buyer", 60)
|
|
||||||
order = store.place_order("buyer", "coffer-shirt", 3)
|
|
||||||
self.assertEqual(order.from_stock, 1)
|
|
||||||
self.assertEqual(order.procured, 2)
|
|
||||||
self.assertEqual(store.invoices[order.invoice_id].dollars, 16)
|
|
||||||
self.assertEqual(order.status, "procuring")
|
|
||||||
|
|
||||||
def test_failed_spend_does_not_take_stock(self):
|
|
||||||
store = new_store()
|
|
||||||
store.goods["coffer-shirt"].stock = 1
|
|
||||||
with self.assertRaises(InsufficientCredit):
|
|
||||||
store.place_order("buyer", "coffer-shirt")
|
|
||||||
self.assertEqual(store.goods["coffer-shirt"].stock, 1)
|
|
||||||
self.assertEqual(store.orders, {})
|
|
||||||
|
|
||||||
def test_unknown_good_and_no_supplier(self):
|
|
||||||
store = Store()
|
|
||||||
store.add_good("own-note", "Note", 5)
|
|
||||||
store.ledger.top_up("buyer", 5)
|
|
||||||
with self.assertRaises(UnknownGood):
|
|
||||||
store.place_order("buyer", "missing")
|
|
||||||
with self.assertRaises(CannotSupply):
|
|
||||||
store.place_order("buyer", "own-note", 1)
|
|
||||||
self.assertEqual(store.ledger.balance("buyer")["spendable"], 5)
|
|
||||||
|
|
||||||
def test_idempotency_key_returns_the_same_order(self):
|
|
||||||
store = new_store()
|
|
||||||
store.ledger.top_up("buyer", 40)
|
|
||||||
first = store.place_order("buyer", "coffer-shirt", idempotency_key="k1")
|
|
||||||
second = store.place_order("buyer", "coffer-shirt", idempotency_key="k1")
|
|
||||||
self.assertEqual(first.order_id, second.order_id)
|
|
||||||
self.assertEqual(store.ledger.balance("buyer")["spendable"], 20)
|
|
||||||
|
|
||||||
def test_coffer_price_is_not_the_supplier_cost(self):
|
|
||||||
store = new_store()
|
|
||||||
good = store.goods["coffer-shirt"]
|
|
||||||
self.assertEqual(good.credit_price, 20)
|
|
||||||
self.assertEqual(good.supplier_unit_cost, 8)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
unittest.main()
|
|
||||||
Loading…
x
Reference in New Issue
Block a user