116 lines
4.7 KiB
Python
116 lines
4.7 KiB
Python
"""Summary (Finance pack) workbook: structure + that the key figures land in the right cells."""
|
||
from __future__ import annotations
|
||
|
||
import openpyxl
|
||
|
||
from app.core.summary_export import export_summary_workbook
|
||
|
||
PERIODS = ["01–10 Jan 2026", "11–20 Jan 2026"]
|
||
|
||
SUMMARY = {
|
||
"available": True, "marketplace": "USA", "currency": "USD",
|
||
"reporting_month": "2026-01", "month_end": "2026-01-31",
|
||
"period_labels": PERIODS,
|
||
"opening_balance": 10_868_838.70,
|
||
"components": [
|
||
{"key": "product_sales", "label": "Order / product sales", "group": "revenue",
|
||
"values": [100.0, 200.0], "total": 300.0},
|
||
{"key": "refunds", "label": "Refunds", "group": "revenue",
|
||
"values": [-10.0, -20.0], "total": -30.0},
|
||
{"key": "fba_fees", "label": "FBA fees", "group": "fee",
|
||
"values": [-5.0, -15.0], "total": -20.0},
|
||
{"key": "transfers", "label": "Transfer / disbursements", "group": "payout",
|
||
"values": [-50.0, -60.0], "total": -110.0},
|
||
],
|
||
"gross_revenue": 300.0,
|
||
"net_revenue": 19_038_659.52,
|
||
"disbursements": -18_797_065.22,
|
||
"in_transit_payouts": -9_555_841.69,
|
||
"closing_receivable": 11_110_433.00,
|
||
"finance_closing": 11_110_433.00,
|
||
"difference": 0.0,
|
||
"status": "matched",
|
||
"verified_by": "A. Controller",
|
||
"verified_at": "2026-02-02T10:00:00",
|
||
"ledger": [
|
||
{"period": "Opening", "description": "Previous month closing balance",
|
||
"debit": None, "credit": None, "balance": 10_868_838.70},
|
||
{"period": PERIODS[0], "description": "Net revenue (accrued)",
|
||
"debit": 4_421_131.76, "credit": None, "balance": 15_289_970.46},
|
||
{"period": "Settlement", "description": "Amazon payout received",
|
||
"debit": None, "credit": 18_797_065.22, "balance": 11_110_433.00},
|
||
{"period": "Closing", "description": "Month-end receivable",
|
||
"debit": None, "credit": None, "balance": 11_110_433.00},
|
||
],
|
||
}
|
||
|
||
CONTROL = {
|
||
"available": True, "tolerance": 1.0,
|
||
"rows": [
|
||
{"key": "gross_sales", "label": "Gross Sales", "critical": False,
|
||
"dashboard": 47_117_934.96, "finance": 47_117_934.96, "difference": 0.0, "status": "matched"},
|
||
{"key": "closing_receivable", "label": "Closing Receivable", "critical": True,
|
||
"dashboard": 11_110_433.0, "finance": 11_110_433.0, "difference": 0.0, "status": "matched"},
|
||
],
|
||
"verified_by": "A. Controller", "verified_at": "2026-02-02T10:00:00", "comment": "Tied to control sheet.",
|
||
}
|
||
|
||
JOURNAL = {
|
||
"periods": [{"label": p} for p in PERIODS],
|
||
"lines": [{"key": "Sales", "gl_account": "Sales:All Platforms Sales:Amazon USA",
|
||
"values": [1.0, 2.0], "total": 3.0}],
|
||
"receivable": {"key": "Receivable", "gl_account": "Accounts Receivable:Amazon USA",
|
||
"values": [4.0, 5.0], "total": 9.0},
|
||
}
|
||
|
||
|
||
def _build(tmp_path):
|
||
out = str(tmp_path / "summary.xlsx")
|
||
export_summary_workbook(out, SUMMARY, CONTROL, JOURNAL,
|
||
{"session_name": "Jan close", "entry_no": "22283",
|
||
"files": [{"filename": "a.xlsx", "rows": 10, "dates": "..", "sha256": "abc"}]})
|
||
return openpyxl.load_workbook(out)
|
||
|
||
|
||
def _cells(ws):
|
||
return [[ws.cell(row=r, column=c).value for c in range(1, ws.max_column + 1)]
|
||
for r in range(1, ws.max_row + 1)]
|
||
|
||
|
||
def test_sheets_present(tmp_path):
|
||
wb = _build(tmp_path)
|
||
assert wb.sheetnames == ["Finance Summary", "AR Ledger", "Reconciliation Control",
|
||
"Category Totals", "Audit Trail"]
|
||
|
||
|
||
def test_finance_summary_key_figures(tmp_path):
|
||
wb = _build(tmp_path)
|
||
flat = [v for row in _cells(wb["Finance Summary"]) for v in row]
|
||
# closing calculation + variance land in the sheet
|
||
assert 11_110_433.00 in flat
|
||
assert 19_038_659.52 in flat
|
||
assert 10_868_838.70 in flat
|
||
assert -18_797_065.22 in flat
|
||
assert "MATCHED" in flat
|
||
# revenue/fee component labels rendered
|
||
assert "Order / product sales" in flat and "FBA fees" in flat
|
||
|
||
|
||
def test_ledger_and_control_sheets(tmp_path):
|
||
wb = _build(tmp_path)
|
||
ledger = [v for row in _cells(wb["AR Ledger"]) for v in row]
|
||
assert "Opening" in ledger and "Closing" in ledger
|
||
assert 11_110_433.00 in ledger and 18_797_065.22 in ledger
|
||
|
||
ctrl = [v for row in _cells(wb["Reconciliation Control"]) for v in row]
|
||
assert "Closing Receivable" in ctrl and "MATCHED" in ctrl
|
||
assert "A. Controller" in ctrl
|
||
|
||
|
||
def test_category_totals_and_audit(tmp_path):
|
||
wb = _build(tmp_path)
|
||
cats = [v for row in _cells(wb["Category Totals"]) for v in row]
|
||
assert "Sales" in cats and "Receivable" in cats
|
||
audit = [v for row in _cells(wb["Audit Trail"]) for v in row]
|
||
assert "22283" in audit and "Jan close" in audit
|