dodil/erp-brick-gl
DODIL ERP brick: general ledger
tests/test_v7.py
"""Spec v7: GET /companies/{code}?on=, the lines answer account_id per line and the group path, the GL-30 purposes of the wave-3 modules, the tool names."""
from conftest import ACCOUNTANT, CONSOLIDATION, OWNER, api, must, person, refused
from test_group_and_ai import gid, group_chart
from test_v6 import READER, map_group
from world import account_id, books, invoice
from brick_gl.application.use_cases import GL
def test_gl_61_the_statutory_tax_rate_is_the_row_in_force_on_the_date_asked():
books()
must(api("POST", "/controls", OWNER, {"key": "statutory_tax_rate", "value": "0.25", "company_code": "UK01", "valid_from": "2026-01-01"}))
must(api("POST", "/controls", OWNER, {"key": "statutory_tax_rate", "value": "0.19", "company_code": "UK01", "valid_from": "2027-01-01"}))
assert must(api("GET", "/companies/UK01", ACCOUNTANT, {"on": "2026-12-31"}))["statutory_tax_rate"] == "0.25"
assert must(api("GET", "/companies/UK01", ACCOUNTANT, {"on": "2027-06-30"}))["statutory_tax_rate"] == "0.19"
assert must(api("GET", "/companies/UK01", ACCOUNTANT))["statutory_tax_rate"] == "0.25" # today
def test_gl_04_each_line_names_its_local_account_and_the_group_path_must_be_the_group_account():
books()
group_chart()
invoice("INV-L1", on="2026-10-05", net="100.00", vat="0.01")
map_group([("1100", "G-1000")])
q = {"company_code": "UK01", "from": "2026-10-01", "to": "2026-10-31"}
plain = must(api("GET", f"/accounts/{account_id('1100')}/lines", READER, q))
assert {ln["account_id"] for ln in plain["lines"]} == {str(account_id("1100"))} and plain["from"] == "2026-10-01" and plain["to"] == "2026-10-31"
by_group = must(api("GET", f"/accounts/{gid('G-1000')}/lines", READER, {**q, "group_account_id": str(gid("G-1000"))}))
assert {ln["account_id"] for ln in by_group["lines"]} == {str(account_id("1100"))}
s, b = api("GET", f"/accounts/{account_id('1100')}/lines", READER, {**q, "group_account_id": str(gid("G-1000"))})
assert s == 422 and b["gate"] == "group_account_matches_path"
def test_the_wave_3_purposes_are_listed_unmapped_until_the_account_map_has_them():
books()
chart = must(api("GET", "/companies/UK01", ACCOUNTANT))["chart_id"]
unmapped = set(must(api("GET", "/account-map", ACCOUNTANT, {"chart_id": chart}))["unmapped"])
assert {"cash_in_transit", "card_clearing", "card_fees", "employee_payable", "wip", "cost_of_sales", "tips_payable"} <= unmapped
def test_the_shared_family_tools_name_the_module_noun():
names = {u.name for u in GL.use_cases}
assert {"set_gl_account_map", "get_gl_account_map", "list_gl_migration_runs", "get_gl_migration_run", "get_gl_migration_map",
"get_gl_migration_status"} <= names
assert not {"set_account_map", "get_account_map", "list_migration_runs", "get_migration_run", "get_migration_map", "get_migration_status"} & names
def test_gl_30_a_payments_payout_posts_through_the_mapped_purposes_and_an_unmapped_one_is_named():
from world import line, post
books()
s, b = post("pay:payout-1", "payout", "2026-10-12", [line(purpose="employee_payable", debit="352.40"), line(account="1200", credit="352.40")],
source_module="payments")
assert s == 422 and "[purposes_mapped]" in str(b) and "employee_payable" in str(b)
must(api("POST", "/account-map", ACCOUNTANT, {"chart_id": "MAIN", "purpose": "employee_payable", "account_id": str(account_id("2100")), "valid_from": "2026-01-02"}))
s, b = post("pay:payout-1", "payout", "2026-10-12", [line(purpose="employee_payable", debit="352.40"), line(account="1200", credit="352.40")],
source_module="payments")
assert s in (200, 201), b
s, b = post("pos:LON-SOHO:2026-10-12", "day_close", "2026-10-12", [line(purpose="card_clearing", debit="100.00"), line(account="4000", credit="100.00")],
source_module="pos")
assert s == 422 and "card_clearing" in str(b) # pos's purposes are the customer's to map