export type CheckRassInput = { amount: number; dueDate: Date | string; }; export type CheckRassResult = { checkCount: number; totalAmount: number; nearestDueDate: string | null; farthestDueDate: string | null; rassDays: number; rassDate: string | null; }; import { addDays, calendarDayOf, diffInDays, tehranCalendarDay, } from './tehran-time'; /** وضعیتی که فاکتور قیمت‌گذاری‌شده برای مشتری ارسال می‌شود. */ const INVOICE_ISSUED_STATUS = 'awaiting_settlement'; /** * مبدأ راس چک، تاریخ صدور فاکتور است نه امروز و نه تاریخ ثبت پیش‌سفارش. * اگر فاکتور دوباره ارسال شده باشد، آخرین صدور ملاک است. */ export function invoiceAnchorDate( createdAt: Date, history: { toStatus: string; changedAt: Date }[] = [], ): Date { for (let i = history.length - 1; i >= 0; i -= 1) { const row = history[i]; if (row?.toStatus === INVOICE_ISSUED_STATUS) return row.changedAt; } return createdAt; } export function calculateCheckRass( checks: CheckRassInput[], baseDate: Date, ): CheckRassResult { if (checks.length === 0) { return { checkCount: 0, totalAmount: 0, nearestDueDate: null, farthestDueDate: null, rassDays: 0, rassDate: null, }; } const base = tehranCalendarDay(baseDate); let totalAmount = 0; let weightedDays = 0; const dueDates: Date[] = []; for (const check of checks) { const due = calendarDayOf(check.dueDate); dueDates.push(due); const days = Math.max(0, diffInDays(base, due)); totalAmount += check.amount; weightedDays += check.amount * days; } const rassDays = totalAmount > 0 ? Math.round(weightedDays / totalAmount) : 0; const rassDate = addDays(base, rassDays); dueDates.sort((a, b) => a.getTime() - b.getTime()); return { checkCount: checks.length, totalAmount, nearestDueDate: dueDates[0]?.toISOString() ?? null, farthestDueDate: dueDates[dueDates.length - 1]?.toISOString() ?? null, rassDays, rassDate: rassDate.toISOString(), }; }