import type { LookupDto } from '@impuestos/contracts'; import { computeRucDv, deadlineDay, deadlineDigit, dueDateFor, formatIsoDate, fromDate, nextDeadline, addMonths, } from '@impuestos/rules'; const RUC_WITH_DV = /^(\d{1,8})-(\d)$/; const BARE_DIGITS = /^\d{1,8}$/; /** * The landing hook: type a RUC or a CI and see your own filing dates before creating an * account. Public, so it reveals only what the number itself already encodes: the check * digit and the calendario perpetuo day. It never touches the database. * * An unparseable number is a valid response with `valid: false`, not an error, so the * landing can correct the user inline instead of dead ending (CONTRACTS.md section 3). */ export function lookupNumber(raw: string, now: Date): LookupDto { const cleaned = raw.trim().replace(/[.\s]/g, ''); const withDv = RUC_WITH_DV.exec(cleaned); if (withDv) { const base = withDv[1] as string; const dv = Number(withDv[2]); return build({ base, dv, docType: 'ruc', valid: computeRucDv(base) === dv, now }); } if (BARE_DIGITS.test(cleaned)) { // A bare number is read as a CI. Individuals whose RUC is their CI plus a check digit // can type either, and the filing day is the same for both. return build({ base: cleaned, dv: null, docType: 'ci', valid: true, now }); } return invalid(); } function build(args: { base: string; dv: number | null; docType: 'ruc' | 'ci'; valid: boolean; now: Date; }): LookupDto { if (!args.valid) return { ...invalid(), docType: args.docType, base: args.base, dv: args.dv }; const digit = deadlineDigit(args.base); return { valid: true, docType: args.docType, base: args.base, dv: args.dv, deadlineDigit: digit, deadlineDay: deadlineDay(digit), nextDeadlines: nextThreeIvaDeadlines(digit, args.now), }; } /** The next three monthly IVA due dates, which is what the landing card shows. */ function nextThreeIvaDeadlines(digit: number, now: Date): string[] { const first = nextDeadline({ digit, obligation: 'iva_120', from: now }); return [0, 1, 2].map((offset) => formatIsoDate( offset === 0 ? fromDate(first.dueDate) : dueDateFor('iva_120', addMonths(first.period, offset), digit), ), ); } function invalid(): LookupDto { return { valid: false, docType: 'ruc', base: '', dv: null, deadlineDigit: 0, deadlineDay: deadlineDay(0), nextDeadlines: ['', '', ''], }; }