Files
Investidor-V2/resources/js/lib/l3-form-preparation.ts
T

254 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { closureBook, closureCurrent } from './iva-closure.ts';
import { substitutionBook, substitutionCurrent } from './iva-substitution.ts';
import { refundBook, refundCurrent } from './iva-refund.ts';
import {
beverageRestitutionBook,
beverageRestitutionCurrent,
} from './iva-beverage-restitution.ts';
import {
fractionPresentation,
fractionLabel,
type FiscalFraction,
} from './fiscal-rational.ts';
export const l3FormReference = {
file: 'VAT Return.pdf',
sha256: '9c0b99a810c7db53eb75d63c22ee22fb86a50ba57bd5509041ed80ee10f75741',
page: 2,
scope: 'Preparação dos campos identificados dos quadros8–11 do exemplar recebido;edição/aceitação electrónica não estabelecida.',
};
export const l3FormalitiesDependencies = [
{
branch: 'A/B',
identified:
'Quadros8/10/11 e campos19/20/22–27 do exemplar recebido,PDF2;CIVA21(2/5)/23(1);RCIVA12/15.',
missing:
'Identificação da edição electrónica aplicável e dados de autenticação/meio de pagamento não inferidos dos recibos.',
trigger:
'Exemplar/instrução da edição electrónica ou dados do sujeito passivo;nenhuma assinatura ou submissão automática.',
},
{
branch: 'C',
identified:
'Quadro9/campo21 e transcrição positiva para22 do exemplar recebido,PDF2.',
missing:
'RCIVA15 refereModeloB;exemplar recebido assinala substituiçãoModeloA. Correspondência de edição/formulário regulamentar por resolver.',
trigger:
'ModeloB/instrução que esclareça a correspondência com a edição recebida. Preservar cálculo e preparação de21–24.',
},
{
branch: 'D',
identified:
'Quadro11:25=26+27;pedido27 não volta a19 sem comunicação. Remissãoart20 impressa conservada como divergência;regime2026 assenta em21.',
missing:
'Exemplares e instruções regulamentares específicos de reembolso:Decretos78/2017,30/2022 e52/2025 identificados na análise anterior;sem correspondência de campos demonstrada.',
trigger:
'Texto/modelo/instrução integral novo ou já fornecido;sem repetir a pesquisa anterior sem nova evidência. Preparação26/27 não equivale ao pedido regulamentar completo.',
},
{
branch: 'E',
identified:
'CIVA13(1)c/d/h e13(2–3);pedido,decisão e recebimento separados no dossier.',
missing:
'Modelo e procedimento específico de restituição de bebidas por identificar. Não usar27 como se fosse a restituição13(2).',
trigger:
'Exemplar ou instrução específica de13(2);sem importar limiares,prazos ou modelos do reembolso geralD.',
},
] as const;
export type L3FormPreparation = {
id: string;
branch: string;
period: string;
entity: string;
current: boolean;
reference: string;
status: string;
fields: Array<{
field: string;
label: string;
value: string;
exact: string;
}>;
notes: string[];
};
const fmt = (v: FiscalFraction | null | undefined) =>
v ? fractionPresentation(v).decimal : 'Indeterminado';
const cell = (
field: string,
label: string,
v: FiscalFraction | null | undefined,
) => ({
field,
label,
value: fmt(v),
exact: v ? fractionLabel(v) : 'Indeterminado',
});
export function l3FormPreparations(
d: Record<string, unknown>,
): L3FormPreparation[] {
const out: L3FormPreparation[] = [],
ab = closureBook(d),
c = substitutionBook(d),
rd = refundBook(d),
e = beverageRestitutionBook(d);
for (const r of ab.result?.rows ?? []) {
const input = ab.result!.inputs.find((i) => i.id === r.id);
out.push({
id: `AB:${r.id}`,
branch: 'A/B',
period: r.period,
entity: r.entityRef,
current: closureCurrent(d, ab),
reference: `${l3FormReference.file};SHA256${l3FormReference.sha256};PDF2/quadros8,10,11`,
status: r.status,
fields: [
['19', 'Excesso do período anterior'],
['20', 'Créditos comunicados'],
['22', 'IVA a entregar'],
['23', 'Juros compensatórios'],
['24', 'Importância a pagar'],
['25', 'Crédito de imposto'],
['26', 'Reporte seguinte'],
['27', 'Pedido de reembolso'],
].map(([f, label]) => cell(f, label, r.fields[f])),
notes: [
`Versão congelada${ab.result!.calculatedAt};${input?.origin ?? ''};fonteL2${input?.source?.revision ?? ''}.`,
'Cabeçalho refere18/19,mas caixas19/20;original não renumerado. Meio de pagamento,assinatura e uso dos serviços ficam por preencher.',
'27 é afectação a pedido geral,não decisão nem recebimento;05/06 permanecem fora desta preparaçãoL3.',
],
});
}
for (const r of c.result?.rows ?? []) {
const input = c.result!.inputs.find((i) => i.id === r.id);
out.push({
id: `C:${r.id}`,
branch: 'C',
period: input?.prior?.period ?? '',
entity: input?.prior?.entityRef ?? '',
current: substitutionCurrent(d, c),
reference: `${l3FormReference.file};PDF2/quadro9;${l3FormalitiesDependencies[1].missing}`,
status: r.status,
fields: [
['21', 'Diferencial'],
['22', 'Diferencial devedor'],
['23', 'Juros adicionais'],
['24', 'Importância adicional a pagar'],
].map(([f, label]) => cell(f, label, r.fields[f])),
notes: [
`Anterior${input?.priorReference ?? ''};${input?.priorDate ?? ''};substituição${input?.reference ?? ''};${input?.date ?? ''};${input?.stage ?? ''}.`,
r.reason,
'Crédito credor aguarda comunicação20;pagamentos anteriores conservam-se e não voltam a ser inscritos como pagamento adicional.',
],
});
}
for (const r of rd.result?.rows ?? []) {
const input = rd.result!.inputs.find((i) => i.id === r.id);
out.push({
id: `D:${r.id}`,
branch: 'D',
period: input?.source?.period ?? '',
entity: input?.source?.entityRef ?? '',
current: refundCurrent(d, rd),
reference: `${l3FormReference.file};PDF2/quadro11;${l3FormalitiesDependencies[2].missing}`,
status: r.status,
fields: [
cell('25', 'Crédito de imposto', input?.source?.credit),
cell('26', 'Reporte separado', r.reported),
cell('27', 'Pedido de reembolso', r.requested),
],
notes: [
`Pedido${input?.requestReference ?? ''};${input?.requestDate ?? ''};${input?.stage ?? ''}.`,
r.reason,
`Acompanhamento separado:reconhecido${fmt(r.approved)};recebido${fmt(r.received)};por receber${fmt(r.awaitingReceipt)}MZN. Estes valores não substituem27.`,
],
});
}
for (const r of e.result?.rows ?? []) {
const input = e.result!.inputs.find((i) => i.id === r.id);
out.push({
id: `E:${r.id}`,
branch: 'E',
period: input?.source?.period ?? '',
entity: input?.entityRef ?? '',
current: beverageRestitutionCurrent(d, e),
reference:
'Dossier de apoio13(2);modelo oficial específico ainda não identificado.',
status: r.status,
fields: [
cell('Dossier', 'IVA documental', input?.source?.vat),
cell('Dossier', 'Parcela pedida', r.requested),
{
field: 'Dossier',
label: 'Dedução desta parcela',
value: r.requested ? '0.00' : 'Indeterminado',
exact: r.requested ? '0/1' : 'Indeterminado',
},
cell('Dossier', 'Reconhecido', r.approved),
cell('Dossier', 'Recebido', r.received),
cell('Dossier', 'Por receber', r.awaitingReceipt),
],
notes: [
`Documento${input?.source?.documentRef ?? ''};linha${input?.source?.documentLine ?? ''};operação${input?.source?.operationId ?? ''};pedido${input?.requestReference ?? ''};${input?.requestDate ?? ''}.`,
r.reason,
'Sem campo27 nem formulárioD presumidos;IVA do fornecimento mantém cálculo próprio.',
],
});
}
return out;
}
export const l3PreparationStatus = (status: string) =>
status === 'determined'
? 'Cálculo determinado'
: status === 'indeterminate'
? 'Indeterminado'
: status;
const escape = (s: string) =>
s.replace(
/[&<>"']/g,
(c) =>
({
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#39;',
})[c]!,
);
export function l3FormPreparationHtml(p: L3FormPreparation): string {
return `<!doctype html><html lang="pt"><meta charset="utf-8"><title>Preparação L3 ${escape(p.branch)}</title><style>body{font:16px system-ui;max-width:960px;margin:32px auto;padding:0 24px;line-height:1.5}table{border-collapse:collapse;width:100%}th,td{border:1px solid #999;padding:8px;text-align:left}small{display:block}h1{font-size:26px}@media print{body{margin:0}}</style><h1>Preparação L3 ${escape(p.branch)} — ${escape(p.period)}</h1><p>${escape(p.entity)};${escape(l3PreparationStatus(p.status))};${p.current ? 'Correspondente às entradas' : 'Histórico preservado;rever entradas/origem'}. Submissão externa;preparação para conferência,não declaração AT submetida.</p><p>${escape(p.reference)}</p><table><thead><tr><th>Campo</th><th>Finalidade</th><th>MZN</th><th>Valor exacto</th></tr></thead><tbody>${p.fields.map((f) => `<tr><td>${escape(f.field)}</td><td>${escape(f.label)}</td><td>${escape(f.value)}</td><td>${escape(f.exact)}</td></tr>`).join('')}</tbody></table>${p.notes.map((n) => `<p>${escape(n)}</p>`).join('')}<h2>Formalidades ainda por completar</h2>${l3FormalitiesDependencies
.filter(
(dep) =>
dep.branch === p.branch ||
(p.branch === 'A/B' && dep.branch === 'D'),
)
.map(
(dep) =>
`<p>${escape(dep.missing)}<small>Condição de retoma:${escape(dep.trigger)}</small></p>`,
)
.join(
'',
)}<p>Fracções exactas conservadas;decimais são apresentação técnica,sem arredondamento fiscal final ou alteração de histórico.</p></html>`;
}
export function l3FormPreparationReportRows(
d: Record<string, unknown>,
): Array<[string, string, string, string]> {
try {
return l3FormPreparations(d).flatMap((p) => [
[
'IVA mensal',
`Preparação formal L3${p.branch} — ${p.period}`,
`${p.current ? 'Entradas correspondentes' : 'Histórico;rever origem'};${l3PreparationStatus(p.status)};${p.fields.map((f) => `${f.field}/${f.label}:${f.value}MZN;exacto${f.exact}`).join(' | ')}`,
`${p.reference};${p.notes.join(' | ')}`,
] as [string, string, string, string],
]);
} catch (error) {
return [
[
'IVA mensal',
'Preparação formalL3',
'Dados não recuperáveis;originais preservados',
String(error),
],
];
}
}