import { inputText } from './input-text.ts'; import type { AcquisitionBook } from './iva-acquisitions.ts'; import { beverageParcel } from './iva-beverage-parcels.ts'; import { addFractions, decimalFraction, fraction, fractionLabel, subtractFractions, type FiscalFraction, } from './fiscal-rational.ts'; /** Technical representation of disjoint VAT portions, not an additional fiscal condition. */ export type BeverageAllocation = { from: string; to: string; reference: string; deductionFrom: string; deductionTo: string; deductionReference: string; }; type Source = { parcel: string; origin: string; vat: FiscalFraction }; export type BeverageRange = { from: FiscalFraction; to: FiscalFraction; amount: FiscalFraction; }; function boundary(raw: string): FiscalFraction { const parts = /^(\d{1,40})\/(\d{1,40})$/.exec(raw); if (!parts) return decimalFraction(raw); if (BigInt(parts[2]) === 0n) throw Error('Denominador nulo nos limites da parcela.'); return fraction(BigInt(parts[1]), BigInt(parts[2])); } export function beverageRange( source: Source, allocation?: BeverageAllocation, ): BeverageRange { const from = allocation ? boundary(allocation.from) : fraction(0n), to = allocation ? boundary(allocation.to) : source.vat, amount = subtractFractions(to, from); if ( BigInt(amount.numerator) <= 0n || BigInt(subtractFractions(source.vat, to).numerator) < 0n ) throw Error( 'Limites da parcela fora do IVA documental; indique início inferior ao fim e fim até ao total da linha.', ); if (allocation && !allocation.reference.trim()) throw Error('Identifique o suporte da repartição da linha.'); return { from, to, amount }; } export function beverageRangesOverlap( a: BeverageRange, b: BeverageRange, ): boolean { return ( BigInt(subtractFractions(a.to, b.from).numerator) > 0n && BigInt(subtractFractions(b.to, a.from).numerator) > 0n ); } export function beverageDeductionEvidence( d: Record, source: Source, ): { amount: FiscalFraction | null; reason: string; records: Array<{ context: string; revision: number; calculatedAt: string; amount: FiscalFraction; documents: string[]; }>; } { const store = JSON.parse(inputText(d.modelo_a_snapshot || '{}')), books = (store.books ?? {}) as Record, records: Array<{ context: string; revision: number; calculatedAt: string; amount: FiscalFraction; documents: string[]; }> = []; for (const [context, book] of Object.entries(books)) for (const calc of [ ...(book.history ?? []), ...(book.result ? [book.result] : []), ]) { const inputs = calc.inputs.filter( (row) => row.origin === 'effective' && source.origin === 'effective' && beverageParcel(row) === source.parcel, ); const amounts: FiscalFraction[] = []; for (const input of inputs) { const result = calc.rows.find((r) => r.id === input.id); if (!result || result.deductible === null) { if (input.bc?.beveragePath === 'deduction') return { amount: null, reason: 'Dedução efectiva anterior indeterminada; identificar montante exacto e documento antes da repartição.', records, }; continue; } const amount = result.deductibleExact ?? decimalFraction(result.deductible); if (BigInt(amount.numerator) > 0n) amounts.push(amount); } if (amounts.length) records.push({ context, revision: calc.revision, calculatedAt: calc.calculatedAt, amount: addFractions(amounts), documents: inputs.map( (row) => `${row.documentRef};linha${row.documentLine};${row.period}`, ), }); } const unique = [ ...new Map( records.map((r) => [fractionLabel(r.amount), r.amount]), ).values(), ]; if (unique.length > 1) return { amount: null, reason: 'Montantes de dedução efectiva divergentes nas versões recuperadas; reconciliar a sequência e o fundamento das regularizações. Uma correcção preparada não liberta IVA para E.', records, }; return { amount: unique[0] ?? fraction(0n), reason: records.length ? 'Dedução efectiva declarada preservada; a sua afectação à parcela exige suporte expresso.' : 'Sem dedução efectiva positiva identificada nos livros recuperados; não é consulta à conta corrente AT.', records, }; } export function reconcileBeverageAllocation( d: Record, source: Source, allocation?: BeverageAllocation, ): { range: BeverageRange | null; prior: FiscalFraction | null; reason: string; } { const evidence = beverageDeductionEvidence(d, source); if (!evidence.amount) return { range: null, prior: null, reason: evidence.reason }; try { const range = beverageRange(source, allocation); if (BigInt(evidence.amount.numerator) > 0n) { if (!allocation || !allocation.deductionReference.trim()) return { range: null, prior: evidence.amount, reason: 'Dedução efectiva anterior identificada; indique limites e suporte da parcela já deduzida, sem a apagar ou repetir em restituição.', }; const deduction = beverageRange(source, { ...allocation, from: allocation.deductionFrom, to: allocation.deductionTo, }); if ( BigInt( subtractFractions(deduction.amount, evidence.amount) .numerator, ) !== 0n || beverageRangesOverlap(range, deduction) ) return { range: null, prior: evidence.amount, reason: 'Parcela da dedução não coincide com o montante efectivo anterior ou sobrepõe-se à restituição; benefício duplicado impedido. Se toda a linha foi deduzida, é necessária regularização com fundamento próprio, ainda não automatizada nesta via.', }; } else if ( allocation && (allocation.deductionFrom || allocation.deductionTo || allocation.deductionReference) ) return { range: null, prior: evidence.amount, reason: 'Foi indicada dedução anterior sem correspondência nos livros efectivos recuperados; resolver a divergência antes de calcular.', }; return { range, prior: evidence.amount, reason: evidence.reason }; } catch (error) { return { range: null, prior: evidence.amount, reason: String(error instanceof Error ? error.message : error), }; } }