516 lines
25 KiB
TypeScript
516 lines
25 KiB
TypeScript
import { inputText } from '../lib/input-text.ts';
|
|
import { useState } from 'react';
|
|
import {
|
|
remissionBook,
|
|
saveRemissionBook,
|
|
newRemission,
|
|
remissionFamilies,
|
|
diplomaticRoles,
|
|
ippItems,
|
|
ippAuthority,
|
|
remissionConditions,
|
|
recordRemissions,
|
|
remissionCurrent,
|
|
remissionReportRows,
|
|
remissionSource,
|
|
type RemissionBook,
|
|
type RemissionRow,
|
|
} from '@/lib/iva-remission-imports';
|
|
import { objectiveImportBook } from '@/lib/iva-objective-imports';
|
|
export function SimulationRemissionImports({
|
|
draft,
|
|
onChange,
|
|
}: {
|
|
draft: Record<string, unknown>;
|
|
onChange: (field: string, value: string) => void;
|
|
}) {
|
|
const [error, setError] = useState('');
|
|
let book: RemissionBook;
|
|
try {
|
|
book = remissionBook(draft);
|
|
} catch (e) {
|
|
return <p role="alert">{String(e)}</p>;
|
|
}
|
|
const save = (b: RemissionBook) => {
|
|
try {
|
|
onChange('modelo_a_snapshot', saveRemissionBook(draft, b));
|
|
setError('');
|
|
} catch (e) {
|
|
setError(String(e));
|
|
}
|
|
};
|
|
const change = (id: string, patch: Partial<RemissionRow>) =>
|
|
save({
|
|
...book,
|
|
rows: book.rows.map((r) => (r.id === id ? { ...r, ...patch } : r)),
|
|
});
|
|
let sources: ReturnType<typeof objectiveImportBook>['rows'] = [];
|
|
try {
|
|
sources = objectiveImportBook(draft).rows;
|
|
} catch {
|
|
/* Direct entry remains available; source originals stay untouched. */
|
|
}
|
|
return (
|
|
<details className="rounded border p-3">
|
|
<summary>
|
|
Importações — quatro famílias de isenção por remissão (P047)
|
|
</summary>
|
|
<p>
|
|
Confirme o fundamento e o âmbito da isenção de direitos. Taxa
|
|
pautal de 0%, redução e pedido apresentado são situações
|
|
distintas. Uma previsão de deferimento produz apenas um cenário
|
|
condicionado. O portal não concede benefícios aduaneiros nem
|
|
calcula direitos, ICE ou dedução.
|
|
</p>
|
|
{error && <p role="alert">{error}</p>}
|
|
<button
|
|
type="button"
|
|
onClick={() => {
|
|
const r = newRemission(
|
|
crypto.randomUUID(),
|
|
inputText(draft.currency ?? ''),
|
|
);
|
|
const known = book.rows
|
|
.map((v) => v.importer)
|
|
.filter(Boolean);
|
|
if (new Set(known).size === 1) r.importer = known[0];
|
|
save({ ...book, rows: [...book.rows, r] });
|
|
}}
|
|
>
|
|
Acrescentar importação por remissão
|
|
</button>
|
|
{book.rows.map((row) => {
|
|
const text = (
|
|
field: Exclude<keyof RemissionRow, 'conditions'>,
|
|
label: string,
|
|
type = 'text',
|
|
) => (
|
|
<label className="block" key={field}>
|
|
{label}
|
|
<input
|
|
type={type}
|
|
value={row[field]}
|
|
onChange={(e) =>
|
|
change(row.id, { [field]: e.target.value })
|
|
}
|
|
/>
|
|
</label>
|
|
);
|
|
const select = (
|
|
field: Exclude<keyof RemissionRow, 'conditions'>,
|
|
label: string,
|
|
options: ReadonlyArray<readonly [string, string]>,
|
|
clear = false,
|
|
) => (
|
|
<label className="block">
|
|
{label}
|
|
<select
|
|
value={row[field]}
|
|
onChange={(e) =>
|
|
change(row.id, {
|
|
[field]: e.target.value,
|
|
...(clear ? { conditions: {} } : {}),
|
|
})
|
|
}
|
|
>
|
|
<option value="">Por indicar</option>
|
|
{options.map(([v, l]) => (
|
|
<option key={v} value={v}>
|
|
{l}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</label>
|
|
);
|
|
return (
|
|
<details key={row.id}>
|
|
<summary>
|
|
{row.description || 'Importação por preparar'} —{' '}
|
|
{row.period || 'período por indicar'}
|
|
</summary>
|
|
{sources.length > 0 && (
|
|
<label>
|
|
Reutilizar dados de importação preparada
|
|
<select
|
|
value=""
|
|
onChange={(e) => {
|
|
const s = sources.find(
|
|
(v) => v.id === e.target.value,
|
|
);
|
|
if (s)
|
|
change(row.id, {
|
|
description:
|
|
row.description ||
|
|
s.description,
|
|
importer:
|
|
row.importer || s.importer,
|
|
period: row.period || s.period,
|
|
documentRef:
|
|
row.documentRef ||
|
|
s.documentRef,
|
|
evidence:
|
|
row.evidence || s.evidence,
|
|
});
|
|
}}
|
|
>
|
|
<option value="">
|
|
Preencher apenas campos em falta
|
|
</option>
|
|
{sources.map((s) => (
|
|
<option key={s.id} value={s.id}>
|
|
{s.description}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</label>
|
|
)}
|
|
{text('description', 'Bens identificados')}
|
|
{text('importer', 'Importador / beneficiário')}
|
|
{text('period', 'Período da importação', 'month')}
|
|
{text(
|
|
'importDate',
|
|
'Data da importação ou previsão',
|
|
'date',
|
|
)}
|
|
{select('origin', 'Natureza dos dados', [
|
|
['projected', 'Pressupostos de projecção'],
|
|
['effective', 'Factos efectivos declarados'],
|
|
])}
|
|
{text(
|
|
'documentRef',
|
|
'Documentos disponíveis ou previsão',
|
|
)}
|
|
{text(
|
|
'scope',
|
|
'Âmbito: bens, operação, período e condições',
|
|
)}
|
|
{text('evidence', 'Factos ou pressupostos expressos')}
|
|
{select(
|
|
'family',
|
|
'Família legal',
|
|
remissionFamilies,
|
|
true,
|
|
)}
|
|
{row.family === 'patronage' && (
|
|
<>
|
|
{select(
|
|
'patronagePath',
|
|
'Percurso do mecenato',
|
|
[
|
|
[
|
|
'book',
|
|
'Livro cultural/científico/escolar ou insumo de produção nacional — art7',
|
|
],
|
|
[
|
|
'donation',
|
|
'Liberalidade de livros/insumos no regulamento — arts10/11',
|
|
],
|
|
[
|
|
'specific',
|
|
'Outro fundamento expresso de isenção de direitos',
|
|
],
|
|
],
|
|
true,
|
|
)}
|
|
{['donation', 'specific'].includes(
|
|
row.patronagePath,
|
|
) &&
|
|
text(
|
|
'specialRef',
|
|
row.patronagePath === 'donation'
|
|
? 'Acção/liberalidade: artigo4, documentos, comunicações, termo e confirmação nos termos do regulamento'
|
|
: 'Disposição expressa, bens e âmbito: não basta doação genérica',
|
|
)}
|
|
</>
|
|
)}
|
|
{row.family === 'ipp' && (
|
|
<>
|
|
{select(
|
|
'ippItem',
|
|
'Item das IPP / Quadro V',
|
|
[
|
|
...ippItems.map(
|
|
(i) =>
|
|
[
|
|
i.id,
|
|
`${i.id} — ${i.label}`,
|
|
] as const,
|
|
),
|
|
[
|
|
'specific',
|
|
'Outro artigo específico das IPP',
|
|
],
|
|
],
|
|
true,
|
|
)}
|
|
{(row.ippItem === 'specific' ||
|
|
(row.ippItem === '30' &&
|
|
row.decisionDate < '2026-07-31')) &&
|
|
text(
|
|
'ippArticle',
|
|
'Disposição, versão e trecho próprios; competência anterior a31/07 quando aplicável',
|
|
)}
|
|
{text(
|
|
'decisionDate',
|
|
'Data da decisão ou previsão para determinar a competência',
|
|
'date',
|
|
)}
|
|
{text(
|
|
'specialRef',
|
|
'Referências das condições/remissões e compromisso de uso; confirmação de tutela apenas quando prevista',
|
|
)}
|
|
<p>
|
|
Competência pela data da decisão/previsão:{' '}
|
|
{row.decisionDate
|
|
? ippAuthority(
|
|
row.ippItem,
|
|
row.decisionDate,
|
|
)
|
|
: 'Data por indicar'}
|
|
. A inclusão no quadro não concede isenção
|
|
automaticamente. Itens28/30 com encargos
|
|
suportados pelo Estado não equivalem, só por
|
|
isso, a isenção de direitos.
|
|
</p>
|
|
</>
|
|
)}
|
|
{row.family === 'diplomatic' && (
|
|
<>
|
|
{select(
|
|
'role',
|
|
'Categoria diplomática',
|
|
diplomaticRoles,
|
|
true,
|
|
)}
|
|
{['agency', 'organisation'].includes(
|
|
row.role,
|
|
) ? (
|
|
<>
|
|
{text(
|
|
'agreementRef',
|
|
'Acordo de Sede ou específico: cláusula e âmbito',
|
|
)}
|
|
{text(
|
|
'specialRef',
|
|
'Condições e limites próprios do acordo; sem quotas presumidas',
|
|
)}
|
|
</>
|
|
) : (
|
|
row.role && (
|
|
<>
|
|
{text(
|
|
'quantity',
|
|
'Veículos desta linha',
|
|
'number',
|
|
)}
|
|
{text(
|
|
'existingQuantity',
|
|
'Veículos já abrangidos no limite, fora destas linhas',
|
|
'number',
|
|
)}
|
|
{text(
|
|
'limit',
|
|
row.role === 'mission'
|
|
? 'Limite fixado caso a caso pelo MINEC'
|
|
: 'Limite aplicável pela reciprocidade, até ao máximo legal',
|
|
'number',
|
|
)}
|
|
{text(
|
|
'limitRef',
|
|
'Referência do limite/reciprocidade e restrições aplicáveis',
|
|
)}
|
|
{select(
|
|
'history',
|
|
'Importação anterior no mesmo direito',
|
|
[
|
|
[
|
|
'none',
|
|
'Nenhuma, segundo histórico identificado',
|
|
],
|
|
[
|
|
'known',
|
|
'Sim, data conhecida',
|
|
],
|
|
],
|
|
)}
|
|
{row.history === 'known' &&
|
|
text(
|
|
'previousDate',
|
|
'Data da importação anterior',
|
|
'date',
|
|
)}
|
|
{text(
|
|
'historyRef',
|
|
'Histórico, incluindo operações fora deste dossier',
|
|
)}
|
|
<p>
|
|
Decreto22/2021: chefe até3;
|
|
agente/cônsul de carreira até2;
|
|
honorário e pessoal
|
|
administrativo/técnico até1. A
|
|
missão tem limite próprio. Nova
|
|
importação após5anos; alienação
|
|
e reexportação conservam regime
|
|
próprio, sem cálculo neste
|
|
percurso.
|
|
</p>
|
|
</>
|
|
)
|
|
)}
|
|
</>
|
|
)}
|
|
{remissionConditions(row).map(([key, label]) => (
|
|
<label className="block" key={key}>
|
|
{label}
|
|
<select
|
|
value={row.conditions[key] ?? ''}
|
|
onChange={(e) =>
|
|
change(row.id, {
|
|
conditions: {
|
|
...row.conditions,
|
|
[key]: e.target.value as
|
|
| ''
|
|
| 'yes'
|
|
| 'no',
|
|
},
|
|
})
|
|
}
|
|
>
|
|
<option value="">Por esclarecer</option>
|
|
<option value="yes">
|
|
Sim, nos factos ou pressupostos
|
|
identificados
|
|
</option>
|
|
<option value="no">Não</option>
|
|
</select>
|
|
</label>
|
|
))}
|
|
{row.family && (
|
|
<>
|
|
{select(
|
|
'rights',
|
|
'Situação dos direitos de importação',
|
|
[
|
|
[
|
|
'exempt',
|
|
'Isenção integral demonstrada ou pressuposto expresso',
|
|
],
|
|
['reduced', 'Redução parcial'],
|
|
['zero', 'Taxa pautal de 0%'],
|
|
['none', 'Sem isenção'],
|
|
],
|
|
)}
|
|
{text(
|
|
'rightsRef',
|
|
'Fundamento exacto da isenção de direitos e âmbito ou pressuposto expresso',
|
|
)}
|
|
{(['ipp', 'diplomatic'].includes(row.family) ||
|
|
(row.family === 'patronage' &&
|
|
row.patronagePath === 'donation')) &&
|
|
text(
|
|
'requestRef',
|
|
row.family === 'diplomatic'
|
|
? 'Pedido caso a caso através do MINEC ou previsão'
|
|
: 'Requerimento/comunicação prévia ou previsão',
|
|
)}
|
|
{select(
|
|
'decision',
|
|
'Situação da decisão / aplicação legal',
|
|
[
|
|
...(row.family === 'party' ||
|
|
(row.family === 'patronage' &&
|
|
row.patronagePath !== 'donation')
|
|
? [
|
|
[
|
|
'not_required',
|
|
'Aplicação legal directa; não é criada aprovação adicional',
|
|
] as const,
|
|
]
|
|
: []),
|
|
[
|
|
'planned',
|
|
'Deferimento previsto — apenas cenário condicionado',
|
|
],
|
|
[
|
|
'requested',
|
|
'Pedido apresentado — benefício solicitado',
|
|
],
|
|
[
|
|
'approved',
|
|
'Decisão / confirmação recebida',
|
|
],
|
|
['denied', 'Indeferido'],
|
|
],
|
|
)}
|
|
{row.decision !== 'not_required' &&
|
|
text(
|
|
'decisionRef',
|
|
'Referência da decisão / pressuposto; entidade, data e âmbito abrangido',
|
|
)}
|
|
<p>
|
|
Mudar família ou subpercurso conserva campos
|
|
ocultos e limpa confirmações. Documentos e
|
|
históricos permanecem guardados. A submissão
|
|
e a decisão administrativas ocorrem fora do
|
|
InvesteMoz.
|
|
</p>
|
|
</>
|
|
)}
|
|
</details>
|
|
);
|
|
})}
|
|
{!!book.rows.length && (
|
|
<button
|
|
type="button"
|
|
onClick={() => {
|
|
try {
|
|
save(recordRemissions(draft, book));
|
|
} catch (e) {
|
|
setError(String(e));
|
|
}
|
|
}}
|
|
>
|
|
Conferir quatro famílias e guardar versão
|
|
</button>
|
|
)}
|
|
{book.result && (
|
|
<>
|
|
<p role="status">
|
|
{remissionCurrent(draft, book)
|
|
? 'Resultado corresponde às entradas guardadas.'
|
|
: 'Entradas alteradas; resultado histórico conservado.'}{' '}
|
|
{book.history?.length ?? 0} versões anteriores.
|
|
</p>
|
|
<dl>
|
|
{remissionReportRows(draft).map(
|
|
([, label, value, source], i) => (
|
|
<div key={i}>
|
|
<dt>{label}</dt>
|
|
<dd>
|
|
{value}
|
|
<small className="block">
|
|
{source}
|
|
</small>
|
|
</dd>
|
|
</div>
|
|
),
|
|
)}
|
|
</dl>
|
|
</>
|
|
)}
|
|
<details>
|
|
<summary>Fontes e limites de cobertura</summary>
|
|
<p>{remissionSource}</p>
|
|
<p>
|
|
Avalia o IVA por remissão quando a isenção de direitos está
|
|
demonstrada. Não substitui a classificação pautal nem
|
|
automatiza todos os procedimentos dos regimes aduaneiros
|
|
referidos. As condições específicas não identificadas
|
|
permanecem indeterminadas. Sem cálculo de alienação,
|
|
direitos, ICE ou dedução.
|
|
</p>
|
|
</details>
|
|
</details>
|
|
);
|
|
}
|