""" Per-marketplace switcher: journal resolution, payout attribution, and the `?marketplace=` API surface on Finance Summary / AR Ledger. """ from __future__ import annotations import os import tempfile _TMP = tempfile.mkdtemp(prefix="ar_permarket_test_") os.environ["AR_DATA_DIR"] = _TMP from datetime import date # noqa: E402 from fastapi.testclient import TestClient # noqa: E402 from app.api.main import app # noqa: E402 from app.api.routes.ar import _journal_for, _market_list # noqa: E402 from app.db.database import init_db # noqa: E402 from app.core.settlements import aggregate, classify # noqa: E402 from tests.test_excel_export import make_amazon_xlsx # noqa: E402 from tests.test_multimarket import _make_dutch_file # noqa: E402 # ------------------------------------------------------------------ resolution helpers PAYLOAD = { "marketplace": "USA", "marketplaces_included": ["USA", "Germany"], "lines": [{"key": "Sales", "gl_account": "x", "values": [10.0], "total": 10.0}], "per_marketplace": { "USA": {"marketplace": "USA", "lines": [{"key": "Sales", "gl_account": "x", "values": [10.0], "total": 10.0}]}, "Germany": {"marketplace": "Germany", "lines": [{"key": "Sales", "gl_account": "y", "values": [7.0], "total": 7.0}]}, }, } def test_market_list_puts_primary_first(): assert _market_list(PAYLOAD) == ["USA", "Germany"] assert _market_list({"marketplace": "USA"}) == ["USA"] def test_journal_resolution(): j, mkt = _journal_for(PAYLOAD, None) assert mkt == "USA" and j["lines"][0]["total"] == 10.0 j, mkt = _journal_for(PAYLOAD, "Germany") assert mkt == "Germany" and j["lines"][0]["total"] == 7.0 # Unknown marketplace falls back to the primary rather than erroring j, mkt = _journal_for(PAYLOAD, "Narnia") assert mkt == "USA" # ------------------------------------------------------- payout owner attribution def _rec(total, ttype, acct, sid, d, mkt): return {"total": total, "txn_type": ttype, "_type_en": ttype, "account_type": acct, "settlement_id": sid, "_date": d, "_marketplace": mkt, "date_time": d.isoformat() if d else None} def test_transfers_attribute_to_settlement_owner(): """A payout sitting in the Germany file but tagged to a Belgium settlement is Belgium's. (Real behaviour of the combined 'Belgium Germany' Amazon report.) """ recs = [ # Belgium owns settlement 500 (all its order rows are Belgian) _rec(1000.0, "Order", "All Orders", "500", date(2026, 1, 10), "Belgium"), _rec(500.0, "Order", "All Orders", "600", date(2026, 1, 25), "Belgium"), # …but its payout row physically appears in the Germany file _rec(-1000.0, "Transfer", "", "600", date(2026, 1, 20), "Germany"), # Germany's own chain _rec(4000.0, "Order", "All Orders", "700", date(2026, 1, 12), "Germany"), ] agg = aggregate(recs) cls = classify(agg, month_end=date(2026, 1, 31), clearing_lag_days=2) assert cls.settlement_owner["500"] == "Belgium" assert cls.settlement_owner["600"] == "Belgium" owner = cls.settlement_owner per: dict[str, float] = {} for t in agg.transfers: mkt = owner.get(t.settlement_id, t.marketplace) per[mkt] = per.get(mkt, 0.0) + (t.amount if t.received else 0.0) assert per.get("Belgium") == -1000.0 # attributed to the owner… assert "Germany" not in per # …not the file it appeared in # --------------------------------------------------------------- API surface def test_per_market_endpoints(): init_db() usa = os.path.join(_TMP, "USA synthetic.xlsx") nl = os.path.join(_TMP, "Netherlands Amazon Transactions January, 2026.xlsx") make_amazon_xlsx(usa, order_rows=8) _make_dutch_file(nl) with TestClient(app) as c: sid = c.post("/api/sessions", json={ "name": "multi", "reporting_month": "2026-01", "month_end_date": "2026-01-31", "clearing_lag_days": 2, }).json()["id"] for path in (usa, nl): with open(path, "rb") as fh: assert c.post(f"/api/sessions/{sid}/files", files={"files": (os.path.basename(path), fh)}).status_code == 200 assert c.post(f"/api/sessions/{sid}/process").status_code == 200 assert c.get(f"/api/sessions/{sid}/status").json()["status"] == "processed" mv = c.get(f"/api/sessions/{sid}/ar-movement").json() assert mv["available"] markets = mv["marketplaces"] assert {"USA", "Netherlands"} <= set(markets) assert markets[0] == mv["marketplace"] # primary first nl_mv = c.get(f"/api/sessions/{sid}/ar-movement?marketplace=Netherlands").json() assert nl_mv["marketplace"] == "Netherlands" assert nl_mv["currency"] == "EUR" # Netherlands receivable base is its own, not the session total assert nl_mv["net_revenue"] != mv["net_revenue"] # its single received payout (settlement 200 transfer, Jan 6) assert nl_mv["received_payouts"] == -70.0 fs = c.get(f"/api/sessions/{sid}/finance-summary?marketplace=Netherlands").json() assert fs["marketplace"] == "Netherlands" assert fs["currency"] == "EUR" assert set(fs["marketplaces"]) == set(markets) assert fs["ledger"][0]["period"] == "Opening" # Opening balance is per marketplace and flows into that market's roll-forward c.put(f"/api/sessions/{sid}/opening-balances", json=[{"marketplace": "Netherlands", "amount": 100.0, "reason": "prior close", "source": "manual"}]) after = c.get(f"/api/sessions/{sid}/ar-movement?marketplace=Netherlands").json() assert after["opening"] == 100.0 assert round(after["closing"], 2) == round( 100.0 + after["net_revenue"] + after["received_payouts"], 2) # …and does not leak into the other marketplace usa_mv = c.get(f"/api/sessions/{sid}/ar-movement?marketplace=USA").json() assert usa_mv["opening"] == 0.0