dodil/erp-brick-pos

DODIL ERP brick: point of sale

tests/test_statement_budget.py

"""Statement count is flat: a receipt of 20 lines, or a session of 20 receipts, costs the engine the same number of statements as
one of 2 (a statement per line is how a shop's busiest minute becomes a timeout). Counted with the engine's own cursor hook."""
from contextlib import contextmanager

from conftest import CASHIER, api, finalise, make_shop, must, open_till, pay, ring
from sqlalchemy import event


@contextmanager
def counting(engine):
    seen: list = []

    def hook(conn, cursor, statement, parameters, context, executemany):
        seen.append(statement)
    event.listen(engine, "before_cursor_execute", hook)
    try:
        yield seen
    finally:
        event.remove(engine, "before_cursor_execute", hook)


def many_products(nb, n=20):
    for i in range(1, n + 1):
        nb.price_lists["LIST-UK-SHOP"]["prices"].append((f"P{i:02d}", "C62", "1", f"{i}.50"))
        nb.product_row(f"P{i:02d}", "hot_drink")


def lines(n):
    return [{"product_id": f"P{i:02d}", "quantity": "1"} for i in range(1, n + 1)]


def test_ringing_paying_and_finalising_cost_the_same_statements_for_2_lines_or_20(engine, nb):
    many_products(nb)
    make_shop()
    sid = open_till()["session_id"]
    costs = {}
    for n in (2, 20):
        with counting(engine) as ring_stmts:
            r = must(api("POST", "/receipts", CASHIER, {"session_id": sid, "lines": lines(n)}))
        with counting(engine) as pay_stmts:
            r = must(api("POST", f"/receipts/{r['receipt_id']}/payments", CASHIER, {"method": "cash", "amount": r["remaining"], "version": r["version"]}))
        with counting(engine) as fin_stmts:
            r = must(api("POST", f"/receipts/{r['receipt_id']}/finalise", CASHIER, {"version": r["version"]}))
        assert len(r["lines"]) == n and r["status"] == "finalised"
        costs[n] = (len(ring_stmts), len(pay_stmts), len(fin_stmts))
    assert costs[2] == costs[20], costs


def test_counting_and_reading_a_session_cost_the_same_statements_for_2_receipts_or_20(engine, nb):
    make_shop()
    a, b = open_till(register="R1"), open_till(register="R2", who=CASHIER)
    for sid, n in ((a["session_id"], 2), (b["session_id"], 20)):
        for _ in range(n):
            r = ring(sid, [("LATTE-REG", 1)])
            finalise(pay(r, "cash", "3.60"))
    costs = {}
    for label, s in (("two", a), ("twenty", b)):
        with counting(engine) as read:
            view = must(api("GET", f"/sessions/{s['session_id']}", CASHIER))
        with counting(engine) as counted:
            must(api("POST", f"/sessions/{s['session_id']}/count", CASHIER, {"counted_cash": "0.00"}))
        costs[label] = (len(read), len(counted))
        assert view["summary"]["receipts"] in (2, 20)
    assert costs["two"] == costs["twenty"], costs