Preserve documented turnover corrections in PEC viability estimates
tests / ci (push) Canceled after 0s
tests / ci (push) Canceled after 0s
This commit is contained in:
@@ -1,3 +1,11 @@
|
|||||||
|
## 11/11/2026 — rectificação do volume PEC na simulação em QA
|
||||||
|
|
||||||
|
**Recorte29(3) avançado:** fonte documental permite rectificar o volume de negócios2025 usado na estimativa PEC2026. Valor original,valor corrigido,titular,exercício e fundamento ficam conservados;escolha explícita entre aplicar a correcção e manter o original. Não submete declaração rectificativa. A estimativa recalcula0,5%,limites e PPC anteriores uma única vez;relatório mostra origem,valor corrigido e efeito na estimativa. Mudança da base/titular/exercício exige reconferência;fonte ou valor ausente não é zero.
|
||||||
|
|
||||||
|
Cálculo→API privada→recuperação→HTML/relatório do mesmo dossier em cinco cenários:aumento40.000→90.000MZN,redução40.000→20.000,original conservado40.000,base divergente e correcção para volumezero indeterminados. Hipótese de caixa anterior40.000 deixa de reconciliar quando o total muda;não é sobrescrita. Saldo anual138MZN,comprovativos pagos e histórico anterior preservados. QA55441 existente:uma prova nativa/47asserções com rollback,cinco testes novos de cálculo/interface e40afectados aprovados;tipos/lint/compilação aprovados. Provas `outputs/irpc-pec-correction-20261111/*-verification.json`;pacote Git com aplicação/reversão conferidas no fecho. Não é conferência visual.
|
||||||
|
|
||||||
|
**Estado:** implementado em QA,ainda não instalado;homologação mantém `a708f079` e270instituições publicadas. Fonte:RIRPC,Decreto9/2008,art29(3),BR16/I16-04-2008,original já aceite SHA256 `fc373ff5637edf75ec6a9595dec8763a8503d4ee786e3b7d8f1c742eae9d80ee`. Sem migração,base,tabela ou direito novo. Restam instalação/visual,última base29(5),PPC excedente,períodos/regimes especiais e demais resíduos fiscais L1–L7/restanteL8/L9–L16. Correcções fora deste recorte conservam âmbito próprio;L8 e portal não são declarados integralmente concluídos. Próximo trabalho:primeiro residual de simulação executável pelas fontes aceites,sem reabrir este recorte.
|
||||||
|
|
||||||
## 11/11/2026 — finalidade de viabilidade e projecção PEC instaladas ema708f079
|
## 11/11/2026 — finalidade de viabilidade e projecção PEC instaladas ema708f079
|
||||||
|
|
||||||
Instalação expressamente autorizada,executada pelo processo Git existente e conferida às10:45:50Africa/Johannesburg(08:45:50UTC): `a708f079b318a13100644c27287f1caa854ecf1d`. No simulador fiscal,L8,os ecrãs apresentam **Simular PPC/PEC**,estimativas fiscais e hipóteses de tesouraria para avaliar a viabilidade do negócio. A hipótese PEC permite distribuir valores por três meses ou concentrar a estimativa num mês escolhido,com soma conferida e prazo legal não certificado. Rascunho,recuperação,histórico e relatórios preservam escolhas;dados documentais já pagos permanecem distintos. O portal não executa pagamentos,nem submete declarações fiscais.
|
Instalação expressamente autorizada,executada pelo processo Git existente e conferida às10:45:50Africa/Johannesburg(08:45:50UTC): `a708f079b318a13100644c27287f1caa854ecf1d`. No simulador fiscal,L8,os ecrãs apresentam **Simular PPC/PEC**,estimativas fiscais e hipóteses de tesouraria para avaliar a viabilidade do negócio. A hipótese PEC permite distribuir valores por três meses ou concentrar a estimativa num mês escolhido,com soma conferida e prazo legal não certificado. Rascunho,recuperação,histórico e relatórios preservam escolhas;dados documentais já pagos permanecem distintos. O portal não executa pagamentos,nem submete declarações fiscais.
|
||||||
|
|||||||
@@ -1,3 +1,11 @@
|
|||||||
|
## 11/11/2026 — rectificação do volume PEC na simulação em QA
|
||||||
|
|
||||||
|
**Recorte29(3) avançado:** fonte documental permite rectificar o volume de negócios2025 usado na estimativa PEC2026. Valor original,valor corrigido,titular,exercício e fundamento ficam conservados;escolha explícita entre aplicar a correcção e manter o original. Não submete declaração rectificativa. A estimativa recalcula0,5%,limites e PPC anteriores uma única vez;relatório mostra origem,valor corrigido e efeito na estimativa. Mudança da base/titular/exercício exige reconferência;fonte ou valor ausente não é zero.
|
||||||
|
|
||||||
|
Cálculo→API privada→recuperação→HTML/relatório do mesmo dossier em cinco cenários:aumento40.000→90.000MZN,redução40.000→20.000,original conservado40.000,base divergente e correcção para volumezero indeterminados. Hipótese de caixa anterior40.000 deixa de reconciliar quando o total muda;não é sobrescrita. Saldo anual138MZN,comprovativos pagos e histórico anterior preservados. QA55441 existente:uma prova nativa/47asserções com rollback,cinco testes novos de cálculo/interface e40afectados aprovados;tipos/lint/compilação aprovados. Provas `outputs/irpc-pec-correction-20261111/*-verification.json`;pacote Git com aplicação/reversão conferidas no fecho. Não é conferência visual.
|
||||||
|
|
||||||
|
**Estado:** implementado em QA,ainda não instalado;homologação mantém `a708f079` e270instituições publicadas. Fonte:RIRPC,Decreto9/2008,art29(3),BR16/I16-04-2008,original já aceite SHA256 `fc373ff5637edf75ec6a9595dec8763a8503d4ee786e3b7d8f1c742eae9d80ee`. Sem migração,base,tabela ou direito novo. Restam instalação/visual,última base29(5),PPC excedente,períodos/regimes especiais e demais resíduos fiscais L1–L7/restanteL8/L9–L16. Correcções fora deste recorte conservam âmbito próprio;L8 e portal não são declarados integralmente concluídos. Próximo trabalho:primeiro residual de simulação executável pelas fontes aceites,sem reabrir este recorte.
|
||||||
|
|
||||||
## 11/11/2026 — finalidade de viabilidade e projecção PEC instaladas ema708f079
|
## 11/11/2026 — finalidade de viabilidade e projecção PEC instaladas ema708f079
|
||||||
|
|
||||||
Instalação expressamente autorizada,executada pelo processo Git existente e conferida às10:45:50Africa/Johannesburg(08:45:50UTC): `a708f079b318a13100644c27287f1caa854ecf1d`. No simulador fiscal,L8,os ecrãs apresentam **Simular PPC/PEC**,estimativas fiscais e hipóteses de tesouraria para avaliar a viabilidade do negócio. A hipótese PEC permite distribuir valores por três meses ou concentrar a estimativa num mês escolhido,com soma conferida e prazo legal não certificado. Rascunho,recuperação,histórico e relatórios preservam escolhas;dados documentais já pagos permanecem distintos. O portal não executa pagamentos,nem submete declarações fiscais.
|
Instalação expressamente autorizada,executada pelo processo Git existente e conferida às10:45:50Africa/Johannesburg(08:45:50UTC): `a708f079b318a13100644c27287f1caa854ecf1d`. No simulador fiscal,L8,os ecrãs apresentam **Simular PPC/PEC**,estimativas fiscais e hipóteses de tesouraria para avaliar a viabilidade do negócio. A hipótese PEC permite distribuir valores por três meses ou concentrar a estimativa num mês escolhido,com soma conferida e prazo legal não certificado. Rascunho,recuperação,histórico e relatórios preservam escolhas;dados documentais já pagos permanecem distintos. O portal não executa pagamentos,nem submete declarações fiscais.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,3 +1,11 @@
|
|||||||
|
## 11/11/2026 — rectificação do volume PEC na simulação em QA
|
||||||
|
|
||||||
|
**Recorte29(3) avançado:** fonte documental permite rectificar o volume de negócios2025 usado na estimativa PEC2026. Valor original,valor corrigido,titular,exercício e fundamento ficam conservados;escolha explícita entre aplicar a correcção e manter o original. Não submete declaração rectificativa. A estimativa recalcula0,5%,limites e PPC anteriores uma única vez;relatório mostra origem,valor corrigido e efeito na estimativa. Mudança da base/titular/exercício exige reconferência;fonte ou valor ausente não é zero.
|
||||||
|
|
||||||
|
Cálculo→API privada→recuperação→HTML/relatório do mesmo dossier em cinco cenários:aumento40.000→90.000MZN,redução40.000→20.000,original conservado40.000,base divergente e correcção para volumezero indeterminados. Hipótese de caixa anterior40.000 deixa de reconciliar quando o total muda;não é sobrescrita. Saldo anual138MZN,comprovativos pagos e histórico anterior preservados. QA55441 existente:uma prova nativa/47asserções com rollback,cinco testes novos de cálculo/interface e40afectados aprovados;tipos/lint/compilação aprovados. Provas `outputs/irpc-pec-correction-20261111/*-verification.json`;pacote Git com aplicação/reversão conferidas no fecho. Não é conferência visual.
|
||||||
|
|
||||||
|
**Estado:** implementado em QA,ainda não instalado;homologação mantém `a708f079` e270instituições publicadas. Fonte:RIRPC,Decreto9/2008,art29(3),BR16/I16-04-2008,original já aceite SHA256 `fc373ff5637edf75ec6a9595dec8763a8503d4ee786e3b7d8f1c742eae9d80ee`. Sem migração,base,tabela ou direito novo. Restam instalação/visual,última base29(5),PPC excedente,períodos/regimes especiais e demais resíduos fiscais L1–L7/restanteL8/L9–L16. Correcções fora deste recorte conservam âmbito próprio;L8 e portal não são declarados integralmente concluídos. Próximo trabalho:primeiro residual de simulação executável pelas fontes aceites,sem reabrir este recorte.
|
||||||
|
|
||||||
## 11/11/2026 — finalidade de viabilidade e projecção PEC instaladas ema708f079
|
## 11/11/2026 — finalidade de viabilidade e projecção PEC instaladas ema708f079
|
||||||
|
|
||||||
Instalação expressamente autorizada,executada pelo processo Git existente e conferida às10:45:50Africa/Johannesburg(08:45:50UTC): `a708f079b318a13100644c27287f1caa854ecf1d`. No simulador fiscal,L8,os ecrãs apresentam **Simular PPC/PEC**,estimativas fiscais e hipóteses de tesouraria para avaliar a viabilidade do negócio. A hipótese PEC permite distribuir valores por três meses ou concentrar a estimativa num mês escolhido,com soma conferida e prazo legal não certificado. Rascunho,recuperação,histórico e relatórios preservam escolhas;dados documentais já pagos permanecem distintos. O portal não executa pagamentos,nem submete declarações fiscais.
|
Instalação expressamente autorizada,executada pelo processo Git existente e conferida às10:45:50Africa/Johannesburg(08:45:50UTC): `a708f079b318a13100644c27287f1caa854ecf1d`. No simulador fiscal,L8,os ecrãs apresentam **Simular PPC/PEC**,estimativas fiscais e hipóteses de tesouraria para avaliar a viabilidade do negócio. A hipótese PEC permite distribuir valores por três meses ou concentrar a estimativa num mês escolhido,com soma conferida e prazo legal não certificado. Rascunho,recuperação,histórico e relatórios preservam escolhas;dados documentais já pagos permanecem distintos. O portal não executa pagamentos,nem submete declarações fiscais.
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
import {
|
||||||
|
newPecCorrection,
|
||||||
|
type PecCorrection,
|
||||||
|
} from '@/lib/irpc-pec-correction';
|
||||||
|
export function SimulationIrpcPecCorrection({
|
||||||
|
value,
|
||||||
|
original,
|
||||||
|
subjectNuit,
|
||||||
|
onChange,
|
||||||
|
}: {
|
||||||
|
value?: PecCorrection;
|
||||||
|
original: string;
|
||||||
|
subjectNuit: string;
|
||||||
|
onChange: (c: PecCorrection) => void;
|
||||||
|
}) {
|
||||||
|
if (!value)
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() =>
|
||||||
|
onChange(newPecCorrection(original, subjectNuit))
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Rectificar volume na simulação PEC
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
const patch = (c: Partial<PecCorrection>) => onChange({ ...value, ...c }),
|
||||||
|
field = (
|
||||||
|
key: Exclude<keyof PecCorrection, 'version' | 'apply'>,
|
||||||
|
label: string,
|
||||||
|
) => (
|
||||||
|
<label className="block">
|
||||||
|
{label}
|
||||||
|
<input
|
||||||
|
aria-label={label}
|
||||||
|
value={value[key]}
|
||||||
|
onChange={(e) => patch({ [key]: e.target.value })}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<section
|
||||||
|
aria-label="Rectificação da estimativa PEC"
|
||||||
|
className="rounded border p-3"
|
||||||
|
>
|
||||||
|
<h6>Volume de negócios corrigido</h6>
|
||||||
|
<p>
|
||||||
|
O volume original permanece {original || 'por indicar'} MZN. A
|
||||||
|
nova fonte fundamenta a correcção da simulação2026; não submete
|
||||||
|
declaração fiscal.
|
||||||
|
</p>
|
||||||
|
<label className="block">
|
||||||
|
Aplicar rectificação PEC
|
||||||
|
<select
|
||||||
|
aria-label="Aplicar rectificação PEC"
|
||||||
|
value={value.apply}
|
||||||
|
onChange={(e) =>
|
||||||
|
patch({
|
||||||
|
apply: e.target.value as PecCorrection['apply'],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<option value="">Por decidir</option>
|
||||||
|
<option value="yes">
|
||||||
|
Usar volume corrigido na estimativa
|
||||||
|
</option>
|
||||||
|
<option value="no">Conservar volume original</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<details open={value.apply !== 'no'}>
|
||||||
|
<summary>Alterar rectificação PEC</summary>
|
||||||
|
{field('originalTurnover', 'Volume original analisado — PEC')}
|
||||||
|
{field('revisedTurnover', 'Volume corrigido — PEC')}
|
||||||
|
{field('subjectNuit', 'NUIT da rectificação PEC')}
|
||||||
|
{field('turnoverYear', 'Ano do volume corrigido — PEC')}
|
||||||
|
{field('reference', 'Fonte e fundamento da rectificação PEC')}
|
||||||
|
</details>
|
||||||
|
<p>
|
||||||
|
Trocar a opção conserva os dois valores. Uma alteração da base
|
||||||
|
original exige nova conferência; recalcule o fecho e a hipótese
|
||||||
|
de tesouraria para reflectir a estimativa corrigida.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
|
import {SimulationIrpcPecCorrection} from './simulation-irpc-pec-correction';
|
||||||
import {SimulationIrpcPecPlan} from './simulation-irpc-pec-plan';
|
import {SimulationIrpcPecPlan} from './simulation-irpc-pec-plan';
|
||||||
import {
|
import {
|
||||||
newPecObligation,
|
newPecObligation,
|
||||||
calculatePecObligation,
|
calculatePecObligation,
|
||||||
pecObligationSource,
|
|
||||||
type PecObligation,
|
type PecObligation,
|
||||||
} from '@/lib/irpc-pec-obligation';
|
} from '@/lib/irpc-pec-obligation';
|
||||||
import { fractionPresentation } from '@/lib/fiscal-rational';
|
import { fractionPresentation } from '@/lib/fiscal-rational';
|
||||||
@@ -42,7 +42,7 @@ export function SimulationIrpcPecObligation({
|
|||||||
patch = (changes: Partial<PecObligation>) =>
|
patch = (changes: Partial<PecObligation>) =>
|
||||||
onChange({ ...p, ...changes });
|
onChange({ ...p, ...changes });
|
||||||
const field = (
|
const field = (
|
||||||
key: Exclude<keyof PecObligation, 'version' | 'scope' | 'plan'>,
|
key: Exclude<keyof PecObligation, 'version' | 'scope' | 'plan' | 'correction'>,
|
||||||
label: string,
|
label: string,
|
||||||
) => (
|
) => (
|
||||||
<label className="block">
|
<label className="block">
|
||||||
@@ -104,6 +104,7 @@ export function SimulationIrpcPecObligation({
|
|||||||
'turnoverReference',
|
'turnoverReference',
|
||||||
'Fonte do volume de negócios — PEC',
|
'Fonte do volume de negócios — PEC',
|
||||||
)}
|
)}
|
||||||
|
<SimulationIrpcPecCorrection value={p.correction} original={p.previousTurnover} subjectNuit={p.subjectNuit} onChange={correction=>patch({correction})}/>
|
||||||
{field(
|
{field(
|
||||||
'previousPpcPaid',
|
'previousPpcPaid',
|
||||||
'PPC efectivamente pagos no ano anterior — MZN',
|
'PPC efectivamente pagos no ano anterior — MZN',
|
||||||
@@ -143,7 +144,7 @@ export function SimulationIrpcPecObligation({
|
|||||||
</p>
|
</p>
|
||||||
<details>
|
<details>
|
||||||
<summary>Fonte e limites PEC</summary>
|
<summary>Fonte e limites PEC</summary>
|
||||||
<p>{pecObligationSource}</p>
|
<p>{m.source}</p>
|
||||||
</details>
|
</details>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import {pecCorrectionVersion} from './irpc-pec-correction.ts';
|
||||||
import {pecPlanVersion} from './irpc-pec-plan.ts';
|
import {pecPlanVersion} from './irpc-pec-plan.ts';
|
||||||
import {validatePecObligation,pecObligationReport,pecObligationVersion,type PecObligation} from './irpc-pec-obligation.ts';
|
import {validatePecObligation,pecObligationReport,pecObligationVersion,type PecObligation} from './irpc-pec-obligation.ts';
|
||||||
import { validatePpcObligation, ppcObligationReport, ppcObligationVersion, type PpcObligation } from './irpc-ppc-obligation.ts';
|
import { validatePpcObligation, ppcObligationReport, ppcObligationVersion, type PpcObligation } from './irpc-ppc-obligation.ts';
|
||||||
@@ -575,6 +576,7 @@ function key(d: Record<string, unknown>, b: AnnualBook) {
|
|||||||
...(b.ppcObligation ? [ppcObligationVersion] : []),
|
...(b.ppcObligation ? [ppcObligationVersion] : []),
|
||||||
...(b.pecObligation ? [pecObligationVersion] : []),
|
...(b.pecObligation ? [pecObligationVersion] : []),
|
||||||
...(b.pecObligation?.plan ? [pecPlanVersion] : []),
|
...(b.pecObligation?.plan ? [pecPlanVersion] : []),
|
||||||
|
...(b.pecObligation?.correction ? [pecCorrectionVersion] : []),
|
||||||
...(b.investmentCredit && b.creditCarry?.lots.length
|
...(b.investmentCredit && b.creditCarry?.lots.length
|
||||||
? ['CBF15-credit-carry-combination/1']
|
? ['CBF15-credit-carry-combination/1']
|
||||||
: []),
|
: []),
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
export const pecCorrectionVersion = 'IRPC-PEC-turnover-correction/1';
|
||||||
|
export const pecCorrectionSource =
|
||||||
|
'RIRPC,Decreto9/2008,art29(3),BR16/I16-04-2008:volume de negócios determinado pelas vendas/serviços até ao final do exercício anterior,podendo ser rectificado no ano seguinte se distinto do usado no cálculo. Recorte:correcção documental do volume2025 na simulação2026;original,fundamento e diferença conservados. Não é declaração fiscal rectificativa submetida.';
|
||||||
|
export type PecCorrection = {
|
||||||
|
version: 1;
|
||||||
|
apply: '' | 'yes' | 'no';
|
||||||
|
originalTurnover: string;
|
||||||
|
revisedTurnover: string;
|
||||||
|
subjectNuit: string;
|
||||||
|
turnoverYear: string;
|
||||||
|
reference: string;
|
||||||
|
};
|
||||||
|
export const newPecCorrection = (
|
||||||
|
originalTurnover: string,
|
||||||
|
subjectNuit: string,
|
||||||
|
): PecCorrection => ({
|
||||||
|
version: 1,
|
||||||
|
apply: '',
|
||||||
|
originalTurnover,
|
||||||
|
revisedTurnover: '',
|
||||||
|
subjectNuit,
|
||||||
|
turnoverYear: '2025',
|
||||||
|
reference: '',
|
||||||
|
});
|
||||||
|
export function validatePecCorrection(c: PecCorrection) {
|
||||||
|
if (
|
||||||
|
!c ||
|
||||||
|
c.version !== 1 ||
|
||||||
|
!['', 'yes', 'no'].includes(c.apply) ||
|
||||||
|
[
|
||||||
|
'originalTurnover',
|
||||||
|
'revisedTurnover',
|
||||||
|
'subjectNuit',
|
||||||
|
'turnoverYear',
|
||||||
|
'reference',
|
||||||
|
].some(
|
||||||
|
(k) =>
|
||||||
|
typeof (c as unknown as Record<string, unknown>)[k] !==
|
||||||
|
'string',
|
||||||
|
)
|
||||||
|
)
|
||||||
|
throw Error('Rectificação PEC não recuperável;original preservado.');
|
||||||
|
}
|
||||||
|
export function correctedPecTurnover(
|
||||||
|
c: PecCorrection | undefined,
|
||||||
|
original: string,
|
||||||
|
subjectNuit: string,
|
||||||
|
previousYear: string,
|
||||||
|
) {
|
||||||
|
if (!c) return { value: original, pending: [] as string[], applied: false };
|
||||||
|
validatePecCorrection(c);
|
||||||
|
if (c.apply === 'no')
|
||||||
|
return { value: original, pending: [] as string[], applied: false };
|
||||||
|
const pending: string[] = [];
|
||||||
|
if (c.apply !== 'yes')
|
||||||
|
pending.push(
|
||||||
|
'PEC:escolha aplicar a rectificação ou conservar o volume original.',
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
c.originalTurnover !== original ||
|
||||||
|
c.subjectNuit !== subjectNuit ||
|
||||||
|
c.turnoverYear !== previousYear ||
|
||||||
|
previousYear !== '2025'
|
||||||
|
)
|
||||||
|
pending.push(
|
||||||
|
'PEC:origem,titular ou exercício da rectificação divergem da base;reconfira sem sobrescrever os valores.',
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
!/^\d{1,18}(\.\d{1,2})?$/.test(c.revisedTurnover) ||
|
||||||
|
!c.reference.trim()
|
||||||
|
)
|
||||||
|
pending.push(
|
||||||
|
'PEC:rectificação exige volume corrigido2025 em MZN e fonte/fundamento;valor desconhecido não equivale a zero.',
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
value: pending.length ? '' : c.revisedTurnover,
|
||||||
|
pending,
|
||||||
|
applied: pending.length === 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import {validatePecCorrection,correctedPecTurnover,pecCorrectionSource,type PecCorrection} from './irpc-pec-correction.ts';
|
||||||
import {validatePecPlan,pecPlanReport,type PecPlan} from './irpc-pec-plan.ts';
|
import {validatePecPlan,pecPlanReport,type PecPlan} from './irpc-pec-plan.ts';
|
||||||
import {
|
import {
|
||||||
decimalFraction,
|
decimalFraction,
|
||||||
@@ -12,6 +13,7 @@ export const pecObligationSource =
|
|||||||
export type PecObligation = {
|
export type PecObligation = {
|
||||||
version: 1;
|
version: 1;
|
||||||
plan?: PecPlan;
|
plan?: PecPlan;
|
||||||
|
correction?: PecCorrection;
|
||||||
scope: '' | 'ordinary_resident' | 'other';
|
scope: '' | 'ordinary_resident' | 'other';
|
||||||
subjectNuit: string;
|
subjectNuit: string;
|
||||||
year: string;
|
year: string;
|
||||||
@@ -40,6 +42,7 @@ export const newPecObligation = (
|
|||||||
ppcReference: '',
|
ppcReference: '',
|
||||||
});
|
});
|
||||||
export function validatePecObligation(p: PecObligation) {
|
export function validatePecObligation(p: PecObligation) {
|
||||||
|
if(p?.correction!==undefined)validatePecCorrection(p.correction);
|
||||||
if(p?.plan!==undefined)validatePecPlan(p.plan);
|
if(p?.plan!==undefined)validatePecPlan(p.plan);
|
||||||
if (
|
if (
|
||||||
!p ||
|
!p ||
|
||||||
@@ -65,6 +68,7 @@ export function calculatePecObligation(
|
|||||||
) {
|
) {
|
||||||
validatePecObligation(p);
|
validatePecObligation(p);
|
||||||
const pending: string[] = [];
|
const pending: string[] = [];
|
||||||
|
const turnover=correctedPecTurnover(p.correction,p.previousTurnover,p.subjectNuit,p.previousYear);
|
||||||
let percentage: FiscalFraction | null = null,
|
let percentage: FiscalFraction | null = null,
|
||||||
limited: FiscalFraction | null = null,
|
limited: FiscalFraction | null = null,
|
||||||
total: FiscalFraction | null = null,
|
total: FiscalFraction | null = null,
|
||||||
@@ -93,6 +97,7 @@ export function calculatePecObligation(
|
|||||||
notApplicable = true;
|
notApplicable = true;
|
||||||
total = fraction(0n);
|
total = fraction(0n);
|
||||||
} else if (!pending.length) {
|
} else if (!pending.length) {
|
||||||
|
pending.push(...turnover.pending);
|
||||||
if (
|
if (
|
||||||
p.previousYear !== '2025' ||
|
p.previousYear !== '2025' ||
|
||||||
!p.turnoverReference.trim() ||
|
!p.turnoverReference.trim() ||
|
||||||
@@ -102,7 +107,7 @@ export function calculatePecObligation(
|
|||||||
'PEC: falta exercício anterior2025 e fontes do volume de negócios e PPC efectivamente pagos,mesmo quando zero.',
|
'PEC: falta exercício anterior2025 e fontes do volume de negócios e PPC efectivamente pagos,mesmo quando zero.',
|
||||||
);
|
);
|
||||||
if (
|
if (
|
||||||
![p.previousTurnover, p.previousPpcPaid].every((v) =>
|
![turnover.value, p.previousPpcPaid].every((v) =>
|
||||||
/^\d{1,18}(\.\d{1,2})?$/.test(v),
|
/^\d{1,18}(\.\d{1,2})?$/.test(v),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -110,7 +115,7 @@ export function calculatePecObligation(
|
|||||||
'PEC: valores anteriores desconhecidos/inválidos;zero deve ser explícito.',
|
'PEC: valores anteriores desconhecidos/inválidos;zero deve ser explícito.',
|
||||||
);
|
);
|
||||||
if (!pending.length) {
|
if (!pending.length) {
|
||||||
const v = decimalFraction(p.previousTurnover),
|
const v = decimalFraction(turnover.value),
|
||||||
ppc = decimalFraction(p.previousPpcPaid);
|
ppc = decimalFraction(p.previousPpcPaid);
|
||||||
if (BigInt(v.numerator) === 0n)
|
if (BigInt(v.numerator) === 0n)
|
||||||
pending.push(
|
pending.push(
|
||||||
@@ -140,12 +145,13 @@ export function calculatePecObligation(
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
version: pecObligationVersion,
|
version: pecObligationVersion,
|
||||||
source: pecObligationSource,
|
source: p.correction ? pecObligationSource.replace('rectificação29(3),','')+' '+pecCorrectionSource : pecObligationSource,
|
||||||
pending,
|
pending,
|
||||||
percentage,
|
percentage,
|
||||||
limited,
|
limited,
|
||||||
total,
|
total,
|
||||||
notApplicable,
|
notApplicable,
|
||||||
|
turnoverCorrection: p.correction ? {original:p.correction.originalTurnover,revised:p.correction.revisedTurnover,applied:!notApplicable&&turnover.applied,pending:turnover.pending,reference:p.correction.reference,source:pecCorrectionSource}:null,
|
||||||
months:
|
months:
|
||||||
total && !notApplicable ? ['2026-06', '2026-08', '2026-10'] : [],
|
total && !notApplicable ? ['2026-06', '2026-08', '2026-10'] : [],
|
||||||
instalmentAmountsDetermined: false,
|
instalmentAmountsDetermined: false,
|
||||||
@@ -162,7 +168,10 @@ export function pecObligationReport(
|
|||||||
const m = calculatePecObligation(p, subjectNuit, year, assessment),
|
const m = calculatePecObligation(p, subjectNuit, year, assessment),
|
||||||
show = (v: FiscalFraction | null) =>
|
show = (v: FiscalFraction | null) =>
|
||||||
v ? fractionPresentation(v).decimal + ' MZN' : 'Indeterminado';
|
v ? fractionPresentation(v).decimal + ' MZN' : 'Indeterminado';
|
||||||
|
const originalEstimate=p.correction ? calculatePecObligation({...p,correction:undefined},subjectNuit,year,assessment).total:null;
|
||||||
|
const correctionEffect=originalEstimate&&m.total ? fractionPresentation(subtractFractions(m.total,originalEstimate)).decimal+' MZN':'por determinar';
|
||||||
return [
|
return [
|
||||||
|
...(p.correction ? [['IRPC — L8','PEC — rectificação do volume','Original: '+p.correction.originalTurnover+' MZN;corrigido: '+p.correction.revisedTurnover+' MZN;'+(m.turnoverCorrection?.applied?'aplicada à estimativa.':'conservada sem aplicação à estimativa.')+' Estimativa original: '+show(originalEstimate)+';efeito na estimativa: '+correctionEffect+'. '+turnoverCorrectionNotice(m.turnoverCorrection),p.correction.reference+'; '+pecCorrectionSource] as [string,string,string,string]]:[]),
|
||||||
...(p.plan ? pecPlanReport(p.plan,m.total,m.notApplicable,year) : []),
|
...(p.plan ? pecPlanReport(p.plan,m.total,m.notApplicable,year) : []),
|
||||||
[
|
[
|
||||||
'IRPC — L8',
|
'IRPC — L8',
|
||||||
@@ -190,3 +199,5 @@ export function pecObligationReport(
|
|||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function turnoverCorrectionNotice(c:{pending:string[]}|null){return c?.pending.join(' ')??'';}
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
final class IrpcPecCorrectionDraftTest extends TestCase
|
||||||
|
{
|
||||||
|
use DatabaseTransactions;
|
||||||
|
|
||||||
|
public function test_pec_obligation_and_calendar_survive_native_draft_without_new_paid_deductions(): void
|
||||||
|
{
|
||||||
|
$target = DB::selectOne('select current_database() as name,inet_server_port() as port');
|
||||||
|
$this->assertSame('proactive_investment_20261027_test', $target->name);
|
||||||
|
$this->assertSame(55441, (int) $target->port);
|
||||||
|
config(['investor-path.enabled' => true, 'suitecrm.enabled' => false]);
|
||||||
|
$this->actingAs(User::findOrFail(1021));
|
||||||
|
$id = null;
|
||||||
|
$etag = null;
|
||||||
|
foreach (['higher' => 138, 'lower' => 138, 'retained' => 138, 'changed_base' => 138, 'zero' => 138] as $name => $balance) {
|
||||||
|
$snapshot = json_decode(file_get_contents(base_path('outputs/irpc-pec-correction-20261111/'.$name.'.json')), true, flags: JSON_THROW_ON_ERROR);
|
||||||
|
$personal = ['person_type' => $snapshot['person_type']];
|
||||||
|
unset($snapshot['person_type']);
|
||||||
|
$payload = ['personal_snapshot' => $personal, 'economic_snapshot' => $snapshot];
|
||||||
|
if ($id === null) {
|
||||||
|
$id = $this->postJson('/api/v1/simulation-drafts', $payload, ['Idempotency-Key' => (string) Str::uuid()])->assertCreated()->json('data.id');
|
||||||
|
} else {
|
||||||
|
$this->putJson('/api/v1/simulation-drafts/'.$id, $payload, ['If-Match' => $etag])->assertOk();
|
||||||
|
}
|
||||||
|
$response = $this->getJson('/api/v1/simulation-drafts/'.$id)->assertOk();
|
||||||
|
$etag = $response->headers->get('ETag');
|
||||||
|
$back = $response->json('data.economic_snapshot');
|
||||||
|
$this->assertSame($snapshot['modelo_a_snapshot'], $back['modelo_a_snapshot']);
|
||||||
|
$book = json_decode($back['modelo_a_snapshot'], true, flags: JSON_THROW_ON_ERROR)['irpcAnnual'][$snapshot['context_opportunity']];
|
||||||
|
$this->assertCount(1, $book['history']);
|
||||||
|
$this->assertSame(['numerator' => '138', 'denominator' => '1'], $book['history'][0]['model']['arithmeticBalance']);
|
||||||
|
$this->assertSame($balance === null ? null : ['numerator' => (string) $balance, 'denominator' => '1'], $book['result']['model']['arithmeticBalance']);
|
||||||
|
$this->assertSame($book['pecObligation'], $book['result']['model']['input']['pecObligation']);
|
||||||
|
$this->assertSame('SYNTHETIC corrected sales2025 reviewed2026', $book['pecObligation']['correction']['reference']);
|
||||||
|
$this->assertSame('SYNTHETIC effective activity'.($name === 'start' ? '2026' : '2020'), $book['pecObligation']['activityReference']);
|
||||||
|
file_put_contents(base_path('outputs/irpc-pec-correction-20261111/recovered-'.$name.'.json'), json_encode($back, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
import test from 'node:test';
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import fs from 'node:fs';
|
||||||
|
import { compileFunction } from 'node:vm';
|
||||||
|
import { createRequire } from 'node:module';
|
||||||
|
import React from 'react';
|
||||||
|
import ts from 'typescript';
|
||||||
|
import * as annual from '../../resources/js/lib/irpc-annual-settlement.ts';
|
||||||
|
import * as investment from '../../resources/js/lib/irpc-pec-correction.ts';
|
||||||
|
import * as rational from '../../resources/js/lib/fiscal-rational.ts';
|
||||||
|
import { renderToStaticMarkup } from 'react-dom/server';
|
||||||
|
|
||||||
|
const require = createRequire(import.meta.url),
|
||||||
|
module = { exports: {} };
|
||||||
|
const source = fs.readFileSync(
|
||||||
|
new URL(
|
||||||
|
'../../resources/js/components/simulation-irpc-pec-correction.tsx',
|
||||||
|
import.meta.url,
|
||||||
|
),
|
||||||
|
'utf8',
|
||||||
|
);
|
||||||
|
const output = ts.transpileModule(source, {
|
||||||
|
compilerOptions: {
|
||||||
|
module: ts.ModuleKind.CommonJS,
|
||||||
|
target: ts.ScriptTarget.ES2022,
|
||||||
|
jsx: ts.JsxEmit.ReactJSX,
|
||||||
|
},
|
||||||
|
}).outputText;
|
||||||
|
compileFunction(output, ['require', 'module', 'exports'])(
|
||||||
|
(name) =>
|
||||||
|
name === './simulation-irpc-pec-plan'
|
||||||
|
? { SimulationIrpcPecPlan: () => null }
|
||||||
|
: name === '@/lib/irpc-annual-settlement'
|
||||||
|
? annual
|
||||||
|
: name === '@/lib/irpc-pec-correction'
|
||||||
|
? investment
|
||||||
|
: name === '@/lib/fiscal-rational'
|
||||||
|
? rational
|
||||||
|
: name === './simulation-irpc-special'
|
||||||
|
? { QualificationFields: () => null }
|
||||||
|
: name === './fiscal-proof-parcel'
|
||||||
|
? { FiscalProofParcelFields: () => null }
|
||||||
|
: require(name),
|
||||||
|
module,
|
||||||
|
module.exports,
|
||||||
|
);
|
||||||
|
const Component = module.exports.SimulationIrpcPecCorrection;
|
||||||
|
const text = (node) =>
|
||||||
|
typeof node === 'string' || typeof node === 'number'
|
||||||
|
? String(node)
|
||||||
|
: React.isValidElement(node)
|
||||||
|
? React.Children.toArray(node.props.children).map(text).join('')
|
||||||
|
: '';
|
||||||
|
const all = (node) =>
|
||||||
|
React.isValidElement(node)
|
||||||
|
? [node, ...React.Children.toArray(node.props.children).flatMap(all)]
|
||||||
|
: [];
|
||||||
|
test('actual correction form preserves original and revised values when retaining baseline', () => {
|
||||||
|
let value;
|
||||||
|
const render = () =>
|
||||||
|
Component({
|
||||||
|
value,
|
||||||
|
original: '10000000',
|
||||||
|
subjectNuit: '123456789',
|
||||||
|
onChange: (next) => (value = next),
|
||||||
|
});
|
||||||
|
const click = (label) => {
|
||||||
|
const n = all(render()).find(
|
||||||
|
(x) => x.type === 'button' && text(x) === label,
|
||||||
|
);
|
||||||
|
assert.ok(n, label);
|
||||||
|
n.props.onClick();
|
||||||
|
};
|
||||||
|
const change = (label, v) => {
|
||||||
|
const n = all(render()).find((x) => x.props['aria-label'] === label);
|
||||||
|
assert.ok(n, label);
|
||||||
|
n.props.onChange({ target: { value: v } });
|
||||||
|
};
|
||||||
|
click('Rectificar volume na simulação PEC');
|
||||||
|
assert.equal(value.originalTurnover, '10000000');
|
||||||
|
assert.equal(value.subjectNuit, '123456789');
|
||||||
|
change('Volume corrigido — PEC', '50000000');
|
||||||
|
change(
|
||||||
|
'Fonte e fundamento da rectificação PEC',
|
||||||
|
'SYNTHETIC correction2025',
|
||||||
|
);
|
||||||
|
change('Aplicar rectificação PEC', 'yes');
|
||||||
|
change('Aplicar rectificação PEC', 'no');
|
||||||
|
assert.equal(value.revisedTurnover, '50000000');
|
||||||
|
assert.equal(value.originalTurnover, '10000000');
|
||||||
|
change('Aplicar rectificação PEC', 'yes');
|
||||||
|
assert.match(renderToStaticMarkup(render()), /não submete declaração/);
|
||||||
|
assert.match(text(render()), /original permanece/);
|
||||||
|
});
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
import test from 'node:test';
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import {
|
||||||
|
newPecCorrection,
|
||||||
|
validatePecCorrection,
|
||||||
|
} from '../../resources/js/lib/irpc-pec-correction.ts';
|
||||||
|
import { calculatePecObligation } from '../../resources/js/lib/irpc-pec-obligation.ts';
|
||||||
|
import {
|
||||||
|
calculatePecPlan,
|
||||||
|
newPecPlan,
|
||||||
|
} from '../../resources/js/lib/irpc-pec-plan.ts';
|
||||||
|
import { plan } from './helpers/irpc-pec-obligation-case.mjs';
|
||||||
|
import { combinedCase } from './helpers/irpc-investment-combination-case.mjs';
|
||||||
|
import {
|
||||||
|
recordAnnual,
|
||||||
|
annualCurrent,
|
||||||
|
annualBook,
|
||||||
|
saveAnnualBook,
|
||||||
|
annualHtml,
|
||||||
|
} from '../../resources/js/lib/irpc-annual-settlement.ts';
|
||||||
|
const f = (n) => ({ numerator: String(n), denominator: '1' }),
|
||||||
|
corrected = (p = plan()) => ({
|
||||||
|
...p,
|
||||||
|
correction: {
|
||||||
|
...newPecCorrection(p.previousTurnover, p.subjectNuit),
|
||||||
|
apply: 'yes',
|
||||||
|
revisedTurnover: '50000000',
|
||||||
|
reference: 'SYNTHETIC corrected sales2025 reviewed2026',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
calc = (p) => calculatePecObligation(p, p.subjectNuit, p.year, 'normal');
|
||||||
|
test('documented correction preserves original and recomputes percentage,cap and previous PPC once', () => {
|
||||||
|
const p = corrected(),
|
||||||
|
m = calc(p);
|
||||||
|
assert.equal(p.previousTurnover, '10000000');
|
||||||
|
assert.deepEqual(m.total, f(90000));
|
||||||
|
assert.deepEqual(calc(plan()).total, f(40000));
|
||||||
|
assert.equal(m.turnoverCorrection.applied, true);
|
||||||
|
assert.equal(m.annualDeductionCreated, false);
|
||||||
|
});
|
||||||
|
test('explicitly retaining original leaves corrective values recoverable without using them', () => {
|
||||||
|
const p = corrected();
|
||||||
|
p.correction.apply = 'no';
|
||||||
|
assert.deepEqual(calc(p).total, f(40000));
|
||||||
|
assert.equal(p.correction.revisedTurnover, '50000000');
|
||||||
|
const {d,b}=combinedCase();b.pecObligation={...p,subjectNuit:b.subjectNuit,correction:{...p.correction,subjectNuit:b.subjectNuit}};d.modelo_a_snapshot=saveAnnualBook(d,recordAnnual(d,b,'2026-11-11T12:05:00Z'));assert.match(annualHtml(d),/Estimativa original: 40000/);assert.match(annualHtml(d),/efeito na estimativa: 0/);
|
||||||
|
p.correction.apply = '';
|
||||||
|
assert.equal(calc(p).total, null);
|
||||||
|
});
|
||||||
|
test('changed original,titular,year and absent evidence cannot silently qualify correction;zero uses29(5)', () => {
|
||||||
|
for (const patch of [
|
||||||
|
{ originalTurnover: '1' },
|
||||||
|
{ subjectNuit: '999999999' },
|
||||||
|
{ turnoverYear: '2024' },
|
||||||
|
{ reference: '' },
|
||||||
|
{ revisedTurnover: '' },
|
||||||
|
]) {
|
||||||
|
const p = corrected();
|
||||||
|
Object.assign(p.correction, patch);
|
||||||
|
assert.equal(calc(p).total, null);
|
||||||
|
}
|
||||||
|
const p = corrected();
|
||||||
|
p.correction.revisedTurnover = '0';
|
||||||
|
assert.match(calc(p).pending.join(' '), /29\(5\)/);
|
||||||
|
assert.throws(() =>
|
||||||
|
validatePecCorrection({ ...p.correction, revisedTurnover: null }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test('correction invalidates old cash-flow allocation and annual snapshot;history/deductions remain intact', () => {
|
||||||
|
const { d, b } = combinedCase();
|
||||||
|
b.pecObligation = { ...plan(), subjectNuit: b.subjectNuit };
|
||||||
|
let current = recordAnnual(d, b, '2026-11-11T12:00:00Z');
|
||||||
|
current.pecObligation = corrected(current.pecObligation);
|
||||||
|
current.pecObligation.plan = {
|
||||||
|
...newPecPlan(),
|
||||||
|
mode: 'three',
|
||||||
|
reference: 'SYNTHETIC old cash hypothesis',
|
||||||
|
june: '10000',
|
||||||
|
august: '15000',
|
||||||
|
october: '15000',
|
||||||
|
};
|
||||||
|
assert.equal(annualCurrent(d, current), false);
|
||||||
|
const m = calc(current.pecObligation);
|
||||||
|
assert.ok(
|
||||||
|
calculatePecPlan(
|
||||||
|
current.pecObligation.plan,
|
||||||
|
m.total,
|
||||||
|
m.notApplicable,
|
||||||
|
'2026',
|
||||||
|
).pending.length,
|
||||||
|
);
|
||||||
|
current = recordAnnual(d, current, '2026-11-11T12:01:00Z');
|
||||||
|
d.modelo_a_snapshot = saveAnnualBook(d, current);
|
||||||
|
const back = annualBook(d);
|
||||||
|
assert.deepEqual(back.result.model.arithmeticBalance, f(138));
|
||||||
|
assert.deepEqual(back.pecPayments, b.pecPayments);
|
||||||
|
assert.equal(back.history.length, 1);
|
||||||
|
assert.equal(annualCurrent(d, back), true);
|
||||||
|
assert.match(annualHtml(d), /efeito na estimativa: 50000/);
|
||||||
|
});
|
||||||
@@ -30,7 +30,7 @@ const output = ts.transpileModule(source, {
|
|||||||
}).outputText;
|
}).outputText;
|
||||||
compileFunction(output, ['require', 'module', 'exports'])(
|
compileFunction(output, ['require', 'module', 'exports'])(
|
||||||
(name) =>
|
(name) =>
|
||||||
name === './simulation-irpc-pec-plan'
|
name === './simulation-irpc-pec-correction' ? {SimulationIrpcPecCorrection:()=>null} : name === './simulation-irpc-pec-plan'
|
||||||
? {SimulationIrpcPecPlan:()=>null}
|
? {SimulationIrpcPecPlan:()=>null}
|
||||||
: name === '@/lib/irpc-annual-settlement'
|
: name === '@/lib/irpc-annual-settlement'
|
||||||
? annual
|
? annual
|
||||||
|
|||||||
@@ -0,0 +1,132 @@
|
|||||||
|
import { newPecCorrection } from '../resources/js/lib/irpc-pec-correction.ts';
|
||||||
|
import {
|
||||||
|
calculatePecPlan,
|
||||||
|
newPecPlan,
|
||||||
|
} from '../resources/js/lib/irpc-pec-plan.ts';
|
||||||
|
import fs from 'node:fs';
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import { combinedCase } from '../tests/frontend/helpers/irpc-investment-combination-case.mjs';
|
||||||
|
import { plan } from '../tests/frontend/helpers/irpc-pec-obligation-case.mjs';
|
||||||
|
import { calculatePecObligation } from '../resources/js/lib/irpc-pec-obligation.ts';
|
||||||
|
import {
|
||||||
|
recordAnnual,
|
||||||
|
saveAnnualBook,
|
||||||
|
annualBook,
|
||||||
|
annualCurrent,
|
||||||
|
annualHtml,
|
||||||
|
} from '../resources/js/lib/irpc-annual-settlement.ts';
|
||||||
|
import { createProjectDossierReport } from '../resources/js/lib/project-dossier-report.ts';
|
||||||
|
const out = new URL(
|
||||||
|
'../outputs/irpc-pec-correction-20261111/',
|
||||||
|
import.meta.url,
|
||||||
|
);
|
||||||
|
fs.mkdirSync(out, { recursive: true });
|
||||||
|
const allocation = {
|
||||||
|
...newPecPlan(),
|
||||||
|
mode: 'three',
|
||||||
|
reference: 'SYNTHETIC cash-flow hypothesis for viability',
|
||||||
|
june: '10000',
|
||||||
|
august: '15000',
|
||||||
|
october: '15000',
|
||||||
|
};
|
||||||
|
const correction = {
|
||||||
|
...newPecCorrection('10000000', '123456789'),
|
||||||
|
apply: 'yes',
|
||||||
|
revisedTurnover: '50000000',
|
||||||
|
reference: 'SYNTHETIC corrected sales2025 reviewed2026',
|
||||||
|
};
|
||||||
|
const scenarios = {
|
||||||
|
higher: { correction },
|
||||||
|
lower: { correction: { ...correction, revisedTurnover: '1000000' } },
|
||||||
|
retained: { correction: { ...correction, apply: 'no' } },
|
||||||
|
changed_base: { correction: { ...correction, originalTurnover: '1' } },
|
||||||
|
zero: { correction: { ...correction, revisedTurnover: '0' } },
|
||||||
|
};
|
||||||
|
for (const [name, changes] of Object.entries(scenarios)) {
|
||||||
|
if (process.argv[2] === 'generate') {
|
||||||
|
const { d, b } = combinedCase();
|
||||||
|
const prior = recordAnnual(d, b, '2026-11-11T10:00:00Z');
|
||||||
|
prior.pecObligation = {
|
||||||
|
...plan(),
|
||||||
|
subjectNuit: b.subjectNuit,
|
||||||
|
year: b.year,
|
||||||
|
...changes,
|
||||||
|
correction: { ...changes.correction, subjectNuit: b.subjectNuit },
|
||||||
|
plan: allocation,
|
||||||
|
};
|
||||||
|
const next = recordAnnual(d, prior, '2026-11-11T10:01:00Z');
|
||||||
|
d.modelo_a_snapshot = saveAnnualBook(d, next);
|
||||||
|
fs.writeFileSync(new URL(name + '.json', out), JSON.stringify(d));
|
||||||
|
} else {
|
||||||
|
const d = JSON.parse(
|
||||||
|
fs.readFileSync(
|
||||||
|
new URL('recovered-' + name + '.json', out),
|
||||||
|
'utf8',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
b = annualBook(d),
|
||||||
|
m = calculatePecObligation(
|
||||||
|
b.pecObligation,
|
||||||
|
b.subjectNuit,
|
||||||
|
b.year,
|
||||||
|
b.assessment,
|
||||||
|
);
|
||||||
|
assert.equal(annualCurrent(d, b), true);
|
||||||
|
assert.equal(b.history.length, 1);
|
||||||
|
assert.deepEqual(b.result.model.arithmeticBalance, {
|
||||||
|
numerator: '138',
|
||||||
|
denominator: '1',
|
||||||
|
});
|
||||||
|
const html = annualHtml(d),
|
||||||
|
report = createProjectDossierReport(d, {
|
||||||
|
selectedSections: new Set(['finance']),
|
||||||
|
generatedAt: '2026-11-11T10:02:00Z',
|
||||||
|
});
|
||||||
|
assert.match(html, /PEC — estimativa fiscal/);
|
||||||
|
assert.match(JSON.stringify(report), /PEC — estimativa fiscal/);
|
||||||
|
assert.equal(m.paymentExecuted, false);
|
||||||
|
assert.equal(m.annualDeductionCreated, false);
|
||||||
|
const allocationModel = calculatePecPlan(
|
||||||
|
b.pecObligation.plan,
|
||||||
|
m.total,
|
||||||
|
m.notApplicable,
|
||||||
|
b.year,
|
||||||
|
);
|
||||||
|
const expected = {
|
||||||
|
higher: '90000',
|
||||||
|
lower: '20000',
|
||||||
|
retained: '40000',
|
||||||
|
changed_base: null,
|
||||||
|
zero: null,
|
||||||
|
}[name];
|
||||||
|
assert.equal(m.total?.numerator ?? null, expected);
|
||||||
|
assert.equal(allocationModel.allocationReconciled, name === 'retained');
|
||||||
|
assert.equal(allocationModel.annualDeductionCreated, false);
|
||||||
|
assert.match(html, /PEC — rectificação do volume/);
|
||||||
|
assert.match(JSON.stringify(report), /PEC — rectificação do volume/);
|
||||||
|
fs.writeFileSync(new URL(name + '-report.html', out), html);
|
||||||
|
fs.writeFileSync(
|
||||||
|
new URL(name + '-verification.json', out),
|
||||||
|
JSON.stringify(
|
||||||
|
{
|
||||||
|
synthetic: true,
|
||||||
|
apiRecovered: true,
|
||||||
|
current: true,
|
||||||
|
historyPreserved: true,
|
||||||
|
annualBalanceUnchanged: true,
|
||||||
|
report: true,
|
||||||
|
visualReview: false,
|
||||||
|
model: m,
|
||||||
|
allocationModel,
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(
|
||||||
|
process.argv[2] === 'generate'
|
||||||
|
? 'Five synthetic PEC correction scenarios prepared.'
|
||||||
|
: 'Five native recovered PEC correction scenarios recalculated and reported without creating deductions.',
|
||||||
|
);
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$root = dirname(__DIR__);
|
||||||
|
if (($argv[1] ?? '') !== 'test' || basename($root) !== 'portal-investidor') {
|
||||||
|
throw new RuntimeException('Explicit local test operation required.');
|
||||||
|
}
|
||||||
|
foreach (['APP_ENV' => 'testing', 'APP_KEY' => 'base64:'.base64_encode(str_repeat('Q', 32)),
|
||||||
|
'APP_CONFIG_CACHE' => $root.'/outputs/irpc-pec-correction-20261111/no-config.php',
|
||||||
|
'DB_CONNECTION' => 'pgsql', 'DB_HOST' => '127.0.0.1', 'DB_PORT' => '55441',
|
||||||
|
'DB_DATABASE' => 'proactive_investment_20261027_test', 'DB_USERNAME' => 'investmoz_test', 'DB_PASSWORD' => '',
|
||||||
|
'DB_URL' => '', 'DATABASE_URL' => '', 'CACHE_STORE' => 'array', 'SESSION_DRIVER' => 'array',
|
||||||
|
'QUEUE_CONNECTION' => 'sync', 'MAIL_MAILER' => 'array', 'SUITECRM_ENABLED' => 'false'] as $key => $value) {
|
||||||
|
putenv($key.'='.$value);
|
||||||
|
$_ENV[$key] = $_SERVER[$key] = $value;
|
||||||
|
}
|
||||||
|
$pdo = new PDO('pgsql:host=127.0.0.1;port=55441;dbname=proactive_investment_20261027_test', 'investmoz_test', '');
|
||||||
|
$identity = $pdo->query('select current_database() as name,inet_server_port() as port')->fetch(PDO::FETCH_ASSOC);
|
||||||
|
if ($identity['name'] !== 'proactive_investment_20261027_test' || (int) $identity['port'] !== 55441) {
|
||||||
|
throw new RuntimeException('Isolated QA identity required.');
|
||||||
|
}
|
||||||
|
$argv = ['phpunit', '-c', 'phpunit.investmoz.xml', '--do-not-cache-result', 'tests/Feature/IrpcPecCorrectionDraftTest.php'];
|
||||||
|
$argc = count($argv);
|
||||||
|
$_SERVER['argv'] = $argv;
|
||||||
|
$_SERVER['argc'] = $argc;
|
||||||
|
require $root.'/vendor/autoload.php';
|
||||||
|
require $root.'/vendor/phpunit/phpunit/phpunit';
|
||||||
Reference in New Issue
Block a user