'use client'; import type { Direction, SupplierRegimeHint } from '@impuestos/contracts'; import { formatGsAmount } from '@impuestos/i18n'; import { computeRucDv, parseRuc, validateRuc } from '@impuestos/rules'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useSearchParams } from 'next/navigation'; import { useState } from 'react'; import { Button } from '@/components/ui/button'; import { Card } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Switch } from '@/components/ui/switch'; import { useRouter } from '@/i18n/navigation'; import { useT } from '@/i18n/t'; import { api } from '@/lib/api'; import { cn } from '@/lib/utils'; const REGIMES: SupplierRegimeHint[] = ['normal', 'resimple', 'unknown']; /** * Flow B3. One screen, numeric keypads for the money, and the IVA split worked out from * the total with an override, because retyping a number the factura already prints is how * people give up halfway. */ export function ManualForm() { const t = useT(); const router = useRouter(); const queryClient = useQueryClient(); const fileId = useSearchParams().get('fileId'); const [direction, setDirection] = useState('purchase'); const [ruc, setRuc] = useState(''); const [emitterName, setEmitterName] = useState(''); const [issueDate, setIssueDate] = useState(() => new Date().toISOString().slice(0, 10)); const [total, setTotal] = useState(''); const [allTenPercent, setAllTenPercent] = useState(true); const [regime, setRegime] = useState('unknown'); const [override, setOverride] = useState<{ iva10: string; iva5: string; exenta: string } | null>(null); const parsedRuc = parseRuc(ruc.trim()); const rucValid = parsedRuc ? validateRuc(parsedRuc.base, parsedRuc.dv) : /^\d{1,8}$/.test(ruc.trim()); const totalValue = total === '' ? 0 : Number(total); // IVA is inside the printed total: at 10% the tax is a eleventh of it, at 5% a twenty first. const derived = allTenPercent ? { iva10: Math.round(totalValue / 11), iva5: 0, exenta: 0 } : { iva10: 0, iva5: Math.round(totalValue / 21), exenta: 0 }; const split = override ? { iva10: Number(override.iva10 || '0'), iva5: Number(override.iva5 || '0'), exenta: Number(override.exenta || '0'), } : derived; const save = useMutation({ mutationFn: () => { const base = parsedRuc?.base ?? ruc.trim(); return api.createManualDocument({ ...(fileId ? { fileId } : {}), direction, docKind: 'factura', emitterRuc: base, emitterDv: parsedRuc ? String(parsedRuc.dv) : String(computeRucDv(base)), emitterName: emitterName.trim(), issueDate, total: totalValue, amountIva10: split.iva10 > 0 ? totalValue - split.iva10 - split.exenta : 0, amountIva5: split.iva5 > 0 ? totalValue - split.iva5 - split.exenta : 0, amountExenta: split.exenta, iva10: split.iva10, iva5: split.iva5, supplierRegimeHint: regime, }); }, onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: ['documents'] }); router.push('/bandeja'); }, }); const canSave = rucValid && emitterName.trim().length > 0 && totalValue > 0; return (

{t('manual.title')}

{fileId ?

{t('scan.needsManual')}

: null}
{t('manual.direction')}
{(['purchase', 'sale'] as const).map((value) => ( ))}
0 && !rucValid} onChange={(event) => setRuc(event.target.value)} /> {ruc.length > 0 && !rucValid ? (

{t('landing.invalidDoc')}

) : null}
setEmitterName(event.target.value)} />
setIssueDate(event.target.value)} />
setTotal(event.target.value.replace(/\D/g, ''))} />

{t('manual.ivaSplit.help')}

{( [ ['iva10', t('manual.iva10')], ['iva5', t('manual.iva5')], ['exenta', t('manual.exenta')], ] as const ).map(([key, label]) => (
{ const digits = event.target.value.replace(/\D/g, ''); setOverride({ iva10: String(split.iva10), iva5: String(split.iva5), exenta: String(split.exenta), [key]: digits, }); }} />
))}
{save.isError ? (

{t('common.error.generic')}

) : null}
); }