Files
Investidor-V2/resources/js/components/simulation-currency-events.tsx
T

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