dodil/erp-brick-sales-order
DODIL ERP brick: sales order
tests/test_audit1.py
"""Audit wave 1: six proven defects, each pinned by the proof that found it."""
import base64
import json
from conftest import _ROLES, ADMIN, BILLER, OWNER, api, confirm, confirmed, get, hand, must, on_day, refused
FEES = [{"product_id": "FEE", "quantity": "1", "unit_code": "C62"}]
W = [{"product_id": "WIDGET", "quantity": "12", "unit_code": "C62"}]
def who(role, sub, email=None):
"""A token with a sub and, optionally, an email (person() always gives both, equal)."""
payload = base64.urlsafe_b64encode(json.dumps({"permissions": list(_ROLES[role])}).encode()).decode()
user = {"sub": sub, "app_roles": [role]}
if email:
user["email"] = email
return {"X-Dodil-User": json.dumps(user), "X-Dodil-User-JWT": f"h.{payload.rstrip('=')}.s"}
def test_1_the_amender_is_not_the_approver_under_another_identity():
pat = who("sales_rep", "u-1", "pat@shop.example")
o = confirmed(who=pat, lines=W)
sid = o["sales_order_id"]
must(api("POST", f"/sales-orders/{sid}/amend", pat, {"reason": "r", "changes": [{"line_no": 1, "quantity": "13"}]}))
same_person = who("order_admin", "u-1") # same sub, no email
assert "approver_not_amender" in refused(api("POST", f"/sales-orders/{sid}/revisions/2/approve", same_person, {}))
assert get(sid)["revision"] == 1
assert api("POST", f"/sales-orders/{sid}/revisions/2/approve", who("order_admin", "u-9", "pat@shop.example"), {})[0] == 409 # same email
assert api("POST", f"/sales-orders/{sid}/revisions/2/approve", ADMIN, {})[0] == 200
def test_2_a_caller_without_an_email_sees_only_their_own_rows():
o = confirmed(lines=FEES)
sid = o["sales_order_id"]
sub_only = who("sales_rep", "u-2")
assert api("GET", f"/sales-orders/{sid}", sub_only)[0] == 404
status, body = api("GET", "/sales-orders", sub_only)
assert status == 200 and body["orders"] == []
mine = must(api("POST", "/sales-orders", sub_only, {"bp_id": 7311, "currency": "GBP", "lines": FEES}))
assert [x["sales_order_id"] for x in must(api("GET", "/sales-orders", sub_only))["orders"]] == [mine["sales_order_id"]]
assert sid in [x["sales_order_id"] for x in must(api("GET", "/sales-orders", ADMIN))["orders"]] # read_all still sees all
def test_3_an_order_number_is_never_reused_when_the_yearly_restart_is_switched_off():
first = confirmed(lines=FEES)
assert first["order_no"] == "SO-2026-0001"
must(api("POST", "/settings/number_restarts_yearly", ADMIN, {"value": False, "valid_from": "2026-11-21"}))
with on_day("2026-11-21T09:00:00+00:00"):
second = confirm(hand(lines=FEES))
third = confirm(hand(lines=FEES))
assert second["order_no"] == "SO-2026-0002" and third["order_no"] == "SO-2026-0003"
def test_3_the_order_no_is_claimed_once(engine):
from sqlalchemy import text
o = confirmed(lines=FEES)
with engine.begin() as c:
rows = c.execute(text("select order_no, sales_order_id from so_order_no_claim")).all()
assert [tuple(r) for r in rows] == [(o["order_no"], o["sales_order_id"])]
from brick_sales_order.infrastructure.tables import Repository
with engine.begin() as c:
assert Repository(c).order_no_claims.claim(o["order_no"], o["sales_order_id"]) is True
assert Repository(c).order_no_claims.claim(o["order_no"], "someone-else") is False
def test_4_the_number_format_is_validated():
def put(key, value, valid_from="2026-11-21"):
return api("POST", f"/settings/{key}", ADMIN, {"value": value, "valid_from": valid_from})
assert "{seq}" in refused(put("order_number_format", "SO"), 422)
assert "{yyyy}" in refused(put("order_number_format", "SO-{seq:4}"), 422) # restarts are yearly
must(put("order_number_format", "SO-{yyyy}-{seq}"))
must(put("number_restarts_yearly", False))
must(put("order_number_format", "SO-{seq:4}", "2026-11-22")) # fine once restarts are off
assert "{yyyy}" in refused(put("number_restarts_yearly", True, "2026-11-23"), 422) # and back on needs {yyyy}
def test_5_a_timezone_row_is_in_force_by_the_business_date_of_the_zone_before_it():
must(api("POST", "/controls/business_timezone", OWNER, {"value": "Pacific/Pago_Pago", "valid_from": "2026-11-21"}))
with on_day("2026-11-22T05:00:00+00:00"): # 18:00 on 11-21 in Pago_Pago
assert hand()["ordered_on"] == "2026-11-21"
must(api("POST", "/controls/business_timezone", OWNER, {"value": "Pacific/Kiritimati", "valid_from": "2026-11-22"}))
assert hand()["ordered_on"] == "2026-11-21" # the Kiritimati row is still in the future
with on_day("2026-11-22T12:00:00+00:00"): # 11-22 in Pago_Pago: Kiritimati now applies (11-23 there)
assert hand()["ordered_on"] == "2026-11-23"
def test_6_an_order_with_an_issued_deposit_is_cancelled_and_the_unused_deposit_is_credited(neighbours):
from test_stories import set_policy
set_policy(7311, "2026-11-20", "2026-11-19", deposit_pct="50")
o = confirmed(lines=FEES)
sid = o["sales_order_id"]
must(api("POST", f"/sales-orders/{sid}/raise-deposit", BILLER, {}))
neighbours.invoicing.issue(f"so:{sid}:1")
out = must(api("POST", f"/sales-orders/{sid}/cancel", ADMIN, {"reason": "x"})) # SO-47: nothing billed but the deposit, which is credited
assert out["lifecycle"] == "cancelled"
credit = neighbours.invoicing.credits[f"so:{sid}:2"]
assert credit["invoice_id"] == "AR-0001" and [x["net_amount"] for x in credit["lines"]] == ["500.00"]