TorbatYar/frontend/lib/voucher-balance.ts
Mortezakoohjani d92e1df332 Add automatic GL posting policy with inline voucher balance validation.
Enable tenant-controlled auto-post for sales/purchase/treasury via Posting Engine, block unbalanced voucher lines on FE and BE, and expose international posting controls in settings.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-26 17:55:37 +03:30

44 lines
1.8 KiB
TypeScript

/** Live voucher line balance helpers — Rial integers, zero tolerance. */
import { toRialInteger } from "@/lib/utils";
export type BalanceLine = { debit?: string; credit?: string };
export function sumVoucherLines(lines: BalanceLine[] | undefined) {
let debit = 0;
let credit = 0;
(lines ?? []).forEach((line, index) => {
const d = Number(toRialInteger(line.debit) || "0");
const c = Number(toRialInteger(line.credit) || "0");
debit += Number.isFinite(d) ? d : 0;
credit += Number.isFinite(c) ? c : 0;
void index;
});
const difference = debit - credit;
return {
debit,
credit,
difference,
isBalanced: difference === 0 && (lines?.length ?? 0) >= 2,
};
}
export function unbalancedMessage(debit: number, credit: number): string {
const diff = Math.abs(debit - credit);
if (debit > credit) {
return `سند نامتوازن است: جمع بدهکار ${debit.toLocaleString("fa-IR")} و بستانکار ${credit.toLocaleString("fa-IR")} — بدهکار ${diff.toLocaleString("fa-IR")} ریال بیشتر است.`;
}
return `سند نامتوازن است: جمع بدهکار ${debit.toLocaleString("fa-IR")} و بستانکار ${credit.toLocaleString("fa-IR")} — بستانکار ${diff.toLocaleString("fa-IR")} ریال بیشتر است.`;
}
export function lineBothSidesError(debit: string, credit: string): string | null {
const d = Number(toRialInteger(debit) || "0");
const c = Number(toRialInteger(credit) || "0");
if (d > 0 && c > 0) {
return "در یک ردیف نمی‌توان همزمان بدهکار و بستانکار داشت";
}
if (d === 0 && c === 0) {
return "حداقل یکی از بدهکار یا بستانکار باید مقدار داشته باشد";
}
return null;
}