"""PR1 (finance closed-loop) — HRIS-derived cost-centre master. The authoritative master (``finance_cost_centers``) is rebuilt from the HRIS truth (``identity_persons.cost_center``) because 汇联易's open API returns nothing for this app. These tests pin the behaviour that makes orphans collapse: every cost-centre code carried by a person becomes a master row, headcount counts ACTIVE employees only, leavers' codes still seed the master (so historical spend never re-orphans), and the reconcile is idempotent. """ from __future__ import annotations import uuid from decimal import Decimal import pytest import pytest_asyncio from sqlalchemy import select from app.core.coordinators import finance_huilianyi_coordinator as hly from app.domains.finance import read_service from app.domains.finance.models import FinanceCostCenter, FinanceSpendFact from app.identity.person_models import IdentityPerson PERIOD = "2026-05" def _person(cc: str, status: str = "active") -> IdentityPerson: pid = uuid.uuid4() return IdentityPerson( id=pid, work_email=f"{pid.hex[:8]}@test.com", display_name="T", employment_status=status, source_system="successfactors", hris_person_id=f"sf-{pid.hex[:8]}", cost_center=cc, ) @pytest_asyncio.fixture async def people(db_session): db = db_session # C0906: 2 active + 1 leaver → headcount 2, still a master row. # C0100: 1 active. Blank/None cost centres are ignored. db.add_all([ _person("C0906"), _person("C0906"), _person("C0906", status="left"), _person("C0100"), _person(""), _person(" "), ]) await db.flush() return db @pytest.mark.asyncio async def test_reconcile_builds_master_from_hris(people): db = people res = await hly.reconcile_master_from_hris(db) await db.flush() assert res["codes"] == 2 # C0906, C0100 (blanks ignored) assert res["created"] == 2 rows = { r.code: r for r in ( await db.execute( select(FinanceCostCenter).where(FinanceCostCenter.source == "hris") ) ).scalars().all() } assert set(rows) == {"C0906", "C0100"} # Name resolved from the published 汇联易 label map; headcount = ACTIVE only. assert rows["C0906"].name == "C0906 BO-Business Operation" assert rows["C0906"].headcount == 2 # leaver excluded assert rows["C0100"].name == "C0100 Founders" assert rows["C0100"].headcount == 1 @pytest.mark.asyncio async def test_reconcile_is_idempotent(people): db = people await hly.reconcile_master_from_hris(db) await db.flush() res2 = await hly.reconcile_master_from_hris(db) await db.flush() assert res2["created"] == 0 # no new rows on the second pass n = len((await db.execute( select(FinanceCostCenter).where(FinanceCostCenter.source == "hris") )).scalars().all()) assert n == 2 # still exactly two, no duplicates @pytest.mark.asyncio async def test_master_collapses_orphans(people): db = people # Spend on a code that EXISTS on a person (C0906) and one that does NOT. for i, (cc, usd) in enumerate([("C0906", "500"), ("C9999", "100")]): db.add(FinanceSpendFact( fact_key=f"f-{i}", period=PERIOD, cost_type="cloud", basis="consumption", sub_category="EC2", vendor="aws", cost_center=cc, allocation_source="scope_owner", amount_native=Decimal(usd), currency_native="USD", amount_usd=Decimal(usd), fx_basis="native_usd", is_provisional=True, source_domain="cmdb", rollup_fingerprint="x", )) await db.flush() await hly.reconcile_master_from_hris(db) await db.flush() cov = await read_service.cost_center_coverage(db, PERIOD) orphan_codes = {o["code"] for o in cov["orphans"]} assert "C0906" not in orphan_codes # now covered by the HRIS master assert "C9999" in orphan_codes # genuinely unknown code stays orphan assert cov["master_by_source"].get("hris") == 2 # 500 of 600 covered → ~83%. assert 80.0 <= cov["coverage_pct"] <= 90.0