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