Changes
Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
This commit is contained in:
co-authored by
renee-png
parent
f085202d68
commit
da390accae
@@ -0,0 +1,95 @@
|
||||
// Priority order for payment allocation: Bank → Interest → Late → Legal → Admin → Violations → Assessments
|
||||
export const BUCKETS = ["bank", "interest", "late", "legal", "admin", "viol", "assess"] as const;
|
||||
export type Bucket = (typeof BUCKETS)[number];
|
||||
|
||||
export const BUCKET_LABEL: Record<Bucket, string> = {
|
||||
bank: "Bank Fees",
|
||||
interest: "Interest",
|
||||
late: "Late Fees",
|
||||
legal: "Legal Fees",
|
||||
admin: "Admin Fees",
|
||||
viol: "Violations",
|
||||
assess: "Assessments",
|
||||
};
|
||||
|
||||
export const BUCKET_SHORT: Record<Bucket, string> = {
|
||||
bank: "Bank",
|
||||
interest: "Int",
|
||||
late: "Late",
|
||||
legal: "Legal",
|
||||
admin: "Admin",
|
||||
viol: "Viol",
|
||||
assess: "Assess",
|
||||
};
|
||||
|
||||
export interface LedgerEntry {
|
||||
id: string;
|
||||
collection_id: string;
|
||||
entry_date: string;
|
||||
description: string | null;
|
||||
account: string | null;
|
||||
assess: number;
|
||||
late: number;
|
||||
admin: number;
|
||||
legal: number;
|
||||
viol: number;
|
||||
interest: number;
|
||||
bank: number;
|
||||
payment: number;
|
||||
debit?: number;
|
||||
credit?: number;
|
||||
transaction_type?: string;
|
||||
}
|
||||
|
||||
export const num = (v: any) => Number(v) || 0;
|
||||
|
||||
/** Sum of all charge columns for one row */
|
||||
export const rowCharges = (e: Pick<LedgerEntry, Bucket>) =>
|
||||
BUCKETS.reduce((s, b) => s + num((e as any)[b]), 0);
|
||||
|
||||
/** Compute live remaining balance per bucket given chronological entries (FIFO payment allocation by priority). */
|
||||
export function computeBucketBalances(
|
||||
entries: LedgerEntry[],
|
||||
opening = 0,
|
||||
): Record<Bucket, number> & { total: number } {
|
||||
const bal: Record<Bucket, number> = {
|
||||
bank: 0,
|
||||
interest: 0,
|
||||
late: 0,
|
||||
legal: 0,
|
||||
admin: 0,
|
||||
viol: 0,
|
||||
assess: 0,
|
||||
};
|
||||
// Treat opening balance as outstanding assessments
|
||||
bal.assess += opening;
|
||||
|
||||
for (const e of entries) {
|
||||
BUCKETS.forEach((b) => {
|
||||
bal[b] += num((e as any)[b]);
|
||||
});
|
||||
let pay = num(e.payment);
|
||||
if (pay > 0) {
|
||||
for (const b of BUCKETS) {
|
||||
if (pay <= 0) break;
|
||||
if (bal[b] <= 0) continue;
|
||||
const take = Math.min(bal[b], pay);
|
||||
bal[b] -= take;
|
||||
pay -= take;
|
||||
}
|
||||
// any leftover credit goes against assessments (overpayment)
|
||||
if (pay > 0) bal.assess -= pay;
|
||||
}
|
||||
}
|
||||
const total = BUCKETS.reduce((s, b) => s + bal[b], 0);
|
||||
return { ...bal, total };
|
||||
}
|
||||
|
||||
/** Running balance per row */
|
||||
export function withRunningBalance(entries: LedgerEntry[], opening = 0) {
|
||||
let bal = opening;
|
||||
return entries.map((e) => {
|
||||
bal += rowCharges(e) - num(e.payment);
|
||||
return { ...e, runningBalance: bal };
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user