import { inputText } from '../lib/input-text.ts'; import { SimulationRemissionImports } from './simulation-remission-imports'; import { SimulationObjectiveImports } from './simulation-objective-imports'; import { SimulationMiners } from './simulation-miners'; import { SimulationReturnees } from './simulation-returnees'; import { SimulationDigital } from './simulation-digital'; import { SimulationTravel } from './simulation-travel'; import { SimulationTravelEvents } from './simulation-travel-events'; import { SimulationForeignOperations } from './simulation-foreign-operations'; import { SimulationCurrencyEvents } from './simulation-currency-events'; import { SimulationPropertyTransfers } from './simulation-property-transfers'; import { SimulationGoodsPresumptions } from './simulation-goods-presumptions'; import { InvestmentContextAssessment } from './investment-context-assessment'; import { useEffect, useRef, useState } from 'react'; import { monthlyIvaInput, monthlyIvaKey, monthlySalesFromDraft, readMonthlyIvaRecord, readMonthlyIvaSettings, recoverMonthlyIvaRecord, monthlyIvaCashTreatment, type MonthlyIvaInput, type MonthlyIvaSettings, } from '@/lib/monthly-iva'; import { fiscalQaRows } from '@/lib/fiscal-qa-preview'; import { type ApiSimulationExecutionV1 } from '@/lib/simulation-contract'; import { SimulationModeloA } from './simulation-modelo-a'; import { SimulationAcquisitions } from './simulation-acquisitions'; import { SimulationImportExemptions } from './simulation-import-exemptions'; import { SimulationPublicAuthorityBase } from './simulation-public-authority-base'; import { SimulationCommissionBase } from './simulation-commission-base'; import { SimulationActSaleBase } from './simulation-act-sale-base'; import { SimulationFuelBase } from './simulation-fuel-base'; import { SimulationNoncashBase } from './simulation-noncash-base'; import { SimulationCompositionBase } from './simulation-composition-base'; import { SimulationExclusionBase } from './simulation-exclusion-base'; import { SimulationValueReview } from './simulation-value-review'; import { SimulationServiceNormal } from './simulation-service-normal'; import { SimulationGoodsLocation } from './simulation-goods-location'; import { SimulationServiceLocation } from './simulation-service-location'; import { SimulationListedServices } from './simulation-listed-services'; import { SimulationReducedBases } from './simulation-reduced-bases'; import { SimulationOwnName } from './simulation-own-name'; import { SimulationGoodsAllocation } from './simulation-goods-allocation'; import { SimulationSecondhand } from './simulation-secondhand'; import { SimulationOfferPreparations } from './simulation-offer-preparations'; import { SimulationOperations } from './simulation-operations'; export function SimulationMonthlyIva({ draft, busy, onCalculate, onChange, }: { draft: Record; busy: boolean; onCalculate: ( input: MonthlyIvaInput, ) => Promise; onChange: (field: string, value: string) => void; }) { const settings = readMonthlyIvaSettings( inputText(draft.monthly_iva_settings ?? ''), ); const record = readMonthlyIvaRecord( inputText(draft.monthly_iva_snapshot ?? ''), ); const [retry, setRetry] = useState(0); const [error, setError] = useState(''); const [available, setAvailable] = useState(null); const attempt = useRef(''); const mounted = useRef(true); const latestSnapshot = useRef(draft.monthly_iva_snapshot); latestSnapshot.current = draft.monthly_iva_snapshot; useEffect(() => { mounted.current = true; return () => { mounted.current = false; }; }, []); 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 pendingRecord = record && ['queued', 'running'].includes(record.execution.status) ? record : null; const actionKey = pendingRecord ? `recover:${pendingRecord.execution.id}` : key; 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 || busy || attempt.current === actionKey) return; if ( !pendingRecord && (!input || input.currency !== 'MZN' || !available || completed) ) return; const request = pendingRecord?.input ?? input!; const savedSnapshot = draft.monthly_iva_snapshot; const timer = window.setTimeout(() => { attempt.current = actionKey; setError(''); void (async () => { try { let execution; if (pendingRecord) { execution = ( await recoverMonthlyIvaRecord(pendingRecord) ).execution; } 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.', ); if ( !mounted.current || (pendingRecord && latestSnapshot.current !== savedSnapshot) ) return; callbacks.current.onChange( 'monthly_iva_snapshot', JSON.stringify({ input: request, execution }), ); } catch (cause) { if (mounted.current) setError( cause instanceof Error ? cause.message : 'Não foi possível calcular o IVA mensal.', ); } })(); }, 700); return () => window.clearTimeout(timer); }, [actionKey, 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: {inputText(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}

} {pendingRecord && (

Consulta da execução {pendingRecord.execution.id}. A suspensão de novos cálculos não impede esta recuperação; não é criado outro pedido.

)} {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}
))}
)}
); }