Files
Investidor-V2/resources/js/components/simulation-import-exemptions.tsx
T

518 lines
28 KiB
TypeScript

import { inputText } from '../lib/input-text.ts';
import { useState } from 'react';
import {
importExemptionBook,
importExemptionCurrent,
importExemptionReportRows,
importExemptionRules,
importRuleSource,
isImportService,
isDevelopmentImport,
importDevelopmentFields,
newImportExemption,
recordImportExemptions,
saveImportExemptionBook,
type ImportExemptionBook,
type ImportExemptionRow,
} from '@/lib/iva-import-exemptions';
export function SimulationImportExemptions({
draft,
onChange,
}: {
draft: Record<string, unknown>;
onChange: (field: string, value: string) => void;
}) {
const [error, setError] = useState('');
let book: ImportExemptionBook;
try {
book = importExemptionBook(draft);
} catch (cause) {
return <p role="alert">{String(cause)}</p>;
}
const save = (next: ImportExemptionBook) => {
try {
onChange('modelo_a_snapshot', saveImportExemptionBook(draft, next));
setError('');
} catch (cause) {
setError(String(cause));
}
};
const change = (id: string, patch: Partial<ImportExemptionRow>) =>
save({
...book,
rows: book.rows.map((row) =>
row.id === id ? { ...row, ...patch } : row,
),
});
return (
<details className="space-y-3 rounded border p-3">
<summary>
IVA na importação e serviços associados — enquadramentos
disponíveis
</summary>
<p>
Oportunidade:{' '}
{inputText(draft.context_opportunity || 'entrada geral')}.
Indique apenas importações relevantes: reimportação no mesmo
estado, ouro pelo Banco de Moçambique, objectos de arte, veículo
de combate a incêndios, embarcação/avião elegível ou objecto da
sua exploração, bens em regime aduaneiro com isenção total de
direitos, importação sob controlo alfandegário em
zona/depósito/loja franca nas condições próprias, equipamento
classe K para empreendimento autorizado, materiais/equipamentos
para projecto de desenvolvimento ONU nas condições próprias, ou
bens de abastecimento a bordo de embarcação/avião de navegação
internacional. Escolha o ramo para apresentar as condições
aplicáveis. Cobertura2026/MZN; as compras internas e os seus
resultados conservam-se.
</p>
<p>
O valor declarado/previsional é conservado, sem o fixar como
base aduaneira. A classificação não preenche o campo11 de
dedução nem os totais do ModeloA. Uma condição não cumprida
afasta só esta isenção; o imposto normal fica por enquadrar. Nas
projecções, as respostas são pressupostos, sem certificação
aduaneira.
</p>
<p>
Nos serviços associados, identifique a importação e a inclusão
do valor na sua base, conforme o artigo16. O IVA do serviço é
apresentado separadamente do IVA dos bens; o simulador não soma
novamente esse valor à base.
</p>
{error && <p role="alert">{error}</p>}
<button
type="button"
className="underline"
onClick={() =>
save({
...book,
rows: [
...book.rows,
newImportExemption(
crypto.randomUUID(),
inputText(draft.currency ?? ''),
),
],
})
}
>
Acrescentar importação ou serviço relevante
</button>
{book.rows.map((row) => {
const rule = importExemptionRules.find(
(item) => item.id === row.treatment,
);
return (
<details key={row.id} className="rounded border p-3">
<summary>
{row.description || 'Importação por identificar'} —{' '}
{row.period || 'mês por indicar'}
</summary>
<div className="grid gap-3 md:grid-cols-2">
<label>
Ramo da importação
<select
className="block rounded border p-2"
value={row.treatment}
onChange={(e) =>
change(row.id, {
treatment: e.target.value,
})
}
>
<option value="">Por indicar</option>
{importExemptionRules.map((item) => (
<option key={item.id} value={item.id}>
{item.label}
</option>
))}
</select>
</label>
<label>
Natureza dos dados
<select
className="block rounded border p-2"
value={row.origin}
onChange={(e) =>
change(row.id, {
origin: e.target
.value as ImportExemptionRow['origin'],
})
}
>
<option value="projected">
Projectados / pressupostos
</option>
<option value="effective">
Efectivos declarados
</option>
</select>
</label>
{(
[
'description',
'importer',
'declaredValue',
'goodsCode',
'originCountry',
'documentRef',
'documentDate',
'period',
'evidence',
] as const
).map((field, index) =>
isImportService(row.treatment) &&
['goodsCode', 'originCountry'].includes(
field,
) ? null : (
<label key={field}>
{isImportService(row.treatment) &&
field === 'description'
? 'Serviço associado à importação'
: isImportService(row.treatment) &&
field === 'importer'
? 'Importador destinatário do serviço'
: isImportService(
row.treatment,
) && field === 'declaredValue'
? 'Valor do serviço incluído na base (moeda indicada)'
: isImportService(
row.treatment,
) && field === 'documentRef'
? 'Documento do serviço / referência da previsão'
: isImportService(
row.treatment,
) && field === 'period'
? 'Mês do serviço'
: [
'Bens importados',
'Importador',
'Valor declarado / previsão (moeda indicada)',
'Código aduaneiro, se disponível',
'País de origem, se disponível',
'Documento aduaneiro / referência da previsão',
'Data do documento / prevista',
'Mês da importação',
'Fundamento factual das condições / pressupostos',
][index]}
<input
className="block rounded border p-2"
type={
field === 'documentDate'
? 'date'
: field === 'period'
? 'month'
: 'text'
}
value={row[field]}
onChange={(e) =>
change(row.id, {
[field]: e.target.value,
})
}
/>
</label>
),
)}
{isImportService(row.treatment) &&
(
[
'serviceImportRef',
'serviceBaseRef',
'serviceProvider',
] as const
).map((field, index) => (
<label key={field}>
{
[
'Importação de bens associada / documento ou previsão',
'Referência da base tributável que inclui o valor do serviço',
'Prestador do serviço, se disponível',
][index]
}
<input
className="block rounded border p-2"
value={row[field] ?? ''}
onChange={(e) =>
change(row.id, {
[field]: e.target.value,
})
}
/>
</label>
))}
{row.treatment ===
'accessory_service_first_destination' && (
<label>
Primeiro destino e documento de transporte
ou primeira ruptura de carga
<input
className="block rounded border p-2"
value={
row.serviceFirstDestination ?? ''
}
onChange={(e) =>
change(row.id, {
serviceFirstDestination:
e.target.value,
})
}
/>
</label>
)}
{row.treatment ===
'authorized_investment_equipment' && (
<>
<p className="md:col-span-2">
A classe K e a autorização são condições
distintas. O portal conserva a
referência e o âmbito aplicável;
apresentar um pedido não equivale a
obter autorização. Numa projecção,
identifique expressamente os
pressupostos.
</p>
<label>
Autorização do empreendimento /
pressuposto expresso
<input
className="block rounded border p-2"
value={
row.investmentAuthorizationRef ??
''
}
onChange={(e) =>
change(row.id, {
investmentAuthorizationRef:
e.target.value,
})
}
/>
</label>
<label>
Âmbito aplicável: bens, operação,
período e condições
<input
className="block rounded border p-2"
value={row.investmentScope ?? ''}
onChange={(e) =>
change(row.id, {
investmentScope:
e.target.value,
})
}
/>
</label>
</>
)}
{isDevelopmentImport(row.treatment) && (
<>
<p className="md:col-span-2">
Pedido apresentado não equivale a
despacho favorável nem a concessão
aduaneira. Reutilize as referências dos
documentos; numa projecção, identifique
os pressupostos. O âmbito do despacho,
lista e plano conserva-se nesta
importação, sem o estender a outros bens
ou operações.
</p>
{importDevelopmentFields.map(
([field, label]) => (
<label key={field}>
{label}
<input
className="block rounded border p-2"
value={row[field] ?? ''}
onChange={(e) =>
change(row.id, {
[field]:
e.target.value,
})
}
/>
</label>
),
)}
{row.treatment ===
'un_development_reduction' && (
<>
<p className="md:col-span-2">
A redução de IVA acompanha a
percentagem de redução de
direitos, que não é a taxa
aduaneira. Informe o IVA sem
benefício já determinado ou
expressamente assumido; o portal
não presume taxa nem base
aduaneira. Resultado decimal
exacto, sem arredondamento
fiscal presumido.
</p>
<label>
Redução dos direitos (%
entre0e100, exclusiva)
<input
className="block rounded border p-2"
value={
row.developmentReductionPercent ??
''
}
onChange={(e) =>
change(row.id, {
developmentReductionPercent:
e.target.value,
})
}
/>
</label>
<label>
IVA desta importação sem
benefício (moeda indicada)
<input
className="block rounded border p-2"
value={
row.developmentNormalVat ??
''
}
onChange={(e) =>
change(row.id, {
developmentNormalVat:
e.target.value,
})
}
/>
</label>
<label>
Referência documental /
pressuposto do IVA sem benefício
<input
className="block rounded border p-2"
value={
row.developmentNormalVatRef ??
''
}
onChange={(e) =>
change(row.id, {
developmentNormalVatRef:
e.target.value,
})
}
/>
</label>
</>
)}
</>
)}
<label>
Moeda de origem
<input
className="block rounded border p-2"
value={row.currency}
onChange={(e) =>
change(row.id, {
currency: e.target.value,
})
}
/>
</label>
</div>
{rule ? (
<>
<p>{importRuleSource(row.treatment)}</p>
<fieldset>
<legend>
Condições de{rule.article}; respostas
conservadas ao mudar de ramo
</legend>
{rule.conditions.map(([key, label]) => (
<label className="block" key={key}>
{label}
<select
className="block rounded border p-2"
value={
row.conditions[key] ?? ''
}
onChange={(e) =>
change(row.id, {
conditions: {
...row.conditions,
[key]: e.target
.value,
},
})
}
>
<option value="">
Por indicar
</option>
<option value="yes">Sim</option>
<option value="no">Não</option>
</select>
</label>
))}
</fieldset>
</>
) : (
<p>
Escolha o ramo para apresentar apenas as
condições aplicáveis; as respostas anteriores
mantêm-se.
</p>
)}
<p>
Editar não substitui o resultado anterior. Ao
classificar novamente, a versão anterior passa ao
histórico.
</p>
</details>
);
})}
{!!book.rows.length && (
<button
type="button"
className="rounded bg-emerald-800 p-2 text-white"
onClick={() => {
try {
save(recordImportExemptions(draft, book));
} catch (cause) {
setError(String(cause));
}
}}
>
{book.rows.some((row) => isImportService(row.treatment))
? 'Classificar importações e serviços e guardar versão'
: 'Classificar importações e guardar versão'}
</button>
)}
{book.result && (
<>
<p role="status">
{importExemptionCurrent(draft, book)
? 'Classificação correspondente às entradas guardadas.'
: 'Entradas alteradas; classificação histórica preservada. Reveja e classifique novamente.'}{' '}
{book.history?.length ?? 0} versões anteriores.
</p>
<dl>
{importExemptionReportRows(draft).map(
([, label, value, origin], index) => (
<div key={index}>
<dt className="font-semibold">{label}</dt>
<dd>
{value}
<small className="block">
{origin}
</small>
</dd>
</div>
),
)}
</dl>
</>
)}
</details>
);
}