import { inputText } from './input-text.ts'; import { ispcEligibilityBook, ispcEligibilityCurrent, } from './ispc-eligibility.ts'; import { acquisitionScope } from './iva-acquisitions.ts'; import { addFractions, decimalFraction, fraction, fractionPresentation, type FiscalFraction, } from './fiscal-rational.ts'; export type IspcWithholdingInput = { applicable: '' | 'no' | 'yes'; quarter: string; base: string; priorAnnualPurchases: string; naturalOccasional: '' | 'yes' | 'no'; unregistered: '' | 'yes' | 'no'; related: '' | 'yes' | 'no'; activity: | '' | 'forestry' | 'fishery' | 'livestock' | 'agriculture' | 'poultry' | 'beekeeping' | 'other'; reference: string; }; export type IspcWithholdingResult = { input: IspcWithholdingInput; key: string; status: 'calculated' | 'indeterminate' | 'not_applicable'; reason: string; withheld: FiscalFraction | null; calculatedAt: string; source: string; }; export type IspcWithholdingBook = { version: 1; input: IspcWithholdingInput; result?: IspcWithholdingResult; history?: IspcWithholdingResult[]; }; export const ispcWithholdingSource = 'Lei9/2025,republicaçãoISPC,arts3(3–4),12(3–4);PDF10–11/p2180(10–11). Retenção liberatória5% sobre aquisição a pessoa singular ocasional não inscrita, ligada à actividade, até limite global anual2.500.000MZN; factura por conta do fornecedor e entrega trimestral em modelo próprio. Não é crédito contra o imposto próprio nem se aplica o mínimo500 desta prestação à retenção.'; const blank = (): IspcWithholdingInput => ({ applicable: '', quarter: '1', base: '', priorAnnualPurchases: '', naturalOccasional: '', unregistered: '', related: '', activity: '', reference: '', }); function snapshot(d: Record) { const s = JSON.parse( inputText(d.modelo_a_snapshot || '{"version":1,"books":{}}'), ); if ( s.version !== 1 || !s.books || typeof s.books !== 'object' || Array.isArray(s.books) ) throw Error('Livros não recuperáveis; originais preservados.'); return s; } function validate(b: IspcWithholdingBook) { if ( !b || b.version !== 1 || !b.input || Object.keys(blank()).some( (k) => typeof b.input[k as keyof IspcWithholdingInput] !== 'string', ) || ['applicable', 'naturalOccasional', 'unregistered', 'related'].some( (k) => !['', 'yes', 'no'].includes( b.input[k as keyof IspcWithholdingInput], ), ) || ![ '', 'forestry', 'fishery', 'livestock', 'agriculture', 'poultry', 'beekeeping', 'other', ].includes(b.input.activity) || (b.history !== undefined && !Array.isArray(b.history)) ) throw Error('RetençõesISPC não recuperáveis; originais preservados.'); } function map(d: Record): Record { const v = snapshot(d).ispcWithholdings; if (v === undefined) return {}; if (!v || typeof v !== 'object' || Array.isArray(v)) throw Error('Mapa de retençõesISPC inválido.'); return v; } export function ispcWithholdingBook( d: Record, ): IspcWithholdingBook { const b = map(d)[acquisitionScope(d)] ?? { version: 1, input: blank() }; validate(b); return b; } export function saveIspcWithholdingBook( d: Record, b: IspcWithholdingBook, ): string { validate(b); return JSON.stringify({ ...snapshot(d), ispcWithholdings: { ...map(d), [acquisitionScope(d)]: b }, }); } export function recordIspcWithholding( d: Record, b: IspcWithholdingBook, now = new Date().toISOString(), ): IspcWithholdingBook { validate(b); const i = b.input, e = ispcEligibilityBook(d), r: IspcWithholdingResult = { input: structuredClone(i), key: JSON.stringify([acquisitionScope(d), i]), status: 'indeterminate', reason: 'Identifique se existem aquisições abrangidas; não presumir retenção zero.', withheld: null, calculatedAt: now, source: ispcWithholdingSource, }; if (i.applicable === 'no') { r.status = 'not_applicable'; r.reason = 'Sem aquisições abrangidas, segundo declaração expressa; entradas anteriores preservadas.'; } else if ( i.applicable === 'yes' && (!ispcEligibilityCurrent(d, e) || e.result?.status !== 'compatible' || e.result.optionStatus === 'not_opted' || e.result.optionStatus === 'indeterminate' || (e.input.origin === 'effective' && e.result.optionStatus !== 'recorded')) ) { r.reason = 'Guarde o enquadramento e a opção aplicáveis do adquirenteISPC antes de calcular retenção efectiva; sem inferir adesão.'; } else if (i.applicable === 'yes') { if ( i.naturalOccasional === 'no' || i.unregistered === 'no' || i.related === 'no' || i.activity === 'other' ) { r.status = 'not_applicable'; r.reason = 'Aquisição fora do recorte12(3–4) declarado; outros regimes de retenção mantêm análise própria,sem presumir isenção.'; } else if ( i.naturalOccasional === 'yes' && i.unregistered === 'yes' && i.related === 'yes' && i.activity && i.reference.trim() && ['1', '2', '3', '4'].includes(i.quarter) ) { try { const base = decimalFraction(i.base), prior = decimalFraction(i.priorAnnualPurchases), total = addFractions([base, prior]); if ( BigInt(total.numerator) > 2500000n * BigInt(total.denominator) ) r.reason = 'Total global anual superior2.500.000MZN: tratamento da aquisição acima do limite por corresponder; não reter automaticamente5% nem truncar a base.'; else { r.status = 'calculated'; r.withheld = fraction( BigInt(base.numerator) * 5n, BigInt(base.denominator) * 100n, ); r.reason = 'Retenção5% calculada separadamente do imposto próprio; emitir factura por conta do fornecedor e preparar entrega trimestral em modelo próprio. Sem deduzir esta retenção da prestaçãoISPC.'; } } catch { r.reason = 'Base e aquisições globais anteriores do ano necessários,incluindo zero expresso; não presumir montantes.'; } } else r.reason = 'Identifique pessoa singular ocasional3(3–4),actividade abrangida,não inscrição,ligação à actividade,trimestre e suporte; dados desconhecidos não viram não aplicáveis.'; } return { ...b, ...(b.result ? { history: [...(b.history ?? []), structuredClone(b.result)] } : {}), result: r, }; } export function ispcWithholdingReportRows( d: Record, ): Array<[string, string, string, string]> { try { const b = ispcWithholdingBook(d), r = b.result; if (!r) return []; return [ [ 'ISPC', 'Retenção em aquisições ocasionais', `${r.status}; ${r.withheld ? fractionPresentation(r.withheld).decimal + ' MZN' : 'Indeterminado/não aplicado'}; ${r.reason}`, `2026/T${r.input.quarter}; base:${r.input.base}; anteriores globais:${r.input.priorAnnualPurchases}; suporte:${r.input.reference}; ${r.key === JSON.stringify([acquisitionScope(d), b.input]) ? 'Entradas correspondentes' : 'Histórico preservado;rever entradas'}; ${r.calculatedAt}; ${r.source}`, ], ]; } catch (e) { return [ [ 'ISPC', 'Retenção não recuperável', String(e), 'Originais preservados.', ], ]; } }