Files
Investidor-V2/resources/js/components/simulation-remission-imports.tsx
T

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