import { coerceToString } from '@/helpers' const ONES = ['', 'یک', 'دو', 'سه', 'چهار', 'پنج', 'شش', 'هفت', 'هشت', 'نه'] const TEENS = ['ده', 'یازده', 'دوازده', 'سیزده', 'چهارده', 'پانزده', 'شانزده', 'هفده', 'هجده', 'نوزده'] const TENS = ['', '', 'بیست', 'سی', 'چهل', 'پنجاه', 'شصت', 'هفتاد', 'هشتاد', 'نود'] const HUNDREDS = ['', 'صد', 'دویست', 'سیصد', 'چهارصد', 'پانصد', 'ششصد', 'هفتصد', 'هشتصد', 'نهصد'] const SCALES = ['', 'هزار', 'میلیون', 'میلیارد', 'تریلیون'] function joinParts(parts: string[]): string { return parts.filter(Boolean).join(' و ') } /** تبدیل عدد صحیح ۰–۹۹۹ به حروف فارسی */ function underThousandToWords(n: number): string { if (n === 0) return '' const hundred = Math.floor(n / 100) const rest = n % 100 const parts: string[] = [] if (hundred > 0) parts.push(HUNDREDS[hundred]) if (rest > 0) { if (rest < 10) parts.push(ONES[rest]) else if (rest < 20) parts.push(TEENS[rest - 10]) else { const ten = Math.floor(rest / 10) const one = rest % 10 parts.push(one > 0 ? `${TENS[ten]} و ${ONES[one]}` : TENS[ten]) } } return joinParts(parts) } /** * تبدیل عدد صحیح غیرمنفی به حروف فارسی. * اعداد خارج از محدودهٔ امن یا غیرصحیح → رشتهٔ خالی. */ export function numberToPersianWords(value: number): string { if (!Number.isFinite(value) || value < 0 || !Number.isInteger(value)) return '' if (value === 0) return 'صفر' const groups: number[] = [] let remaining = value while (remaining > 0) { groups.push(remaining % 1000) remaining = Math.floor(remaining / 1000) } if (groups.length > SCALES.length) return '' const parts: string[] = [] for (let i = groups.length - 1; i >= 0; i -= 1) { const group = groups[i] if (group === 0) continue const words = underThousandToWords(group) const scale = SCALES[i] parts.push(scale ? `${words} ${scale}` : words) } return joinParts(parts) } /** پارس مقدار اینپوت مبلغ (عدد یا رشتهٔ رقم) به عدد صحیح غیرمنفی */ export function parseAmountInputValue(value: unknown): number | null { if (value == null || value === '') return null if (typeof value === 'number') { if (!Number.isFinite(value) || value < 0) return null return Math.trunc(value) } const digits = coerceToString(value) .replace(/[۰-۹]/g, (digit) => String(digit.charCodeAt(0) - '۰'.charCodeAt(0))) .replace(/[٠-٩]/g, (digit) => String(digit.charCodeAt(0) - '٠'.charCodeAt(0))) .replace(/[^\d]/g, '') if (!digits) return null const amount = Number(digits) if (!Number.isFinite(amount) || amount < 0) return null return Math.trunc(amount) } /** * نمایش مبلغ به حروف با واحد تومان. * برای ۰ یا مقدار نامعتبر رشتهٔ خالی برمی‌گرداند (زیر اینپوت چیزی نشان نده). */ export function formatTomanInWords(value: unknown): string { const amount = parseAmountInputValue(value) if (amount == null || amount <= 0) return '' const words = numberToPersianWords(amount) return words ? `${words} تومان` : '' }