106 lines
4.8 KiB
Python
106 lines
4.8 KiB
Python
"""
|
||
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"
|