Files
Investidor-V2/resources/js/lib/iva-closure.ts
T

1056 lines
38 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 { inputText } from './input-text.ts';
import type { AcquisitionCalculation } from './iva-acquisitions.ts';
import {
addFractions,
decimalFraction,
fraction,
subtractFractions,
positiveFraction,
fractionLabel,
fractionPresentation,
type FiscalFraction,
} from './fiscal-rational.ts';
export const closureVersion = 'iva-closure-ab/1';
export const closureSource =
'L3-A/B: CIVA21(2/5),Lei13/2016 PDF15/p1426(273),23(1),PDF16/p1426(274);RCIVA12/15,Decreto7/2008 PDF3/p101;ModeloA/VAT Return9c0b99a8,p1–2,quadros8/10/11. Crédito anterior/comunicado19/20;22=max(17−19−20,0);25=max(18+19+20−17,0);24=22+23;reporte integral26=25 quando expressamente escolhido. Sem substituição21,reembolso27 ou restituição13(2). Cabeçalho remete18/19 mas caixas são19/20;divergência conservada,distinta deP00105/06.';
type Choice = '' | 'yes' | 'no';
export type ClosureSource = {
revision: number;
calculatedAt: string;
fingerprint: string;
opportunity: string;
currency: string;
period: string;
location: string;
projected: boolean;
debit: FiscalFraction | null;
credit: FiscalFraction | null;
};
export type ClosureCredit = {
id: string;
kind: 'opening' | 'communication' | 'previous';
period: string;
reference: string;
date: string;
amount: string;
previousId: string;
carryKey: string;
};
export type ClosurePayment = {
id: string;
reference: string;
date: string;
amount: string;
};
export type ClosureInput = {
id: string;
period: string;
entityRef: string;
origin: 'projected' | 'effective';
source: ClosureSource | null;
firstDeclaration: Choice;
filing: '' | 'timely' | 'late';
submissionDate: string;
filingRef: string;
creditsComplete: Choice;
credits: ClosureCredit[];
interest: '' | 'none' | 'documented';
interestAmount: string;
interestRef: string;
destination: '' | 'report' | 'refund';
refundAmount?: string;
payments: ClosurePayment[];
};
export type ClosureResult = {
id: string;
period: string;
entityRef: string;
origin: ClosureInput['origin'];
status: 'determined' | 'indeterminate';
reason: string;
inputKey: string;
financialKey: string;
carryKey: string;
fields: Record<string, FiscalFraction | null>;
paid: FiscalFraction | null;
outstanding: FiscalFraction | null;
overpayment: FiscalFraction | null;
source: string;
};
export type ClosureBook = {
version: 1;
rows: ClosureInput[];
result?: {
calculatedAt: string;
key: string;
inputs: ClosureInput[];
rows: ClosureResult[];
};
history?: Array<NonNullable<ClosureBook['result']>>;
};
const scope = (d: Record<string, unknown>) =>
inputText(d.context_opportunity || 'general');
const zero = () => fraction(0n);
export function closureFingerprint(v: unknown): string {
const raw = JSON.stringify(v);
let h = 14695981039346656037n;
for (let i = 0; i < raw.length; i++)
h = BigInt.asUintN(
64,
(h ^ BigInt(raw.charCodeAt(i))) * 1099511628211n,
);
return `${raw.length}:${h.toString(16)}`;
}
function store(d: Record<string, unknown>): Record<string, unknown> {
const s = JSON.parse(
inputText(d.modelo_a_snapshot || '{"version":1,"books":{}}'),
);
if (s.version !== 1 || !s.books)
throw Error('Livros fiscais não recuperáveis;originais preservados.');
return s;
}
function maps(d: Record<string, unknown>): Record<string, ClosureBook> {
const v = store(d).ivaClosures;
if (v === undefined) return {};
if (!v || typeof v !== 'object' || Array.isArray(v))
throw Error('Mapa de fechos não recuperável;originais preservados.');
return v as Record<string, ClosureBook>;
}
function validate(b: ClosureBook): void {
if (
!b ||
b.version !== 1 ||
!Array.isArray(b.rows) ||
(b.history !== undefined && !Array.isArray(b.history)) ||
(b.result !== undefined &&
(!Array.isArray(b.result.rows) ||
!Array.isArray(b.result.inputs) ||
typeof b.result.key !== 'string'))
)
throw Error('FechoL3 não recuperável;originais preservados.');
if (
b.result?.rows.some(
(r) =>
!r ||
typeof r.id !== 'string' ||
!r.fields ||
typeof r.fields !== 'object' ||
!['determined', 'indeterminate'].includes(r.status) ||
Object.values(r.fields).some(
(v) =>
v !== null &&
(!v ||
!/^\d{1,80}$/.test(v.numerator) ||
!/^\d{1,80}$/.test(v.denominator) ||
BigInt(v.denominator) === 0n),
),
)
)
throw Error('ResultadoL3 não recuperável;originais preservados.');
for (const r of b.rows) {
if (
!r ||
![
'id',
'period',
'entityRef',
'submissionDate',
'filingRef',
'interestAmount',
'interestRef',
].every((k) => typeof r[k as keyof ClosureInput] === 'string') ||
!['projected', 'effective'].includes(r.origin) ||
!['', 'yes', 'no'].includes(r.firstDeclaration) ||
!['', 'yes', 'no'].includes(r.creditsComplete) ||
!['', 'timely', 'late'].includes(r.filing) ||
!['', 'none', 'documented'].includes(r.interest) ||
!['', 'report', 'refund'].includes(r.destination) ||
(r.refundAmount !== undefined &&
typeof r.refundAmount !== 'string') ||
!Array.isArray(r.credits) ||
!Array.isArray(r.payments) ||
r.credits.some(
(c) =>
!c ||
!['opening', 'communication', 'previous'].includes(
c.kind,
) ||
![
'id',
'period',
'reference',
'date',
'amount',
'previousId',
'carryKey',
].every(
(k) => typeof c[k as keyof ClosureCredit] === 'string',
),
) ||
r.payments.some(
(p) =>
!p ||
!['id', 'reference', 'date', 'amount'].every(
(k) => typeof p[k as keyof ClosurePayment] === 'string',
),
) ||
(r.source !== null &&
(!r.source ||
!Number.isInteger(r.source.revision) ||
![
'calculatedAt',
'fingerprint',
'opportunity',
'currency',
'period',
'location',
].every(
(k) =>
typeof r.source![k as keyof ClosureSource] ===
'string',
)))
)
throw Error(
'EntradasL3 não recuperáveis;originais preservados,sem substituir por zero.',
);
}
}
export function closureBook(d: Record<string, unknown>): ClosureBook {
const b = maps(d)[scope(d)] ?? { version: 1, rows: [] };
validate(b);
return b;
}
export function saveClosureBook(
d: Record<string, unknown>,
b: ClosureBook,
): string {
validate(b);
const s = store(d);
return JSON.stringify({ ...s, ivaClosures: { ...maps(d), [scope(d)]: b } });
}
export function closureSources(d: Record<string, unknown>): ClosureSource[] {
const b = (
store(d).books as Record<
string,
{
result?: AcquisitionCalculation;
history?: AcquisitionCalculation[];
}
>
)[scope(d)];
return b
? [...(b.history ?? []), ...(b.result ? [b.result] : [])]
.flatMap((calc) =>
calc.periods.map((p) => ({
revision: calc.revision,
calculatedAt: calc.calculatedAt,
fingerprint: closureFingerprint(calc),
opportunity: calc.opportunity,
currency: calc.currency,
period: p.period,
location: calc.location,
projected:
calc.inputs.length === 0 ||
calc.inputs.some((r) => r.origin === 'projected'),
debit:
p.exactFields?.['17'] ??
(p.fields['17'] === null
? null
: decimalFraction(p.fields['17'])),
credit:
p.exactFields?.['18'] ??
(p.fields['18'] === null
? null
: decimalFraction(p.fields['18'])),
})),
)
.filter(
(r, i, a) =>
a.findIndex(
(s) =>
s.fingerprint === r.fingerprint &&
s.period === r.period,
) === i,
)
: [];
}
export function newClosure(
id: string,
source: ClosureSource | null,
entityRef = '',
): ClosureInput {
return {
id,
period: source?.period ?? '',
entityRef,
origin: 'projected',
source: source ? structuredClone(source) : null,
firstDeclaration: '',
filing: '',
submissionDate: '',
filingRef: '',
creditsComplete: '',
credits: [],
interest: '',
interestAmount: '',
interestRef: '',
destination: '',
payments: [],
};
}
export function newClosureCredit(
id: string,
kind: ClosureCredit['kind'],
): ClosureCredit {
return {
id,
kind,
period: '',
reference: '',
date: '',
amount: '',
previousId: '',
carryKey: '',
};
}
const priorPeriod = (p: string) => {
const d = new Date(p + '-01T00:00:00Z');
d.setUTCMonth(d.getUTCMonth() - 1);
return d.toISOString().slice(0, 7);
};
const validDate = (d: string) =>
/^20\d{2}-(0[1-9]|1[0-2])-\d{2}$/.test(d) &&
!Number.isNaN(Date.parse(d)) &&
new Date(d).toISOString().slice(0, 10) === d;
const normalize = (s: string) => s.trim().toLocaleLowerCase();
function creditIdentity(r: ClosureInput, c: ClosureCredit) {
return closureFingerprint([
normalize(r.entityRef),
r.origin,
c.kind === 'previous' ? 'carry' : 'document',
c.kind === 'previous' ? c.previousId : normalize(c.reference),
c.period,
]);
}
function paymentIdentity(r: ClosureInput, p: ClosurePayment) {
return closureFingerprint([
normalize(r.entityRef),
r.origin,
normalize(p.reference),
]);
}
function inputKey(r: ClosureInput) {
return closureFingerprint(r);
}
function financialKey(r: ClosureInput) {
const { payments: _, ...rest } = r;
return closureFingerprint(rest);
}
function otherUses(d: Record<string, unknown>, b: ClosureBook): ClosureInput[] {
return [
...b.rows,
...Object.entries(maps(d))
.filter(([id]) => id !== scope(d))
.flatMap(([, book]) => {
validate(book);
return book.rows.filter((r) => r.origin === 'effective');
}),
];
}
function duplicateCredits(
d: Record<string, unknown>,
b: ClosureBook,
): Set<string> {
const uses = new Map<string, number>();
for (const r of otherUses(d, b))
for (const c of r.credits) {
const k = creditIdentity(r, c);
uses.set(k, (uses.get(k) ?? 0) + 1);
}
return new Set([...uses].filter(([, n]) => n > 1).map(([k]) => k));
}
function substitutionPayments(d: Record<string, unknown>): string[] {
const m = store(d).ivaSubstitutions as
| Record<
string,
{
result?: {
inputs: Array<{
id: string;
prior: { entityRef: string; origin: string } | null;
payments: ClosurePayment[];
}>;
rows: Array<{
id: string;
status: string;
paid: FiscalFraction | null;
}>;
};
history?: Array<{
inputs: Array<{
id: string;
prior: { entityRef: string; origin: string } | null;
payments: ClosurePayment[];
}>;
rows: Array<{
id: string;
status: string;
paid: FiscalFraction | null;
}>;
}>;
}
>
| undefined;
return Object.entries(m ?? {}).flatMap(([id, b]) =>
[...(b.history ?? []), ...(b.result ? [b.result] : [])].flatMap((c) =>
c.inputs
.filter(
(r) =>
r.prior &&
(id === scope(d) || r.prior.origin === 'effective') &&
c.rows.some(
(v) =>
v.id === r.id &&
v.status === 'determined' &&
v.paid,
),
)
.flatMap((r) =>
r.payments.map((p) =>
closureFingerprint([
normalize(r.prior!.entityRef),
r.prior!.origin,
normalize(p.reference),
]),
),
),
),
);
}
function bookKey(d: Record<string, unknown>, b: ClosureBook) {
const sources = closureSources(d);
const values: unknown[] = [
b.rows,
b.rows.map((r) =>
sources.some((s) => JSON.stringify(s) === JSON.stringify(r.source)),
),
otherUses(d, b).map((r) => [
normalize(r.entityRef),
r.origin,
r.period,
r.credits.map((c) => creditIdentity(r, c)),
r.payments.map((p) => paymentIdentity(r, p)),
]),
];
const reserved = substitutionPayments(d),
conflicts = b.rows
.flatMap((r) => r.payments.map((p) => paymentIdentity(r, p)))
.filter((k) => reserved.includes(k));
if (conflicts.length) values.push(conflicts);
return closureFingerprint(values);
}
export function closureCurrent(
d: Record<string, unknown>,
b: ClosureBook,
): boolean {
return (
!!b.result &&
b.result.key === bookKey(d, b) &&
b.result.rows.length === b.rows.length &&
b.rows.every(
(r) =>
b.result!.rows.filter(
(x) => x.id === r.id && x.inputKey === inputKey(r),
).length === 1,
)
);
}
export function adoptPreviousCredit(
c: ClosureCredit,
previous: ClosureResult,
): ClosureCredit {
if (previous.status !== 'determined' || !previous.fields['26'])
throw Error('Fecho anterior indeterminado;sem crédito presumido.');
return {
...c,
kind: 'previous',
period: previous.period,
reference: `Fecho ${previous.id}`,
previousId: previous.id,
carryKey: previous.carryKey,
amount: fractionPresentation(previous.fields['26']).decimal,
date: '',
};
}
function refundLockConflict(
d: Record<string, unknown>,
row: ClosureInput,
fields: Record<string, FiscalFraction | null>,
): boolean {
const books = store(d).ivaRefunds as
| Record<
string,
{
rows: Array<{
source: {
closureId: string;
entityRef: string;
origin: string;
period: string;
credit: FiscalFraction;
carry: FiscalFraction;
requested: FiscalFraction;
} | null;
stage: string;
decision: string;
receipts: unknown[];
}>;
result?: {
inputs: Array<{
source: {
closureId: string;
entityRef: string;
origin: string;
period: string;
credit: FiscalFraction;
carry: FiscalFraction;
requested: FiscalFraction;
} | null;
stage: string;
decision: string;
receipts: unknown[];
}>;
};
history?: Array<{
inputs: Array<{
source: {
closureId: string;
entityRef: string;
origin: string;
period: string;
credit: FiscalFraction;
carry: FiscalFraction;
requested: FiscalFraction;
} | null;
stage: string;
decision: string;
receipts: unknown[];
}>;
}>;
}
>
| undefined;
return Object.entries(books ?? {}).some(([context, book]) =>
[
...book.rows,
...[
...(book.history ?? []),
...(book.result ? [book.result] : []),
].flatMap((c) => c.inputs),
].some(
(r) =>
r.source &&
(context === scope(d) || r.source.origin === 'effective') &&
r.source.closureId === row.id &&
normalize(r.source.entityRef) === normalize(row.entityRef) &&
r.source.origin === row.origin &&
r.source.period === row.period &&
(r.stage === 'submitted' ||
r.decision !== 'pending' ||
r.receipts.length > 0) &&
[
['25', r.source.credit],
['26', r.source.carry],
['27', r.source.requested],
].some(
([f, v]) =>
JSON.stringify(fields[f as string]) !==
JSON.stringify(v),
),
),
);
}
function effectiveCarryAlreadyUsed(
d: Record<string, unknown>,
row: ClosureInput,
fields: Record<string, FiscalFraction | null>,
): boolean {
if (row.origin !== 'effective' || row.destination !== 'refund')
return false;
const books = maps(d),
own = books[scope(d)];
if (!own) return false;
const prior = [...(own.history ?? []), ...(own.result ? [own.result] : [])]
.flatMap((c) => c.rows)
.filter(
(r) =>
r.id === row.id &&
r.origin === 'effective' &&
r.fields['26'] &&
fields['26'] &&
BigInt(
subtractFractions(r.fields['26'], fields['26']).numerator,
) > 0n,
);
return prior.some((p) =>
Object.values(books).some((b) =>
[...(b.history ?? []), ...(b.result ? [b.result] : [])].some((c) =>
c.inputs.some(
(input) =>
input.origin === 'effective' &&
normalize(input.entityRef) ===
normalize(row.entityRef) &&
input.period > row.period &&
input.credits.some(
(credit) =>
credit.kind === 'previous' &&
credit.previousId === p.id &&
credit.carryKey === p.carryKey,
) &&
c.rows.some(
(r) =>
r.id === input.id && r.status === 'determined',
),
),
),
),
);
}
export function recordClosure(
d: Record<string, unknown>,
b: ClosureBook,
now = new Date().toISOString(),
): ClosureBook {
validate(b);
if (new Set(b.rows.map((r) => r.id)).size !== b.rows.length)
throw Error('Identidade de fecho repetida.');
const contexts = b.rows.map((r) =>
JSON.stringify([normalize(r.entityRef), r.origin, r.period]),
);
if (new Set(contexts).size !== contexts.length)
throw Error(
'Mesmo âmbito/período repetido neste cenário;sem duplicar o fecho.',
);
const duplicate = duplicateCredits(d, b),
puses = new Map<string, number>();
for (const r of otherUses(d, b))
for (const p of r.payments) {
const k = paymentIdentity(r, p);
puses.set(k, (puses.get(k) ?? 0) + 1);
}
const results: ClosureResult[] = [];
for (const row of [...b.rows].sort((a, z) =>
a.period.localeCompare(z.period),
)) {
const fields: Record<string, FiscalFraction | null> =
Object.fromEntries(
[
'17',
'18',
'19',
'20',
'21',
'22',
'23',
'24',
'25',
'26',
'27',
].map((f) => [f, null]),
);
const r: ClosureResult = {
id: row.id,
period: row.period,
entityRef: row.entityRef,
origin: row.origin,
status: 'indeterminate',
reason: '',
inputKey: inputKey(row),
financialKey: financialKey(row),
carryKey: '',
fields,
paid: null,
outstanding: null,
overpayment: null,
source: closureSource,
};
const stop = (reason: string) => {
r.reason = reason;
results.push(r);
};
if (
!/^2026-(0[1-9]|1[0-2])$/.test(row.period) ||
!row.entityRef.trim() ||
d.currency !== 'MZN'
) {
stop(
'Identifique âmbito fiscal e período2026/MZN;sem consolidar oportunidades automaticamente.',
);
continue;
}
if (
row.origin === 'effective' &&
otherUses(d, b).some(
(other) =>
other !== row &&
other.origin === 'effective' &&
normalize(other.entityRef) === normalize(row.entityRef) &&
other.period === row.period,
)
) {
stop(
'Mesmo âmbito fiscal/período efectivo reservado noutra oportunidade recuperada;reconcilie a declaração única,sem duplicar ou consolidar dossiers silenciosamente.',
);
continue;
}
const source = closureSources(d).find(
(s) => JSON.stringify(s) === JSON.stringify(row.source),
);
if (
!source ||
source.period !== row.period ||
source.opportunity !== scope(d) ||
source.currency !== 'MZN' ||
!source.debit ||
!source.credit
) {
stop(
'ResultadoL2 congelado correspondente ausente ou17/18 indeterminados;sem recalcular nem presumir impostozero.',
);
continue;
}
fields['17'] = source.debit;
fields['18'] = source.credit;
if (
BigInt(source.debit.numerator) < 0n ||
BigInt(source.credit.numerator) < 0n ||
(BigInt(source.debit.numerator) > 0n &&
BigInt(source.credit.numerator) > 0n)
) {
stop('Sinais17/18 incompatíveis;resolver origemL2.');
continue;
}
if (row.origin === 'effective' && source.projected) {
stop(
'OrigemL2 contém pressupostos projectados;não os apresentar como declaração efectiva.',
);
continue;
}
if (row.firstDeclaration !== 'yes') {
stop(
'Este percursoA/B exige primeira declaração;substituição21 permaneceL3-C.',
);
continue;
}
if (
!validDate(row.submissionDate) ||
row.submissionDate.slice(0, 7) < row.period ||
!row.filingRef.trim() ||
row.filing !== 'timely'
) {
stop(
row.filing === 'late'
? 'Declaração fora do prazo:RCIVA15 e exemplar não permitem uso19/20 neste percurso;conservar conta de crédito e tratar procedimento próprio,sem o apagar.'
: 'Indique data e fundamento da entrega em prazo,efectivos ou pressupostos;sem calendário/prazo presumido.',
);
continue;
}
if (row.creditsComplete !== 'yes') {
stop(
'Reconcilie todos os créditos aplicáveis;lista vazia só significa ausência quando expressamente confirmada.',
);
continue;
}
const credits19: FiscalFraction[] = [],
credits20: FiscalFraction[] = [];
let invalid = '';
for (const c of row.credits) {
if (
!c.reference.trim() ||
!/^20\d{2}-(0[1-9]|1[0-2])$/.test(c.period) ||
c.period > row.period ||
(c.kind !== 'communication' && c.period === row.period) ||
duplicate.has(creditIdentity(row, c))
) {
invalid =
'Crédito sem origem/período aplicável ou já utilizado/reservado noutra linha/âmbito recuperado;sem utilização duplicada.';
break;
}
if (c.kind === 'previous') {
const p = results.find((p) => p.id === c.previousId);
if (
!p ||
p.status !== 'determined' ||
p.period !== priorPeriod(row.period) ||
p.entityRef.trim().toLowerCase() !==
row.entityRef.trim().toLowerCase() ||
p.origin !== row.origin ||
p.period !== c.period ||
p.carryKey !== c.carryKey ||
!p.fields['26']
) {
invalid =
'Fecho anterior alterado,indeterminado,fora do mês imediatamente anterior ou de outro âmbito;não adoptar novo crédito silenciosamente.';
break;
}
credits19.push(p.fields['26']);
} else {
let v: FiscalFraction;
try {
v = decimalFraction(c.amount);
} catch {
invalid =
'Montante de crédito em falta/inválido;não substituir porzero.';
break;
}
if (
c.kind === 'opening' &&
c.period !== priorPeriod(row.period)
) {
invalid =
'Saldo de abertura exige referência ao saldo disponível do mês imediatamente anterior;origem mais antiga deve ser reconciliada nesse saldo.';
break;
}
if (
c.kind === 'opening' &&
b.rows.some(
(p) =>
p.id !== row.id &&
p.entityRef.trim().toLowerCase() ===
row.entityRef.trim().toLowerCase() &&
p.origin === row.origin &&
p.period === priorPeriod(row.period),
)
) {
invalid =
'Há fecho do período anterior neste livro;reutilize26 expressamente em vez de introduzir outra abertura.';
break;
}
if (
c.kind === 'communication' &&
(!validDate(c.date) || c.date > row.submissionDate)
) {
invalid =
'Crédito20 exige comunicação recebida,referência e data não posterior à entrega declarada;pedido não é comunicação.';
break;
}
(c.kind === 'opening' ? credits19 : credits20).push(v);
}
}
if (invalid) {
stop(invalid);
continue;
}
fields['19'] = addFractions(credits19);
fields['20'] = addFractions(credits20);
const balance = subtractFractions(
addFractions([source.credit, fields['19'], fields['20']]),
source.debit,
);
fields['22'] = positiveFraction(
fraction(-BigInt(balance.numerator), BigInt(balance.denominator)),
);
fields['25'] = positiveFraction(balance);
if (row.interest === 'none' && row.interestRef.trim())
fields['23'] = zero();
else if (row.interest === 'documented' && row.interestRef.trim()) {
try {
fields['23'] = decimalFraction(row.interestAmount);
} catch {
/* Preserve unknown. */
}
}
fields['24'] = fields['23']
? addFractions([fields['22'], fields['23']])
: null;
if (row.destination === 'report') {
fields['26'] = fields['25'];
fields['27'] = zero();
} else if (row.destination === 'refund') {
let amount: FiscalFraction;
try {
amount = decimalFraction(row.refundAmount ?? '');
} catch {
stop(
'Identifique a parcela destinada ao pedido;sem montante presumido.',
);
continue;
}
if (
BigInt(amount.numerator) === 0n ||
BigInt(subtractFractions(fields['25'], amount).numerator) < 0n
) {
stop(
'Parcela do pedido deve ser positiva e não exceder o crédito25;sem dupla utilização.',
);
continue;
}
fields['27'] = amount;
fields['26'] = subtractFractions(fields['25'], amount);
r.source +=
' D:afectação expressa26+27=25,sem concessão/recebimento;CIVA21 eModeloA11.';
} else {
stop(
'Escolha reporte integral ou parcela para preparação de reembolsoD;sem destino presumido.',
);
continue;
}
if (refundLockConflict(d, row, fields)) {
stop(
'Crédito ligado a pedido apresentado/decisão/recebimento;conserve a afectação e reconcilie o procedimento,sem o voltar a reportar ou alterar silenciosamente.',
);
continue;
}
if (effectiveCarryAlreadyUsed(d, row, fields)) {
stop(
'Reporte efectivo já utilizado numa declaração posterior recuperada;prepare o pedido sobre o saldo disponível do período reconciliado,sem reafectar retroactivamente crédito consumido.',
);
continue;
}
if (!fields['24']) {
stop(
'Juros23 por identificar:indique montante documentado/pressuposto ou inexistência expressa com motivo;sem taxa presumida.',
);
continue;
}
r.status = 'determined';
r.carryKey = closureFingerprint([
r.id,
r.financialKey,
fields['26'],
scope(d),
]);
const payments: FiscalFraction[] = [];
for (const p of row.payments) {
if (
!p.reference.trim() ||
!validDate(p.date) ||
(puses.get(paymentIdentity(row, p)) ?? 0) > 1 ||
substitutionPayments(d).includes(paymentIdentity(row, p))
) {
invalid =
'Pagamento sem referência/data ou repetido noutro período/declaração;conserve o documento e reconcilie a imputação.';
break;
}
try {
payments.push(decimalFraction(p.amount));
} catch {
invalid =
'Montante de pagamento por identificar;sem baixa presumida.';
break;
}
}
if (invalid) {
stop(invalid);
continue;
}
r.paid = addFractions(payments);
r.outstanding = positiveFraction(
subtractFractions(fields['24'], r.paid),
);
r.overpayment = positiveFraction(
subtractFractions(r.paid, fields['24']),
);
r.status = 'determined';
r.carryKey = closureFingerprint([
r.id,
r.financialKey,
fields['26'],
scope(d),
]);
r.reason =
'Saldo calculado, pagamentos registados e crédito transportado distintos. Pagamento não altera22/25/26;excedente pago não cria crédito20 sem comunicação.21 não aplicável à primeira declaração;' +
(row.destination === 'refund'
? '26/27 separados pela afectação;27 é parcela destinada a pedidoD,sem concessão nem recebimento. '
: '27zero pela opção expressa de reporte integral. ') +
(row.origin === 'projected'
? 'Pressupostos de simulação,sem declaração ou pagamento efectuados pelo portal.'
: 'Dados efectivos declarados;registo não certifica cobrança nem submissãoAT.');
results.push(r);
}
const result = {
calculatedAt: now,
key: bookKey(d, b),
inputs: structuredClone(b.rows),
rows: results,
};
return {
...b,
...(b.result
? { history: [...(b.history ?? []), structuredClone(b.result)] }
: {}),
result,
};
}
export function closureReportRows(
d: Record<string, unknown>,
): Array<[string, string, string, string]> {
try {
const b = closureBook(d);
if (!b.rows.length) return [];
const hasRefund = b.result?.rows.some(
(r) => r.fields['27'] && BigInt(r.fields['27'].numerator) > 0n,
);
const description = hasRefund
? closureSource.replace(
'Sem substituição21,reembolso27 ou restituição13(2).',
'ExtensãoD:26+27=25;27é parcela destinada a pedido,não decisão/recebimento. Substituição21 e restituição13(2) mantêm percursos próprios.',
)
: closureSource;
const out: Array<[string, string, string, string]> = [
[
'IVA mensal',
'L3-A/B — fecho por período',
closureCurrent(d, b)
? 'Resultado correspondente às entradas'
: 'Histórico preservado;rever entradas/origens antes de utilizar',
description,
],
];
for (const r of b.result?.rows ?? []) {
const input = b.result?.inputs.find((x) => x.id === r.id),
source = input?.source;
const context = `${r.entityRef};${scope(d)};MZN;${r.origin === 'projected' ? 'Pressuposto de simulação' : 'Dados efectivos declarados'};versão guardada${b.result!.calculatedAt}`;
out.push([
'IVA mensal',
`Fecho A/B — ${r.period}`,
Object.entries(r.fields)
.map(
([f, v]) =>
`${f}:${v ? fractionPresentation(v).decimal : f === '21' && input?.firstDeclaration === 'yes' ? 'Não aplicável (primeira declaração)' : 'Indeterminado'}`,
)
.join('; '),
`${context};${r.reason}`,
]);
const credits =
(input?.credits ?? [])
.map(
(c) =>
`${c.kind === 'communication' ? 'Comunicação20' : c.kind === 'previous' ? 'Transporte19' : 'Abertura19'}: ${c.reference};origem${c.period};montante apresentado${c.amount}MZN${c.date ? ';recebida' + c.date : ''}`,
)
.join(' | ') || 'Sem créditos registados';
const payments =
(input?.payments ?? [])
.map((p) => `${p.reference};${p.date};${p.amount}MZN`)
.join(' | ') || 'Sem pagamentos registados';
out.push([
'IVA mensal',
`Pagamentos e reporte — ${r.period}`,
`Calculado24:${r.fields['24'] ? fractionLabel(r.fields['24']) : 'Indeterminado'};pagamentos registados:${r.paid ? fractionLabel(r.paid) : 'Indeterminado'};por liquidar:${r.outstanding ? fractionLabel(r.outstanding) : 'Indeterminado'};excedente pago:${r.overpayment ? fractionLabel(r.overpayment) : 'Indeterminado'};transportado26:${r.fields['26'] ? fractionLabel(r.fields['26']) : 'Indeterminado'}`,
`${context};OrigemL2:revisão${source?.revision ?? 'por identificar'},${source?.period ?? ''},${source?.location ?? ''},${source?.calculatedAt ?? ''};${credits};${payments};entrega${input?.submissionDate ?? ''},${input?.filingRef ?? ''};juros:${input?.interestRef ?? ''}`,
]);
}
return out;
} catch {
return [
[
'IVA mensal',
'L3-A/B',
'Fecho guardado não recuperável',
'Originais preservados;sem crédito ou pagamento presumidos.',
],
];
}
}