Finance-Accounts/ar-aging-app/backend/app/core/movement.py

72 lines
3.0 KiB
Python
Raw 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.

"""
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,
}