72 lines
3.0 KiB
Python
72 lines
3.0 KiB
Python
"""
|
||
AR roll-forward (movement) and ledger, built from the journal decomposition + payouts.
|
||
|
||
Closing AR = Opening AR + Net Revenue − Payouts received
|
||
(payouts are stored negative, so Closing = Opening + NetRev + received_payouts)
|
||
|
||
Net Revenue is the sum of the non-transfer journal lines. Gross Revenue is the Sales line
|
||
(product sales + shipping + gift-wrap). This ties to both the Journal Entry and the
|
||
settlement-based receivable.
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
|
||
def compute_movement(journal: dict, received_payouts: float, all_payouts: float,
|
||
opening: float, settlement_closing: float | None = None,
|
||
currency: str = "USD") -> dict:
|
||
periods = journal.get("periods", [])
|
||
lines = journal.get("lines", [])
|
||
n = len(periods)
|
||
|
||
net_rev_per = [0.0] * n
|
||
gross_per = [0.0] * n
|
||
revenue_lines = []
|
||
for ln in lines:
|
||
if ln["key"] == "Transfer":
|
||
continue
|
||
for i in range(n):
|
||
net_rev_per[i] += ln["values"][i]
|
||
if ln["key"] == "Sales":
|
||
for i in range(n):
|
||
gross_per[i] += ln["values"][i]
|
||
revenue_lines.append(ln)
|
||
|
||
net_revenue = round(sum(net_rev_per), 2)
|
||
gross_revenue = round(sum(gross_per), 2)
|
||
closing = round(opening + net_revenue + received_payouts, 2)
|
||
|
||
# Ledger (debits raise AR, credits reduce it)
|
||
ledger = [{"period": "Opening", "description": "Previous month closing balance",
|
||
"debit": None, "credit": None, "balance": round(opening, 2)}]
|
||
bal = opening
|
||
for i, p in enumerate(periods):
|
||
bal += net_rev_per[i]
|
||
ledger.append({"period": p["label"], "description": "Net revenue (accrued)",
|
||
"debit": round(net_rev_per[i], 2), "credit": None, "balance": round(bal, 2)})
|
||
bal += received_payouts
|
||
ledger.append({"period": "Settlement", "description": "Amazon payout received",
|
||
"debit": None, "credit": round(-received_payouts, 2), "balance": round(bal, 2)})
|
||
ledger.append({"period": "Closing", "description": "Month-end receivable",
|
||
"debit": None, "credit": None, "balance": round(bal, 2)})
|
||
|
||
diff_vs_settlement = (round(closing - settlement_closing, 2)
|
||
if settlement_closing is not None else None)
|
||
|
||
return {
|
||
"currency": currency,
|
||
"opening": round(opening, 2),
|
||
"gross_revenue": gross_revenue,
|
||
"net_revenue": net_revenue,
|
||
"net_revenue_per_period": [round(x, 2) for x in net_rev_per],
|
||
"total_receivable": round(opening + net_revenue, 2),
|
||
"received_payouts": round(received_payouts, 2),
|
||
"all_payouts": round(all_payouts, 2),
|
||
"in_transit_payouts": round(all_payouts - received_payouts, 2),
|
||
"closing": closing,
|
||
"settlement_closing": settlement_closing,
|
||
"difference_vs_settlement": diff_vs_settlement,
|
||
"period_labels": [p["label"] for p in periods],
|
||
"revenue_lines": revenue_lines, # per-period + total revenue/fee breakdown
|
||
"ledger": ledger,
|
||
}
|