dodil/erp-brick-sales-order
DODIL ERP brick: sales order
tests/test_statement_budget.py
"""Statement budgets: every statement is a round trip (100-300 ms on the live pg wire), so a request's statements must not grow with the number of
unrelated orders or lines. Counted at the engine, one request at a time."""
from datetime import date
import pytest
from conftest import BILLER, REP, api, as_role, confirm, hand, must
from erp_platform.testing import count_statements
from test_slice_b import DAY1, DAY2, SYSTEM, D, on_day, partner_rows, warehouse_set
COLA = {"product_id": "COLA-075", "quantity": "6", "unit_code": "C62"}
@pytest.fixture
def counted(engine):
return lambda: count_statements(engine)
def _orders(n):
return [confirm(hand(REP, lines=[COLA])) for _ in range(n)]
def _show(label, c):
print(f"BUDGET {label}: statements={c.statements} round_trips={c.round_trips}")
def test_reschedule_statements_do_not_grow_with_the_number_of_open_orders(neighbours, inventory, counted):
warehouse_set("WH1")
_orders(3)
with counted() as few:
must(api("POST", "/sales-orders/reschedule", SYSTEM, {}))
_orders(10)
with counted() as many:
must(api("POST", "/sales-orders/reschedule", SYSTEM, {"trigger": "cancelled", "cancelled_sales_order_id": "other"}))
_show("reschedule 3 unchanged", few)
_show("reschedule 13 unchanged", many)
assert few.statements <= 24 and few.round_trips <= 24, few # was 65 for 3 orders: thirteen reads per order; 22 now
assert many.statements <= few.statements + 2, (few.statements, many.statements) # was 245 for 13
def test_reschedule_that_changes_twelve_orders_stays_within_its_budget(neighbours, inventory, counted):
neighbours.partners.customers[7312] = True
partner_rows()
warehouse_set()
inventory.supply["JUICE-075"] = [(date(2026, 11, 10), D("720"), None)]
with on_day(DAY1):
ids = [confirm(hand(REP, lines=[{"product_id": "JUICE-075", "quantity": "10", "unit_code": "C62"}], requested_delivery_on="2026-11-04"))["sales_order_id"]
for _ in range(12)]
inventory.supply["JUICE-075"] = [(date(2026, 11, 17), D("720"), None)] # the purchase order slips a week: every promise moves later
with on_day(DAY2), counted() as c:
res = must(api("POST", "/sales-orders/reschedule", SYSTEM, {}))
_show("reschedule 12 changed", c)
assert sorted(res["re_promised"]) == sorted(ids)
assert c.statements <= 330 and c.round_trips <= 330, c # 706 before the snapshot and load_many: 27 an order, not 59
def test_cancel_with_many_open_orders_stays_within_its_budget(neighbours, inventory, counted, monkeypatch):
from brick_sales_order.application import use_cases
kicked = []
monkeypatch.setattr(use_cases, "after_response", lambda fn, *a, conn=None, **kw: kicked.append((fn, a))) # hold the post-response job: the response itself is what is counted
warehouse_set("WH1")
_orders(10)
o = confirm(hand(REP, lines=[COLA]))
with counted() as c:
must(api("POST", f"/sales-orders/{o['sales_order_id']}/cancel", as_role("order_admin"), {"reason": "withdrawn"}))
_show("cancel (request)", c)
assert c.statements <= 20 and c.round_trips <= 20, c # was 45: the re-promise never runs inside the cancel
assert len(kicked) == 1 and not [x for x in inventory.calls if x[0] == "reserve" and x[1] != o["sales_order_id"] and x[2] > 1] # not run inside the cancel
with counted() as run:
kicked[0][0](*kicked[0][1]) # the after-response job: the freed stock is re-promised without any explicit reschedule call
_show("cancel (after-response job)", run)
assert run.statements <= 26 and run.round_trips <= 26, run # was 40
with use_cases.repository() as repo:
assert repo.postings.pending_repromises() == [] # and it settled the durable row
def test_cancel_runs_the_re_promise_after_the_response_through_the_platform(neighbours, inventory):
warehouse_set("WH1")
o = confirm(hand(REP, lines=[COLA]))
must(api("POST", f"/sales-orders/{o['sales_order_id']}/cancel", as_role("order_admin"), {"reason": "withdrawn"}))
from brick_sales_order.application import use_cases
with use_cases.repository() as repo:
assert repo.postings.pending_repromises() == []
def test_confirm_and_request_invoice_stay_within_their_budgets(neighbours, inventory, counted):
o = hand(REP, lines=[COLA])
with counted() as c:
o = must(api("POST", f"/sales-orders/{o['sales_order_id']}/confirm", REP, {}))
_show("confirm", c)
assert c.statements <= 32 and c.round_trips <= 32, c # was 55 (44 measured)
with counted() as c:
must(api("POST", f"/sales-orders/{o['sales_order_id']}/request-invoice", BILLER, {}))
_show("request-invoice", c)
assert c.statements <= 41 and c.round_trips <= 41, c # was 80 (71 measured)
def test_get_order_statements(neighbours, inventory, counted):
o = confirm(hand(REP, lines=[COLA]))
with counted() as c:
must(api("GET", f"/sales-orders/{o['sales_order_id']}", REP))
_show("get order", c)
assert c.statements <= 14 and c.round_trips <= 14, c
def test_the_sale_steps_of_an_order_are_flat_in_lines_and_in_other_orders(neighbours, inventory, counted):
"""SO's own statements per sale step (the neighbours are fakes here; l2c-e2e counts theirs): draft_by_hand, confirm, inventory's two reads
(GET the order, its fulfilment), the delivery inventory records, request_invoice - for 2 vs 20 lines, and 2 lines after 50 other orders."""
from conftest import INVENTORY
def sale(n):
lines = [COLA for _ in range(n)]
with counted() as draft:
o = hand(REP, lines=lines)
sid = o["sales_order_id"]
with counted() as conf:
confirm(o)
with counted() as read:
must(api("GET", f"/sales-orders/{sid}", INVENTORY))
with counted() as ful:
must(api("GET", f"/sales-orders/{sid}/fulfilment", INVENTORY))
with counted() as dlv:
must(api("POST", f"/sales-orders/{sid}/deliveries", INVENTORY, {"line_no": 1, "quantity": "6", "unit_code": "C62",
"delivery_ref": f"inv:{sid}", "source": "inventory"}))
with counted() as inv:
must(api("POST", f"/sales-orders/{sid}/request-invoice", BILLER, {}))
return tuple(c.statements for c in (draft, conf, read, ful, dlv, inv))
sale(1)
two, twenty = sale(2), sale(20)
for _ in range(48):
sale(1)
later = sale(2)
print("BUDGET draft, confirm, get, fulfilment, delivery, request-invoice: 2 lines", two, "20 lines", twenty, "2 lines after 50 orders", later)
assert two == twenty == later, (two, twenty, later)
# was (29, 30, 13, 17, 22, 39): a new order re-read its 13 tables' keys before its first save, the number series its key; confirm asked the
# partner register once per partner function (sold-to, ship-to, bill-to and payer are often one partner). The order's 13-table load is ONE
# round trip on the pg wire (pipelined); request-invoice loads it twice by design (claim, ask invoicing, mark: three transactions)
assert all(n <= cap for n, cap in zip(two, (16, 29, 13, 17, 22, 39))), two