dodil/erp-brick-fixed-assets

DODIL ERP brick: fixed assets

tests/scenarios/catalogue.py

"""fixed-assets' scenario catalogue (module.yaml tests.scenarios), each mapped to its stories and written ONCE over the
composition's doors — routes, MCP tools — never a table. The role x use-case matrix is generated; the six business
scenarios are skeletons that say their Given/When/Then and are skipped until their steps are written (the stories behind them
are covered by tests/test_*.py, in process, against a stand-in gl).

    erp-scenarios list tests/scenarios/catalogue.py
    erp-scenarios run  tests/scenarios/catalogue.py
"""
import pathlib

from erp_platform.scenarios import Catalogue, Skip

HERE = pathlib.Path(__file__).resolve().parent
CAT = Catalogue("fixed-assets", compose=HERE / "compose.yaml", roles=HERE / "roles.yaml", clock="fa")

# Generated over the composition's doors, both HTTP and MCP: every role x every use case — 401 with no identity, 403 without the
# permission, served with it. The machine roles (system) hold the machine permissions only. The process view is the signed
# workflows as BPMN (GET /process/bpmn). TODO(scenario): `rows=Rows(make, owner, other)` for the custodian's row visibility.
CAT.permission_matrix()


@CAT.scenario("SC-FA-01", "bought on an order, depreciated, sold at a gain", stories=['FA-06', 'FA-10', 'FA-12', 'FA-13', 'FA-16', 'FA-17'])
def sc_sc_fa_01(s):
    s.given("class VEH (LIN5 full_month, areas 01 -> 0L and CA); a van ordered on PO-2026-0051 with account assignment asset; the bill 12,000.00 net approved 20 October 2026")
    s.when("purchasing hands over the bill line; October to September next year are run and approved by another person; the van is sold for 5,500.00 on 15 October 2027")
    s.then("cost 12,000.00 in 0L, clearing nets to zero; 12 runs of 200.00 and October 2027's 200.00 (full_month retirement); NBV 9,400.00 and a loss of 3,900.00 posted; the history sheet for 2027 (calendar fiscal year) shows a charge of 2,000.00 (January to October) and the retirement of cost 12,000.00 with accumulated depreciation 2,600.00; the tax area shows a main-pool disposal of 5,500.00")
    raise Skip("TODO(scenario): write the steps over the doors (routes, tools)")


@CAT.scenario("SC-FA-02", "a warehouse built, settled, then a late invoice", stories=['FA-09', 'FA-14', 'FA-06'])
def sc_sc_fa_02(s):
    s.given("AUC with 210,000.00 of bills and 15,000.00 own labour; settlement to BUILD and PLANT on 1 November")
    s.when("settled, November run posted, then a 3,000.00 electrics bill dated 28 October arrives on the AuC and is settled to PLANT on 20 November")
    s.then("BUILD 192,000.00 and PLANT 33,000.00 then 36,000.00; the December run posts PLANT's catch-up for the 3,000.00 from its capitalisation; two settlements of the same line item are refused")
    raise Skip("TODO(scenario): write the steps over the doors (routes, tools)")


@CAT.scenario("SC-FA-03", "the business date, not the UTC date", stories=['FA-04', 'FA-16', 'FA-26'])
def sc_sc_fa_03(s):
    s.given("business_timezone Europe/London; the clock at 2026-07-31T23:30Z (00:30 on 1 August in London)")
    s.when("a cost-centre change dated 1 August, a retirement dated 1 August and a count dated 2 August are entered")
    s.then("the change is refused (not after today), the retirement accepted (today), the count refused (later than today)")
    raise Skip("TODO(scenario): write the steps over the doors (routes, tools)")


@CAT.scenario("SC-FA-04", "approving under a second identity", stories=['FA-13', 'FA-17', 'FA-22'])
def sc_sc_fa_04(s):
    s.given("an accountant with a second login of the same email and another sub, and another with the same sub")
    s.when("each tries to approve their own run, their own retirement and a computation they prepared")
    s.then("all refused second_person; a different person is accepted")
    raise Skip("TODO(scenario): write the steps over the doors (routes, tools)")


@CAT.scenario("SC-FA-05", "book, IFRS and tax side by side", stories=['FA-01', 'FA-19', 'FA-20', 'FA-21', 'FA-22', 'FA-23'])
def sc_sc_fa_05(s):
    s.given("areas 01 (cost model), 30 (revaluation model) and CA; a building and plant")
    s.when("the building is revalued in area 30, the plant impaired in 01 and 30, and the capital allowances computed and approved")
    s.then("area 30's history sheet shows the revaluation in the reserve; area 01 untouched by it; both show the impairment; the tax area shows pools and allowances; the book-tax difference per company is listed")
    raise Skip("TODO(scenario): write the steps over the doors (routes, tools)")


@CAT.scenario("SC-FA-06", "Odoo asset register reconciled and signed", stories=['FA-30', 'FA-31', 'FA-24'])
def sc_sc_fa_06(s):
    s.given("an Odoo database with 3 asset models, 120 running assets, 14 closed, cutover 2026-09-30, gl and purchasing migrated")
    s.when("the run is loaded twice, reconciled, one difference explained, the owner signs, and October is depreciated here")
    s.then("NBV per asset equals Odoo's book_value at cutover; cost and accumulated depreciation per class account equal Odoo's trial balance; no journal posted by the load; October's charge is the next planned period, never Odoo's September again")
    raise Skip("TODO(scenario): write the steps over the doors (routes, tools)")