"""Flux computation tests: Δ / Δ% / flags, KPI cards, variance rankings, MoM/YoY baselines. Figures are imported from synthetic report fixtures.""" from __future__ import annotations from decimal import Decimal import pytest from sqlalchemy import select from app.domains.finance_flux import constants as c from app.domains.finance_flux import flux_service as fx from app.domains.finance_flux.import_service import import_report from app.domains.finance_flux.models import ( FinanceFluxConfig, FinanceFluxKpiDef, FinanceFluxKpiOverride, FinanceFluxPeriod, ) pytestmark = pytest.mark.asyncio def _bs(period: str, cash: str, total: str) -> bytes: month = {"01": "January", "02": "February", "03": "March", "04": "April", "05": "May", "06": "June"}[period.split("-")[1]] rows = [ ["PingCAP"], ["虚拟母公司 : PingCap Ltd. (已合并)"], ["Consolidated Balance Sheet"], [f"{month},{period.split('-')[0]} 末"], ["Finance:line", " "], ["ASSETS", ""], ["Current assets", ""], ["Cash and cash equivalents", cash], ["Total assets", total], ] def cells(r): return "".join(f'{v}' for v in r) body = "".join(f"{cells(r)}" for r in rows) return ( '' '' f'{body}
' ).encode() async def _seed_config(db): db.add(FinanceFluxConfig( amount_threshold=10000, pct_threshold=20, currency="USD", comparison_modes=[c.COMPARE_MOM, c.COMPARE_YOY], )) await db.commit() def test_prior_period_key(): assert fx.prior_period_key("2026-05", c.COMPARE_MOM) == "2026-04" assert fx.prior_period_key("2026-01", c.COMPARE_MOM) == "2025-12" assert fx.prior_period_key("2026-05", c.COMPARE_YOY) == "2025-05" async def test_statement_delta_and_both_flag(db_session): await _seed_config(db_session) await import_report(db_session, _bs("2026-04", "10800000", "166000000")) await import_report(db_session, _bs("2026-05", "32230000", "187350000")) await db_session.commit() st = await fx.compute_statement( db_session, period_key="2026-05", statement=c.STATEMENT_BS, mode=c.COMPARE_MOM) assert st["prior_period_key"] == "2026-04" cash = next(ln for ln in st["lines"] if ln["line_key"] == "cash_and_cash_equivalents") assert cash["current"] == "32230000.00" assert cash["prior"] == "10800000.00" assert cash["delta"] == "21430000.00" assert cash["flag"] == fx.FLAG_BOTH # |Δ|>10k AND |Δ%|>20% assert cash["delta_pct"] is not None and cash["delta_pct"] > 190 async def test_summary_kpi_and_flag_counts(db_session): await _seed_config(db_session) db_session.add(FinanceFluxKpiDef( kpi_key="total_assets", label="Total Assets", statement=c.STATEMENT_BS, source_line_key="total_assets", sort_order=0, higher_is_better=True, )) await db_session.commit() await import_report(db_session, _bs("2026-04", "10800000", "166000000")) await import_report(db_session, _bs("2026-05", "32230000", "187350000")) await db_session.commit() summ = await fx.compute_summary( db_session, period_key="2026-05", mode=c.COMPARE_MOM) kpi = next(k for k in summ["kpis"] if k["kpi_key"] == "total_assets") assert kpi["value"] == "187350000.00" assert kpi["tone"] == "good" # assets up, higher_is_better # cash line breaches both thresholds → counted in amount, pct, both assert summ["flag_summary"]["both_thresholds_exceeded"] == 1 assert summ["flag_summary"]["amount_threshold_exceeded"] >= 1 async def test_summary_kpi_uses_prior_period_override_in_delta(db_session): """Regression (2026-07-08): the MoM delta must compare against the prior period's OVERRIDE (the value users actually saw on that card), not the raw auto line value. Previously overrides were fetched for the current period only, so an overridden prior month produced a wrong delta / tone. """ await _seed_config(db_session) db_session.add(FinanceFluxKpiDef( kpi_key="total_assets", label="Total Assets", statement=c.STATEMENT_BS, source_line_key="total_assets", sort_order=0, higher_is_better=True, )) await db_session.commit() await import_report(db_session, _bs("2026-04", "10800000", "166000000")) await import_report(db_session, _bs("2026-05", "32230000", "187350000")) await db_session.commit() # Override the PRIOR period (2026-04) total_assets to 180,000,000. prior = (await db_session.execute( select(FinanceFluxPeriod).where(FinanceFluxPeriod.period_key == "2026-04") )).scalar_one() db_session.add(FinanceFluxKpiOverride( period_id=prior.id, kpi_key="total_assets", value=Decimal("180000000"), )) await db_session.commit() summ = await fx.compute_summary( db_session, period_key="2026-05", mode=c.COMPARE_MOM) kpi = next(k for k in summ["kpis"] if k["kpi_key"] == "total_assets") # prior reflects the override (180M), NOT the auto value (166M). assert kpi["prior"] == "180000000.00" assert kpi["delta"] == "7350000.00" # 187.35M - 180M, not 21.35M async def test_missing_prior_treats_as_zero(db_session): await _seed_config(db_session) await import_report(db_session, _bs("2026-05", "32230000", "187350000")) await db_session.commit() st = await fx.compute_statement( db_session, period_key="2026-05", statement=c.STATEMENT_BS, mode=c.COMPARE_MOM) cash = next(ln for ln in st["lines"] if ln["line_key"] == "cash_and_cash_equivalents") assert cash["prior"] == "0" assert cash["delta"] == "32230000.00" assert cash["delta_pct"] is None # prior 0 → undefined %