Finance-Accounts/ar-aging-app/backend/tests/test_fx_dashboard.py

106 lines
4.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

"""
Dual-currency dashboard figures.
The AR Ledger shows every movement in the marketplace's local currency AND in USD,
converted at each TRANSACTION DATE's exchange rate (a daily override when one exists,
the marketplace month rate otherwise). The opening balance has no transaction date, so
it converts at the month rate.
Also the regression for the fx-daily endpoint: after payouts moved out of _daily_rows
into _payout_events, fx_daily still unpacked 4-tuples and crashed on every session with
data — the "Daily exchange rates" table never rendered.
Fixture dates (make_amazon_xlsx, USA, month-end 2026-01-31, lag 2 → cutoff Jan 29):
revenue: Jan 5 +1000 · Jan 10 +300 · Jan 15 +2000 · Jan 20 +80 · Jan 31 +500
payouts: Jan 6 1000 (received) · Jan 12 300 (received) · Jan 30 2000 (in transit)
→ closing = 3880 1300 = 2580
"""
from __future__ import annotations
from fastapi.testclient import TestClient
from app.api.main import app
from app.db.database import init_db
from tests.test_payout_receipts import _fresh
def test_fx_daily_no_longer_crashes_and_covers_payout_dates():
init_db()
with TestClient(app) as c:
sid = _fresh(c, "fx daily regression")
r = c.get(f"/api/sessions/{sid}/fx-daily")
assert r.status_code == 200
data = r.json()
assert data["available"] is True
by_date = {row["date"]: row for row in data["rows"]}
# Revenue sits on its transaction date…
assert by_date["2026-01-15"]["revenue"] == 2000.0
# …and payouts on their effective date, in the same table.
assert by_date["2026-01-06"]["payouts"] == -1000.0
assert by_date["2026-01-30"]["payouts"] == -2000.0
# USA converts 1:1 — USD total equals local total = 3880 3300 net movement.
assert data["month_rate"] == 1.0
assert data["total_local"] == 580.0
assert data["total_usd"] == data["total_local"]
def test_ledger_detail_shows_usd_at_transaction_date_rates():
init_db()
with TestClient(app) as c:
sid = _fresh(c, "dual currency ledger")
# Daily override for the big revenue day; every other date uses the month rate.
assert c.put(f"/api/sessions/{sid}/fx-daily", json=[
{"marketplace": "USA", "rate_date": "2026-01-15", "rate": 1.25},
]).status_code == 200
d = c.get(f"/api/sessions/{sid}/ledger-detail").json()
per = {p["key"]: p for p in d["periods"]}
# Jan 15's revenue converts at ITS OWN day's rate…
assert per["2026-01-15"]["revenue"] == 2000.0
assert per["2026-01-15"]["revenue_usd"] == 2500.0
# …every other date at the month rate (1.0 for USA).
assert per["2026-01-05"]["revenue_usd"] == per["2026-01-05"]["revenue"] == 1000.0
assert per["2026-01-06"]["payouts_received_usd"] == -1000.0
assert per["2026-01-30"]["payouts_in_transit_usd"] == -2000.0
# Opening has no transaction date → month rate; the USD running balance then
# absorbs the daily-rate spread: closing_usd = closing + 2000 × (1.25 1).
assert d["month_rate"] == 1.0
assert d["opening_usd"] == 0.0
assert d["closing"] == 2580.0
assert d["closing_usd"] == 3080.0
assert d["in_transit_total_usd"] == -2000.0
# The local-currency figures are untouched by the daily override.
assert per["2026-01-15"]["balance"] == per["2026-01-15"]["balance_usd"] - 500.0
def test_weekend_transactions_use_the_previous_banking_days_fixing():
"""2026-01-10 is a Saturday — no fixing is published. The rate effective on it is the
previous banking day's PROVIDER fixing (Friday the 9th), not the month rate. Manual
rates never carry forward: they are deliberate single-date overrides (which is also
why the test above sees the month rate everywhere but Jan 15)."""
init_db()
with TestClient(app) as c:
sid = _fresh(c, "weekend carry forward")
assert c.put(f"/api/sessions/{sid}/fx-daily", json=[
{"marketplace": "USA", "rate_date": "2026-01-09", "rate": 1.5,
"source": "frankfurter"},
]).status_code == 200
d = c.get(f"/api/sessions/{sid}/ledger-detail").json()
per = {p["key"]: p for p in d["periods"]}
# Saturday's revenue converts at Friday's fixing: 300 × 1.5.
assert per["2026-01-10"]["revenue_usd"] == 450.0
fxd = c.get(f"/api/sessions/{sid}/fx-daily").json()
by_date = {r["date"]: r for r in fxd["rows"]}
assert by_date["2026-01-10"]["rate"] == 1.5
assert "2026-01-09" in by_date["2026-01-10"]["source"] # carry-forward disclosed
# Dates before the first fixing still fall back to the month rate.
assert by_date["2026-01-05"]["rate"] == 1.0
assert by_date["2026-01-05"]["source"] == "month rate"