518 lines
28 KiB
TypeScript
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>
|
|
);
|
|
}
|