import { InvestmentContextAssessment } from './investment-context-assessment'; import { useEffect, useRef, useState } from 'react'; import { monthlyIvaInput, monthlyIvaKey, monthlySalesFromDraft, readMonthlyIvaRecord, readMonthlyIvaSettings, monthlyIvaCashTreatment, type MonthlyIvaInput, type MonthlyIvaSettings } from '@/lib/monthly-iva'; import { fiscalQaRows } from '@/lib/fiscal-qa-preview'; import { isApiSimulationReadResponseV1, type ApiSimulationExecutionV1 } from '@/lib/simulation-contract'; import { SimulationModeloA } from './simulation-modelo-a'; export function SimulationMonthlyIva({draft, busy, onCalculate, onChange}: { draft: Record; busy: boolean; onCalculate: (input: MonthlyIvaInput) => Promise; onChange: (field: string, value: string) => void; }) { const settings = readMonthlyIvaSettings(String(draft.monthly_iva_settings ?? '')); const record = readMonthlyIvaRecord(String(draft.monthly_iva_snapshot ?? '')); const [retry, setRetry] = useState(0); const [error, setError] = useState(''); const [available, setAvailable] = useState(null); const attempt = useRef(''); const callbacks = useRef({onCalculate, onChange}); callbacks.current = {onCalculate, onChange}; let input: MonthlyIvaInput | null = null; let inputError = ''; let source = ''; try { source = monthlySalesFromDraft(draft).source; input = monthlyIvaInput(draft, settings); } catch (cause) { inputError = cause instanceof Error ? cause.message : 'Reveja a projecção de vendas.'; } const key = input ? monthlyIvaKey(input) : ''; const current = !!(input && record && monthlyIvaKey(record.input) === key); const completed = current && record?.execution.status === 'completed'; const saveSettings = (patch: Partial) => onChange('monthly_iva_settings', JSON.stringify({...settings, ...patch})); useEffect(() => { let active = true; fetch('/api/v1/fiscal-consumer', {credentials: 'same-origin', headers: {Accept:'application/json'}}).then(async (response) => { if (!response.ok) throw new Error(); const payload = await response.json(); if (active) setAvailable(payload?.data?.enabled === true); }).catch(() => {if (active) setError('Não foi possível consultar a disponibilidade da regra. As vendas foram preservadas.');}); return () => { active = false; }; }, [retry]); useEffect(() => { if (!settings.auto || !input || input.currency !== 'MZN' || busy || !available || completed || attempt.current === key) return; const request = input; const pending = current && record && ['queued','running'].includes(record.execution.status) ? record.execution.id : null; const timer = window.setTimeout(() => { attempt.current = key; setError(''); void (async () => { try { let execution; if (pending) { const response = await fetch(`/api/v1/simulations/${pending}`, {credentials:'same-origin', headers:{Accept:'application/json'}}); const value: unknown = await response.json(); if (!response.ok || !isApiSimulationReadResponseV1(value)) throw new Error('Não foi possível recuperar a execução mensal guardada. Não foi criada uma execução alternativa.'); execution = value.data; } else execution = await callbacks.current.onCalculate(request); if (!execution) throw new Error('O cálculo mensal não foi concluído. As vendas e a versão anterior foram preservadas; tente novamente.'); callbacks.current.onChange('monthly_iva_snapshot', JSON.stringify({input:request, execution})); } catch (cause) {setError(cause instanceof Error ? cause.message : 'Não foi possível calcular o IVA mensal.');} })(); }, 700); return () => window.clearTimeout(timer); }, [key, settings.auto, busy, available, completed, retry]); const snapshot = record?.execution.result_snapshot; const projection = snapshot?.monthly_projection as {rows?: Array<{date:string; sales_declared:string|null; net_sales:string|null; rate:string|null; vat:string|null; gross_sales:string|null; status:string}>} | undefined; return

IVA mensal das vendas projectadas

{source}

Declare se os preços projectados são sem IVA ou com IVA incluído. Nenhuma taxa é fixada neste formulário; o servidor selecciona a versão publicada.

Sem importações, taxas reduzidas, isenções, regimes especiais, deduções, reembolsos ou apuramento final. Valores com IVA incluído permanecem indeterminados neste lote; não se aplica a taxa ao preço bruto.

{monthlyIvaCashTreatment}

{!!draft.currency && draft.currency !== "MZN" &&

Moeda do projecto: {String(draft.currency)}. A regra disponível aceita apenas MZN. As vendas e a moeda são preservadas, sem conversão; o IVA mensal não pode ser determinado por esta versão.

} {inputError &&

{inputError}

} {available === null && !error &&

A consultar a disponibilidade da versão publicada…

} {available === false &&

A versão publicada de IVA está indisponível neste ambiente para novas execuções. Os pressupostos e históricos mantêm-se.

} {record && !current &&

As vendas ou os pressupostos mudaram. Os resultados abaixo são históricos e estão desactualizados; não são usados como resultados da projecção actual.

} {settings.auto && !completed && current &&

Execução mensal guardada: {record?.execution.status === 'queued' ? 'em fila' : record?.execution.status === 'running' ? 'em processamento' : 'não concluída'}. Pode voltar a consultar sem duplicar.

} {busy &&

A actualizar os resultados dependentes das vendas…

} {error &&

{error}

} {settings.auto && } {input &&

{input.monthly_sales.length} meses; moeda {input.currency || 'por indicar'}. Campo vazio significa informação em falta; zero declarado permanece zero. Depois da primeira execução, alterações das vendas actualizam o cálculo enquanto a actualização automática estiver ligada.

} {projection?.rows &&
{['Mês','Vendas declaradas','Vendas tributáveis sem IVA','Taxa aplicada (%)','IVA calculado','Total com IVA','Estado'].map((label) => )}{projection.rows.map((row) => )}
IVA mensal — {current ? 'versão correspondente às entradas' : 'versão histórica'}
{label}
{row.date.slice(0,7)}{row.sales_declared ?? 'Sem informação'}{row.net_sales ?? 'Indeterminado'}{row.rate ?? 'Indeterminada'}{row.vat ?? 'Indeterminado'}{row.gross_sales ?? 'Indeterminado'}{{calculated:'Calculado',missing_sales:'Sem informação de vendas',unsupported_basis:'Base líquida não determinada',indeterminate:'Enquadramento ou regra indeterminados'}[row.status] ?? 'Indeterminado'}
} {snapshot &&
Ver fundamento e relatório da versão guardada
{fiscalQaRows(snapshot).map(([label,value], index) =>
{label}
{value}
)}
}
; }