624 lines
35 KiB
TypeScript
624 lines
35 KiB
TypeScript
import { inputText } from '../lib/input-text.ts';
|
|
import { SimulationFxSelection } from './simulation-fx-selection';
|
|
import { useState } from 'react';
|
|
import {
|
|
currencyEventBook,
|
|
currencyEventCurrent,
|
|
currencyEventReportRows,
|
|
currencyEventSource,
|
|
currencyEventTreatments,
|
|
newCurrencyEvent,
|
|
recordCurrencyEvents,
|
|
saveCurrencyEventBook,
|
|
switchCurrencyEvent,
|
|
type CurrencyEventBook,
|
|
type CurrencyEventRow,
|
|
} from '@/lib/iva-currency-events';
|
|
export function SimulationCurrencyEvents({
|
|
draft,
|
|
onChange,
|
|
}: {
|
|
draft: Record<string, unknown>;
|
|
onChange: (field: string, value: string) => void;
|
|
}) {
|
|
const [error, setError] = useState('');
|
|
let book: CurrencyEventBook;
|
|
try {
|
|
book = currencyEventBook(draft);
|
|
} catch (e) {
|
|
return <p role="alert">{String(e)}</p>;
|
|
}
|
|
const save = (next: CurrencyEventBook) => {
|
|
try {
|
|
onChange('modelo_a_snapshot', saveCurrencyEventBook(draft, next));
|
|
setError('');
|
|
} catch (e) {
|
|
setError(String(e));
|
|
}
|
|
};
|
|
const change = (id: string, patch: Partial<CurrencyEventRow>) =>
|
|
save({
|
|
...book,
|
|
rows: book.rows.map((r) => (r.id === id ? { ...r, ...patch } : r)),
|
|
});
|
|
return (
|
|
<details className="rounded border p-3">
|
|
<summary>Moeda e datas do facto gerador/exigibilidade</summary>
|
|
<p>
|
|
Facto gerador e exigibilidade são estados separados. Identifique
|
|
a operação já preparada,sem repetir os seus cálculos. P104
|
|
permite a selecção operacional definida pela Tempo;os cenários
|
|
antigos permanecem condicionados até selecção explícita,sem
|
|
recálculo de históricos.
|
|
</p>
|
|
<p>{currencyEventSource}</p>
|
|
{error && <p role="alert">{error}</p>}
|
|
<button
|
|
type="button"
|
|
onClick={() =>
|
|
save({
|
|
...book,
|
|
rows: [
|
|
...book.rows,
|
|
newCurrencyEvent(
|
|
crypto.randomUUID(),
|
|
inputText(draft.currency ?? ''),
|
|
),
|
|
],
|
|
})
|
|
}
|
|
>
|
|
Acrescentar moeda/evento
|
|
</button>
|
|
{book.rows.map((row) => (
|
|
<details key={row.id} className="rounded border p-3">
|
|
<summary>
|
|
{row.description || 'Operação por identificar'} —{' '}
|
|
{row.period || 'mês por indicar'}
|
|
</summary>
|
|
<label>
|
|
Percurso de moeda/evento
|
|
<select
|
|
value={row.treatment}
|
|
onChange={(e) =>
|
|
change(
|
|
row.id,
|
|
switchCurrencyEvent(row, e.target.value),
|
|
)
|
|
}
|
|
>
|
|
<option value="">Por indicar</option>
|
|
{currencyEventTreatments.map((t) => (
|
|
<option key={t.id} value={t.id}>
|
|
{t.label}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</label>
|
|
<label>
|
|
Natureza dos dados
|
|
<select
|
|
value={row.origin}
|
|
onChange={(e) =>
|
|
change(row.id, {
|
|
origin: e.target
|
|
.value as CurrencyEventRow['origin'],
|
|
})
|
|
}
|
|
>
|
|
<option value="projected">
|
|
Projectados / pressupostos
|
|
</option>
|
|
<option value="effective">
|
|
Efectivos declarados
|
|
</option>
|
|
</select>
|
|
</label>
|
|
{(
|
|
[
|
|
['description', 'Operação identificada'],
|
|
[
|
|
'operationRef',
|
|
'Referência da operação existente',
|
|
],
|
|
['period', 'Mês do contexto'],
|
|
[
|
|
'documentRef',
|
|
'Documento / referência da previsão',
|
|
],
|
|
['documentDate', 'Data do documento'],
|
|
['scope', 'Âmbito e parcelas abrangidas'],
|
|
['evidence', 'Factos ou pressupostos expressos'],
|
|
['amount', 'Montante total do âmbito identificado'],
|
|
[
|
|
'currency',
|
|
'Moeda do montante / destino da conversão',
|
|
],
|
|
] as const
|
|
).map(([field, label]) => (
|
|
<label className="block" key={field}>
|
|
{label}
|
|
<input
|
|
value={row[field]}
|
|
type={
|
|
field === 'period'
|
|
? 'month'
|
|
: field === 'documentDate'
|
|
? 'date'
|
|
: 'text'
|
|
}
|
|
onChange={(e) =>
|
|
change(row.id, { [field]: e.target.value })
|
|
}
|
|
/>
|
|
</label>
|
|
))}
|
|
<label>
|
|
Factos próprios correspondem ao percurso,operação e
|
|
âmbito
|
|
<select
|
|
value={row.factsConfirmed}
|
|
onChange={(e) =>
|
|
change(row.id, {
|
|
factsConfirmed: e.target
|
|
.value as CurrencyEventRow['factsConfirmed'],
|
|
})
|
|
}
|
|
>
|
|
<option value="">Por esclarecer</option>
|
|
<option value="yes">Sim</option>
|
|
<option value="no">Não</option>
|
|
</select>
|
|
</label>
|
|
{row.treatment === 'foreign_scenario' ? (
|
|
<>
|
|
<SimulationFxSelection
|
|
row={row}
|
|
onChange={(patch) => change(row.id, patch)}
|
|
/>
|
|
{(
|
|
[
|
|
['sourceCurrency', 'Moeda original'],
|
|
['fxRate', 'Taxa de conversão indicada'],
|
|
['fxDate', 'Data efectiva da taxa'],
|
|
['fxSource', 'Fonte e exemplar da taxa'],
|
|
[
|
|
'fxRuleRef',
|
|
'Regra/pressuposto da escolha da taxa e data',
|
|
],
|
|
] as const
|
|
).map(([field, label]) => (
|
|
<label className="block" key={field}>
|
|
{label}
|
|
<input
|
|
value={row[field]}
|
|
type={
|
|
field === 'fxDate' ? 'date' : 'text'
|
|
}
|
|
onChange={(e) =>
|
|
change(row.id, {
|
|
[field]: e.target.value,
|
|
})
|
|
}
|
|
/>
|
|
</label>
|
|
))}
|
|
<label>
|
|
Direcção da taxa
|
|
<select
|
|
value={row.fxDirection}
|
|
onChange={(e) =>
|
|
change(row.id, {
|
|
fxDirection: e.target
|
|
.value as CurrencyEventRow['fxDirection'],
|
|
})
|
|
}
|
|
>
|
|
<option value="">Por indicar</option>
|
|
<option value="mzn_per_unit">
|
|
MZN por unidade da moeda original
|
|
</option>
|
|
<option value="units_per_mzn">
|
|
Unidades da moeda original por MZN
|
|
</option>
|
|
</select>
|
|
</label>
|
|
<p>
|
|
{row.fxSelection?.applied
|
|
? 'Conversão operacional após selecção explícita da taxa.'
|
|
: 'Cenário histórico condicionado;selecção operacional ainda não aplicada.'}{' '}
|
|
Não activa base fiscal,IVA ou dedução,nem altera
|
|
os livros anteriores.
|
|
</p>
|
|
</>
|
|
) : (
|
|
row.treatment && (
|
|
<>
|
|
{(
|
|
[
|
|
[
|
|
'eventDate',
|
|
'Data própria do evento escolhido',
|
|
],
|
|
[
|
|
'eventRef',
|
|
'Referência e factos do evento7/8',
|
|
],
|
|
[
|
|
'reviewDate',
|
|
'Data até à qual os eventos foram conferidos',
|
|
],
|
|
] as const
|
|
)
|
|
.filter(
|
|
([field]) =>
|
|
field !== 'eventDate' ||
|
|
![
|
|
'advance_payment',
|
|
'consignment_deadline',
|
|
].includes(row.treatment),
|
|
)
|
|
.map(([field, label]) => (
|
|
<label className="block" key={field}>
|
|
{label}
|
|
<input
|
|
value={row[field]}
|
|
type={
|
|
field === 'eventRef'
|
|
? 'text'
|
|
: 'date'
|
|
}
|
|
onChange={(e) =>
|
|
change(row.id, {
|
|
[field]: e.target.value,
|
|
})
|
|
}
|
|
/>
|
|
</label>
|
|
))}
|
|
{[
|
|
'consignment_deadline',
|
|
'service_early_charge',
|
|
'contract_effects',
|
|
].includes(row.treatment) && (
|
|
<label>
|
|
{row.treatment ===
|
|
'consignment_deadline'
|
|
? 'Entrega ao destinatário da consignação'
|
|
: row.treatment ===
|
|
'service_early_charge'
|
|
? 'Data de realização do serviço posterior à cobrança/débito'
|
|
: 'Disponibilização anterior aos efeitos translativos'}
|
|
<input
|
|
type="date"
|
|
value={row.otherDate}
|
|
onChange={(e) =>
|
|
change(row.id, {
|
|
otherDate: e.target.value,
|
|
})
|
|
}
|
|
/>
|
|
</label>
|
|
)}
|
|
{row.treatment === 'contract_effects' && (
|
|
<p>
|
|
Este percurso7(7) exclui locação
|
|
vinculativa e venda com reserva3(3)a/b.
|
|
Confirme essa distinção nos factos.
|
|
</p>
|
|
)}
|
|
{row.treatment === 'continuous_period' && (
|
|
<p>
|
|
Use o termo do período a que respeita
|
|
cada pagamento sucessivo,não a data
|
|
arbitrária de emissão.
|
|
</p>
|
|
)}
|
|
{row.treatment === 'consignment_deadline' && (
|
|
<p>
|
|
7(6) fixa exigibilidade no termo180dias
|
|
da consignação. P123 conserva a
|
|
conferência da não
|
|
devolução;embalagensP102 são distintas.
|
|
</p>
|
|
)}
|
|
{![
|
|
'import_clearance',
|
|
'import_auction',
|
|
].includes(row.treatment) && (
|
|
<>
|
|
<label>
|
|
Operação sujeita à obrigação de
|
|
emitir factura25
|
|
<select
|
|
value={row.invoiceRequired}
|
|
onChange={(e) =>
|
|
change(row.id, {
|
|
invoiceRequired: e
|
|
.target
|
|
.value as CurrencyEventRow['invoiceRequired'],
|
|
})
|
|
}
|
|
>
|
|
<option value="">
|
|
Por esclarecer
|
|
</option>
|
|
<option value="yes">Sim</option>
|
|
<option value="no">Não</option>
|
|
</select>
|
|
</label>
|
|
{row.invoiceRequired === 'yes' && (
|
|
<>
|
|
{(
|
|
[
|
|
[
|
|
'legalDeadline',
|
|
'Termo legal do prazo de emissão aplicável',
|
|
],
|
|
[
|
|
'deadlineRef',
|
|
'Disposição,calendário e âmbito que fundamentam esse prazo',
|
|
],
|
|
] as const
|
|
).map(([field, label]) => (
|
|
<label
|
|
className="block"
|
|
key={field}
|
|
>
|
|
{label}
|
|
<input
|
|
value={row[field]}
|
|
type={
|
|
field ===
|
|
'legalDeadline'
|
|
? 'date'
|
|
: 'text'
|
|
}
|
|
onChange={(e) =>
|
|
change(row.id, {
|
|
[field]:
|
|
e.target
|
|
.value,
|
|
})
|
|
}
|
|
/>
|
|
</label>
|
|
))}
|
|
<label>
|
|
Facturação até à conferência
|
|
<select
|
|
value={row.invoiceState}
|
|
onChange={(e) =>
|
|
change(row.id, {
|
|
invoiceState: e
|
|
.target
|
|
.value as CurrencyEventRow['invoiceState'],
|
|
})
|
|
}
|
|
>
|
|
<option value="">
|
|
Por esclarecer
|
|
</option>
|
|
<option value="issued">
|
|
Factura emitida
|
|
</option>
|
|
<option value="not_issued">
|
|
Factura não emitida
|
|
</option>
|
|
</select>
|
|
</label>
|
|
{row.invoiceState ===
|
|
'issued' && (
|
|
<>
|
|
{(
|
|
[
|
|
[
|
|
'invoiceDate',
|
|
'Data de emissão da factura',
|
|
],
|
|
[
|
|
'invoiceRef',
|
|
'Referência da factura',
|
|
],
|
|
] as const
|
|
).map(
|
|
([
|
|
field,
|
|
label,
|
|
]) => (
|
|
<label
|
|
key={field}
|
|
>
|
|
{label}
|
|
<input
|
|
value={
|
|
row[
|
|
field
|
|
]
|
|
}
|
|
type={
|
|
field ===
|
|
'invoiceDate'
|
|
? 'date'
|
|
: 'text'
|
|
}
|
|
onChange={(
|
|
e,
|
|
) =>
|
|
change(
|
|
row.id,
|
|
{
|
|
[field]:
|
|
e
|
|
.target
|
|
.value,
|
|
},
|
|
)
|
|
}
|
|
/>
|
|
</label>
|
|
),
|
|
)}
|
|
</>
|
|
)}
|
|
<label>
|
|
Recebimentos neste âmbito
|
|
<select
|
|
value={row.paymentState}
|
|
onChange={(e) =>
|
|
change(row.id, {
|
|
paymentState: e
|
|
.target
|
|
.value as CurrencyEventRow['paymentState'],
|
|
})
|
|
}
|
|
>
|
|
<option value="">
|
|
Por esclarecer
|
|
</option>
|
|
<option value="none">
|
|
Sem recebimento
|
|
</option>
|
|
<option value="received">
|
|
Recebimento
|
|
identificado
|
|
</option>
|
|
</select>
|
|
</label>
|
|
{row.paymentState ===
|
|
'received' && (
|
|
<>
|
|
{(
|
|
[
|
|
[
|
|
'paymentDate',
|
|
'Data de recebimento',
|
|
],
|
|
[
|
|
'paymentAmount',
|
|
'Montante recebido',
|
|
],
|
|
[
|
|
'paymentRef',
|
|
'Referência e âmbito do recebimento',
|
|
],
|
|
] as const
|
|
).map(
|
|
([
|
|
field,
|
|
label,
|
|
]) => (
|
|
<label
|
|
className="block"
|
|
key={field}
|
|
>
|
|
{label}
|
|
<input
|
|
value={
|
|
row[
|
|
field
|
|
]
|
|
}
|
|
type={
|
|
field ===
|
|
'paymentDate'
|
|
? 'date'
|
|
: 'text'
|
|
}
|
|
onChange={(
|
|
e,
|
|
) =>
|
|
change(
|
|
row.id,
|
|
{
|
|
[field]:
|
|
e
|
|
.target
|
|
.value,
|
|
},
|
|
)
|
|
}
|
|
/>
|
|
</label>
|
|
),
|
|
)}
|
|
</>
|
|
)}
|
|
<p>
|
|
O recebimento anterior é
|
|
separado do restante,sem
|
|
dupla contagem. Para
|
|
parcelas com factos
|
|
distintos,use âmbitos
|
|
distintos e conserve as
|
|
referências.
|
|
</p>
|
|
</>
|
|
)}
|
|
</>
|
|
)}
|
|
</>
|
|
)
|
|
)}
|
|
<p>
|
|
Trocar percurso conserva dados ocultos e
|
|
referências;reveja as confirmações aplicáveis.
|
|
</p>
|
|
</details>
|
|
))}
|
|
{!!book.rows.length && (
|
|
<button
|
|
type="button"
|
|
onClick={() => {
|
|
try {
|
|
save(recordCurrencyEvents(draft, book));
|
|
} catch (e) {
|
|
setError(String(e));
|
|
}
|
|
}}
|
|
>
|
|
Conferir moeda/eventos e guardar versão
|
|
</button>
|
|
)}
|
|
{book.result && (
|
|
<>
|
|
<p role="status">
|
|
{currencyEventCurrent(draft, book)
|
|
? 'Resultados correspondentes às entradas guardadas.'
|
|
: 'Entradas alteradas;histórico conservado.'}{' '}
|
|
{book.history?.length ?? 0} versões anteriores.P104
|
|
conserva a natureza operacional ou condicionada indicada
|
|
em cada resultado;semIVA/base/dedução activados.
|
|
</p>
|
|
<dl>
|
|
{currencyEventReportRows(draft).map(
|
|
([, label, value, origin], i) => (
|
|
<div key={i}>
|
|
<dt>{label}</dt>
|
|
<dd>
|
|
{value}
|
|
{label.startsWith('P104') ? (
|
|
<details>
|
|
<summary>
|
|
Fonte,âmbito e versão
|
|
</summary>
|
|
<small className="block">
|
|
{origin}
|
|
</small>
|
|
</details>
|
|
) : (
|
|
<small className="block">
|
|
{origin}
|
|
</small>
|
|
)}
|
|
</dd>
|
|
</div>
|
|
),
|
|
)}
|
|
</dl>
|
|
</>
|
|
)}
|
|
</details>
|
|
);
|
|
}
|