162 lines
6.2 KiB
TypeScript
162 lines
6.2 KiB
TypeScript
import {
|
||
fraction,
|
||
fractionPresentation,
|
||
type FiscalFraction,
|
||
} from './fiscal-rational.ts';
|
||
export const normalFormSource =
|
||
'M/22, exemplar inventariado IRPC M-22- DECLARACAO DE RENDIMENTOS.pdf, SHA256 4b61bd83d1fbcfd3b7b5c5523cea5923f7d0f65dc4c004eff58e5db38f763c88, quadros10/11/12, três páginas conferidas. Correspondência parcial de preparação, sem ficheiro oficial ou submissão.';
|
||
export function normalFormFields(m: {
|
||
profit?: {
|
||
lines: Array<{ code: string; amount: string; reference: string }>;
|
||
};
|
||
taxableProfit: FiscalFraction | null;
|
||
taxableBase: FiscalFraction | null;
|
||
colecta?: FiscalFraction | null;
|
||
base?: {
|
||
deductedLosses: FiscalFraction | null;
|
||
benefitDeduction: FiscalFraction | null;
|
||
};
|
||
}) {
|
||
const p = m.taxableProfit,
|
||
negative = p !== null && BigInt(p.numerator) < 0n;
|
||
const values: Array<[string, string, FiscalFraction | null, string]> = [
|
||
[
|
||
'259',
|
||
'Prejuízo fiscal',
|
||
negative
|
||
? fraction(-BigInt(p!.numerator), BigInt(p!.denominator))
|
||
: null,
|
||
'Quadro10; apenas quando há perda conhecida.',
|
||
],
|
||
[
|
||
'260',
|
||
'Lucro tributável',
|
||
p && !negative ? p : null,
|
||
'Quadro10; não preencher o campo de lucro com uma perda.',
|
||
],
|
||
[
|
||
'269',
|
||
'Prejuízo fiscal — regime geral',
|
||
negative
|
||
? fraction(-BigInt(p!.numerator), BigInt(p!.denominator))
|
||
: null,
|
||
'Quadro11; não é uma perda anterior dedutível.',
|
||
],
|
||
[
|
||
'270',
|
||
'Lucro tributável — regime geral',
|
||
p && !negative ? p : null,
|
||
'Quadro11; manter separados regimes reduzido/isento.',
|
||
],
|
||
[
|
||
'276',
|
||
'Prejuízos fiscais deduzidos',
|
||
m.base?.deductedLosses ?? null,
|
||
'Quadro11; utilização simulada expressa.',
|
||
],
|
||
[
|
||
'277',
|
||
'Benefícios fiscais a deduzir',
|
||
m.base?.benefitDeduction ?? null,
|
||
'Quadro11; benefícios positivos pertencem aL7.',
|
||
],
|
||
[
|
||
'278',
|
||
'Matéria colectável — regime geral',
|
||
m.taxableBase,
|
||
'Quadro11; não é colecta.',
|
||
],
|
||
[
|
||
'300',
|
||
'Imposto à taxa normal',
|
||
m.colecta ?? null,
|
||
'Quadro12; não é saldo final.',
|
||
],
|
||
[
|
||
'200',
|
||
'Resultado do exercício',
|
||
null,
|
||
'Resultado financeiro antes de imposto não preenche automaticamente o resultado líquido do formulário; falta reconciliação própria.',
|
||
],
|
||
[
|
||
'302',
|
||
'Colecta total',
|
||
null,
|
||
'Inclui eventual parcela de taxa reduzida; não presumir a ausência deL7.',
|
||
],
|
||
[
|
||
'309–316',
|
||
'Liquidação e saldo final',
|
||
null,
|
||
'ÂmbitoL8; retenções, pagamentos, créditos e juros não preenchidos porL6.',
|
||
],
|
||
];
|
||
const m20Fields = (
|
||
[
|
||
[
|
||
'cost_of_sales',
|
||
'6.1.1',
|
||
'Custo dos inventários vendidos/consumidos',
|
||
],
|
||
['personnel', '6.2', 'Gastos com pessoal'],
|
||
['depreciation', '6.5', 'Amortizações do período'],
|
||
[
|
||
'interest_expense',
|
||
'6.9',
|
||
'Gastos financeiros — apenas juros da origem',
|
||
],
|
||
] as const
|
||
).map(([key, account, label]) => {
|
||
const l = m.profit?.lines.find((l) => l.code === key);
|
||
let value: FiscalFraction | null = null;
|
||
if (l && /^\d+(?:\.\d{1,2})?$/.test(l.amount)) {
|
||
const [n, f = ''] = l.amount.split('.');
|
||
value = fraction(BigInt(n) * 100n + BigInt(f.padEnd(2, '0')), 100n);
|
||
}
|
||
return {
|
||
account,
|
||
label,
|
||
value,
|
||
display: value
|
||
? fractionPresentation(value).decimal + ' MZN'
|
||
: 'Não preenchido',
|
||
transcriptionReady:
|
||
value !== null &&
|
||
BigInt(value.numerator) % BigInt(value.denominator) === 0n,
|
||
reference: l?.reference ?? '',
|
||
reason: 'M20 A.1 quadro4, código contabilístico do exemplar, sem inventar número de campo. Valor contabilístico da origem; não é dedução fiscal. Exemplar exige meticais sem centavos; fracções conservadas, sem arredondamento presumido.',
|
||
};
|
||
});
|
||
return {
|
||
source: normalFormSource,
|
||
m20: {
|
||
status: 'partial',
|
||
fields: m20Fields,
|
||
source: 'Cópia de IRPS-IMOP.xlsx, SHA256 1ceca6f4c6d77853c1b2b445876599b42093175fa89eecf40590180956d83a9e, folhas M,20 e M20A.1; rótulos reutilizados, sem valores de terceiros ou execução das fórmulas do exemplar.',
|
||
reason: 'M20 A.1: correspondência parcial dos custos contabilísticos. Vendas/serviços, fornecimentos/outros gastos, imposto sobre o rendimento e resultado líquido exigem discriminação própria. Divergência concreta: M20A.1 remete resultado para M22 campo201/quadro8; PDF M22 inventariado usa campo200/quadro10. Sem transporte automático entre essas edições; restantes campos independentes conservados.',
|
||
},
|
||
fields: values.map(([code, label, value, reason]) => {
|
||
const centExact = value
|
||
? (BigInt(value.numerator) * 100n) %
|
||
BigInt(value.denominator) ===
|
||
0n
|
||
: null;
|
||
return {
|
||
code,
|
||
label,
|
||
value,
|
||
centExact,
|
||
display: value
|
||
? fractionPresentation(value).decimal + ' MZN'
|
||
: 'Não preenchido',
|
||
transcriptionReady: value !== null && centExact === true,
|
||
reason:
|
||
reason +
|
||
(centExact === false
|
||
? ' Fracção de cêntimo conservada exactamente; regra de representação fiscal por corresponder, sem arredondamento presumido.'
|
||
: ''),
|
||
};
|
||
}),
|
||
};
|
||
}
|