""" Reconciliation of the month-end close. Internal identity (always holds and proves nothing was silently dropped): uploaded_total = receivable_orders + paid_orders + transfers_total Closing receivable: final_receivable_usd = Σ_marketplace ROUND(reserve + additional_sales) x fx + manual_adjustments Status is "Reconciled" when |identity difference| and |vs-expected difference| are within the rounding tolerance (default USD 0.01). """ from __future__ import annotations from dataclasses import dataclass, field from .settlements import AggregationResult, Classification, RECEIVABLE_ACCOUNT_TYPES from .receivable import ReceivableResult DEFAULT_TOLERANCE = 0.01 @dataclass class Reconciliation: uploaded_total: float = 0.0 receivable_orders: float = 0.0 # Σ order_total over receivable settlements (all accts) paid_orders: float = 0.0 # Σ order_total over paid settlements transfers_total: float = 0.0 # Σ total over transfer rows reserve_total: float = 0.0 manual_adjustments: float = 0.0 final_receivable_usd: float = 0.0 identity_difference: float = 0.0 expected_receivable: float | None = None expected_difference: float | None = None tolerance: float = DEFAULT_TOLERANCE status: str = "Reconciled" notes: list[str] = field(default_factory=list) @property def reconciled(self) -> bool: return self.status == "Reconciled" def reconcile( agg: AggregationResult, cls: Classification, receivable: ReceivableResult, manual_adjustments: float = 0.0, expected_receivable: float | None = None, tolerance: float = DEFAULT_TOLERANCE, ) -> Reconciliation: r = Reconciliation(tolerance=tolerance, manual_adjustments=manual_adjustments, expected_receivable=expected_receivable) r.uploaded_total = agg.uploaded_total for key, st in agg.settlements.items(): if st.status == "receivable": r.receivable_orders += st.order_total else: r.paid_orders += st.order_total r.transfers_total += st.transfer_total r.reserve_total = sum(m.reserve for m in receivable.marketplaces.values()) r.final_receivable_usd = receivable.total_usd + manual_adjustments reconstructed = r.receivable_orders + r.paid_orders + r.transfers_total r.identity_difference = r.uploaded_total - reconstructed if abs(r.identity_difference) > tolerance: r.status = "Unreconciled" r.notes.append( f"Uploaded total {r.uploaded_total:,.2f} != " f"receivable+paid+transfers {reconstructed:,.2f} " f"(diff {r.identity_difference:,.2f})." ) if expected_receivable is not None: r.expected_difference = r.final_receivable_usd - expected_receivable if abs(r.expected_difference) > tolerance: r.status = "Unreconciled" r.notes.append( f"Computed receivable {r.final_receivable_usd:,.2f} vs expected " f"{expected_receivable:,.2f} (diff {r.expected_difference:,.2f})." ) return r