import { inputText } from './input-text.ts'; import { acquisitionBook, acquisitionCurrent } from './iva-acquisitions.ts'; import { beverageParcel } from './iva-beverage-parcels.ts'; import { closureFingerprint } from './iva-closure.ts'; import { assessFiscalClaim, newFiscalClaimEvents, fiscalClaimDate, type FiscalClaimEvents, type FiscalClaimState, } from './fiscal-claim-events.ts'; import { decimalFraction, fractionPresentation, type FiscalFraction, } from './fiscal-rational.ts'; import { beverageRange, beverageRangesOverlap, beverageDeductionEvidence, reconcileBeverageAllocation, type BeverageAllocation, } from './iva-beverage-allocation.ts'; export const beverageRestitutionSource = 'L3-E:CIVA13(1)(c/d/h),13(2–3),Lei13/2016 PDF12/p1426(270). Bebidas:dedução ou restituição,sem aplicar20(1)(d);não isenção directa. Provisões de pesca costeira excluídas. Linha integral ou parcelas documentadas sem sobreposição;dedução efectiva anterior reconciliada por montante e suporte,sem apagar históricos. Procedimento/formulário específico não automatizado;sem importar limites/prazos do reembolso geral21.'; export type BeverageRestitutionSource = { parcel: string; acquisitionId: string; scope: string; period: string; origin: 'projected' | 'effective'; documentRef: string; documentDate: string; documentLine: string; supplier: string; vat: FiscalFraction; fingerprint: string; operationId: string; operationRef: string; calculatedAt: string; }; export type BeverageRestitutionInput = FiscalClaimEvents & { id: string; source: BeverageRestitutionSource | null; entityRef: string; exclusive: '' | 'yes' | 'no'; allocation?: BeverageAllocation; }; export type BeverageRestitutionResult = FiscalClaimState & { id: string; inputKey: string; requested: FiscalFraction | null; source: string; }; type Calc = { calculatedAt: string; key: string; inputs: BeverageRestitutionInput[]; rows: BeverageRestitutionResult[]; }; export type BeverageRestitutionBook = { version: 1; rows: BeverageRestitutionInput[]; result?: Calc; history?: Calc[]; }; const scope = (d: Record) => inputText(d.context_opportunity || 'general'), norm = (s: string) => s.trim().toLocaleLowerCase(); function store(d: Record): Record { const s = JSON.parse( inputText(d.modelo_a_snapshot || '{"version":1,"books":{}}'), ); if (s.version !== 1 || !s.books) throw Error('Livros não recuperáveis;originais preservados.'); return s; } function maps( d: Record, ): Record { const m = store(d).ivaBeverageRestitutions; if (m === undefined) return {}; if (!m || typeof m !== 'object' || Array.isArray(m)) throw Error( 'Mapa de restituições não recuperável;originais preservados.', ); return m as Record; } function validate(b: BeverageRestitutionBook): void { if ( !b || b.version !== 1 || !Array.isArray(b.rows) || (b.history !== undefined && !Array.isArray(b.history)) || (b.result !== undefined && (!Array.isArray(b.result.rows) || !Array.isArray(b.result.inputs))) ) throw Error('LivroE não recuperável;originais preservados.'); for (const r of b.rows) if ( !r || ![ 'id', 'entityRef', 'requestReference', 'requestDate', 'decisionReference', 'decisionDate', 'decisionIssuer', 'approvedAmount', ].every( (k) => typeof r[k as keyof BeverageRestitutionInput] === 'string', ) || (r.allocation !== undefined && (!r.allocation || ![ 'from', 'to', 'reference', 'deductionFrom', 'deductionTo', 'deductionReference', ].every( (k) => typeof r.allocation![ k as keyof BeverageAllocation ] === 'string', ))) || !['', 'yes', 'no'].includes(r.exclusive) || !['preparation', 'submitted'].includes(r.stage) || !['pending', 'approved', 'partial', 'rejected'].includes( r.decision, ) || !Array.isArray(r.receipts) || r.receipts.some( (p) => !p || !['id', 'reference', 'date', 'amount'].every( (k) => typeof p[k as keyof typeof p] === 'string', ), ) ) throw Error('EntradasE não recuperáveis;sem apagar documentos.'); } export function beverageRestitutionBook( d: Record, ): BeverageRestitutionBook { const b = maps(d)[scope(d)] ?? { version: 1, rows: [] }; validate(b); return b; } export function saveBeverageRestitutionBook( d: Record, b: BeverageRestitutionBook, ): string { validate(b); const s = store(d); return JSON.stringify({ ...s, ivaBeverageRestitutions: { ...maps(d), [scope(d)]: b }, }); } export function beverageRestitutionSources( d: Record, ): BeverageRestitutionSource[] { const b = acquisitionBook(d); if (!acquisitionCurrent(d, b)) return []; return b.rows.flatMap((row) => { const result = b.result!.rows.find((r) => r.id === row.id); if ( row.bc?.expense !== 'beverages' || row.bc.beveragePath !== 'refund' || !result?.reason.startsWith('Via restituição13(2)') || result.deductible !== '0.00' || !result.vat || !row.deductionLink ) return []; const vat = decimalFraction(result.vat); if (BigInt(vat.numerator) <= 0n) return []; return [ { parcel: beverageParcel(row), acquisitionId: row.id, scope: scope(d), period: row.period, origin: row.origin, documentRef: row.documentRef, documentDate: row.documentDate, documentLine: row.documentLine, supplier: row.supplier, vat, fingerprint: closureFingerprint([row, result]), operationId: row.deductionLink.operationId, operationRef: row.deductionLink.allocationRef, calculatedAt: b.result!.calculatedAt, }, ]; }); } export function newBeverageRestitution( id: string, source: BeverageRestitutionSource | null, entityRef = '', ): BeverageRestitutionInput { return { ...newFiscalClaimEvents(), id, source: source ? structuredClone(source) : null, entityRef, exclusive: '', }; } function sameSource( d: Record, r: BeverageRestitutionInput, ): boolean { return ( !!r.source && beverageRestitutionSources(d).some( (s) => s.scope === r.source!.scope && s.parcel === r.source!.parcel && s.fingerprint === r.source!.fingerprint, ) ); } function allUses( d: Record, b: BeverageRestitutionBook, ): BeverageRestitutionInput[] { return [ ...b.rows, ...Object.entries(maps(d)) .filter(([id]) => id !== scope(d)) .flatMap(([, v]) => { validate(v); return v.rows.filter((r) => r.source?.origin === 'effective'); }), ]; } function past( d: Record, b: BeverageRestitutionBook, ): BeverageRestitutionInput[] { return Object.entries({ ...maps(d), [scope(d)]: b }).flatMap( ([context, v]) => [...(v.history ?? []), ...(v.result ? [v.result] : [])].flatMap( (c) => c.inputs.filter( (r) => (context === scope(d) || r.source?.origin === 'effective') && c.rows.some( (v) => v.id === r.id && v.status === 'determined' && v.requested && BigInt(v.requested.numerator) > 0n, ), ), ), ); } function committedParcel(r: BeverageRestitutionInput): string { if (!r.source) return ''; try { const range = beverageRange(r.source, r.allocation); return closureFingerprint([ r.source.parcel, r.source.origin, r.source.vat, range.from, range.to, ]); } catch { return 'invalid'; } } const receiptKey = ( r: BeverageRestitutionInput, p: FiscalClaimEvents['receipts'][number], ) => JSON.stringify([norm(r.entityRef), r.source?.origin, norm(p.reference)]); function key(d: Record, b: BeverageRestitutionBook): string { return closureFingerprint([ b.rows, b.rows.map((r) => [ sameSource(d, r), r.source ? beverageDeductionEvidence(d, r.source) : null, ]), allUses(d, b).map((r) => [ r.id, r.source, r.allocation, r.receipts.map((p) => receiptKey(r, p)), ]), past(d, b) .filter( (r) => r.stage === 'submitted' || r.decision !== 'pending' || r.receipts.length, ) .map((r) => [ r.id, r.source, r.allocation, r.receipts.map((p) => receiptKey(r, p)), ]), ]); } export function beverageRestitutionCurrent( d: Record, b: BeverageRestitutionBook, ): boolean { return ( !!b.result && b.result.key === key(d, b) && b.rows.length === b.result.rows.length && b.rows.every((r) => b.result!.rows.some( (v) => v.id === r.id && v.inputKey === closureFingerprint(r), ), ) ); } export function recordBeverageRestitution( d: Record, b: BeverageRestitutionBook, now = new Date().toISOString(), ): BeverageRestitutionBook { validate(b); if (new Set(b.rows.map((r) => r.id)).size !== b.rows.length) throw Error('IdentidadeE repetida.'); const uses = allUses(d, b), history = past(d, b); const rows = b.rows.map((row) => { const r: BeverageRestitutionResult = { id: row.id, inputKey: closureFingerprint(row), status: 'indeterminate', reason: '', requested: null, approved: null, received: null, awaitingReceipt: null, notGranted: null, source: beverageRestitutionSource, }, stop = (reason: string) => ({ ...r, reason }), s = row.source; if ( !s || !sameSource(d, row) || d.currency !== 'MZN' || s.scope !== scope(d) ) return stop( 'OrigemL1/L2 alterada ou ausente;sem restituição presumida.', ); if (!row.entityRef.trim() || row.exclusive !== 'yes') return stop( 'Identifique entidade e confirme a parcela exclusivamente para restituição;sem dupla utilização.', ); const reconciled = reconcileBeverageAllocation(d, s, row.allocation); if (!reconciled.range) return stop(reconciled.reason); const related = (v: BeverageRestitutionInput) => v.source?.origin === s.origin && v.source?.parcel === s.parcel; const overlaps = (v: BeverageRestitutionInput) => { try { return beverageRangesOverlap( reconciled.range!, beverageRange(v.source!, v.allocation), ); } catch { return true; } }; if ( uses.some((v) => v !== row && related(v) && overlaps(v)) || history.some( (v) => related(v) && (v.stage === 'submitted' || v.decision !== 'pending' || v.receipts.length) && (v.id !== row.id ? overlaps(v) : committedParcel(v) !== committedParcel(row)), ) ) return stop( 'Parcela documental já ligada a pedido ou histórico comprometido;limites sobrepostos ou alterados. Sem duplicar nem libertar IVA por apagar a preparação.', ); r.requested = reconciled.range.amount; if ( !fiscalClaimDate(row.requestDate) || row.requestDate < s.documentDate ) return stop( 'Data do pedido/preparação anterior ao documento ou por identificar.', ); const events = assessFiscalClaim( row, reconciled.range.amount, (p) => uses.flatMap((v) => v.receipts.filter( (q) => receiptKey(v, q) === receiptKey(row, p), ), ).length > 1 || history.some( (v) => v.id !== row.id && v.receipts.some( (q) => receiptKey(v, q) === receiptKey(row, p), ), ), ); Object.assign(r, events); if (r.status === 'determined') r.reason += ' Linha13(2) fora da deduçãoL2 e do crédito geralD;pedido não é isenção directa nem concessão automática. Decisão e recebimento registados fora doInvesteMoz;procedimento/formulário específico e prazos não automatizados,sem os importar deD.'; return r; }); const result = { calculatedAt: now, key: '', inputs: structuredClone(b.rows), rows, }; const next = { ...b, ...(b.result ? { history: [...(b.history ?? []), structuredClone(b.result)] } : {}), result, }; result.key = key(d, next); return next; } export function beverageRestitutionReportRows( d: Record, ): Array<[string, string, string, string]> { try { const b = beverageRestitutionBook(d); if (!b.rows.length) return []; const out: Array<[string, string, string, string]> = [ [ 'IVA mensal', 'L3-E — restituição de bebidas', beverageRestitutionCurrent(d, b) ? 'Resultado correspondente às entradas' : 'Histórico preservado;rever origem/parcela', beverageRestitutionSource, ], ], fmt = (v: FiscalFraction | null) => v ? fractionPresentation(v).decimal : 'Indeterminado'; for (const r of b.result?.rows ?? []) { const i = b.result!.inputs.find((v) => v.id === r.id), s = i?.source; out.push([ 'IVA mensal', `Restituição de bebidas — ${s?.period ?? ''}`, `IVA documental:${fmt(s?.vat ?? null)};parcelaIVA:${fmt(r.requested)};dedução desta parcela:${r.requested ? '0pela via exclusivaE' : 'Indeterminada;ver origem'};reconhecido:${fmt(r.approved)};recebido:${fmt(r.received)};aguarda recebimento:${fmt(r.awaitingReceipt)}`, `${i?.entityRef ?? ''};${scope(d)};${s?.origin === 'effective' ? 'Dados efectivos declarados' : 'Pressuposto de simulação'};${s?.documentRef ?? ''};${s?.documentDate ?? ''};linha${s?.documentLine ?? ''};operaçãoL1${s?.operationId ?? ''};${s?.operationRef ?? ''};pedido${i?.requestReference ?? ''},${i?.requestDate ?? ''};decisão${i?.decisionReference ?? ''},${i?.decisionDate ?? ''},${i?.decisionIssuer ?? ''};recibos:${i?.receipts.map((p) => `${p.reference};${p.date};${p.amount}MZN`).join(' | ') || 'nenhum registado'};${i?.allocation ? `repartiçãoIVA[${i.allocation.from},${i.allocation.to}[;suporte${i.allocation.reference};dedução anterior[${i.allocation.deductionFrom},${i.allocation.deductionTo}[;${i.allocation.deductionReference};` : ''}${r.reason}`, ]); } return out; } catch { return [ [ 'IVA mensal', 'L3-E', 'Restituição não recuperável', 'Originais preservados;sem dedução/restituição presumidas.', ], ]; } }