This commit is contained in:
@@ -1,3 +1,17 @@
|
|||||||
|
## 10/11/2026 — janela de cinco exercícios CBF15 em QA
|
||||||
|
|
||||||
|
Homologação mantém `89e6d72c`; o delta seguinte ainda não está instalado. Pendência encerrada em QA: contagem inclusiva dos cinco exercícios civis do CBF15(4/7) na liquidação2026, para investimento efectivo do próprio2026. Início de exploração para projecto novo; início dos procedimentos após aprovação para projecto em funcionamento. Não usa cinco anos móveis nem reinicia o prazo pela data de aquisição. Início2022, mesmo em31/12 → quinto exercício2026, dedução200 pela quota residual, saldo500MZN. Início2021 → sexto exercício2026, prazo expirado, dedução0 e saldo700; activo e documento preservados. Datas inválidas/futuras e activos antigos apresentados como investimento corrente permanecem indeterminados/recusados, não zero por omissão.
|
||||||
|
|
||||||
|
Cálculo → POST/PUT do mesmo rascunho privado → GET → recálculo → HTML/dossier demonstrados no QA55441 existente, com rollback, cinco/seis resultados históricos e todos os anteriores preservados. Provas `outputs/irpc-cbf15-window-20261110/{fifth,expired}-verification.json` e relatórios respectivos. Modelo `IRPC-CBF15/2`, livro existente versão2 sem migration; snapshots aceites não são reescritos. Fonte já aceite APIEX2025 PDF71–72,CBF15(4/7), reutilizada sem nova validação de autenticidade. Provas anteriores reutilizadas; apenas fronteiras alteradas conferidas.
|
||||||
|
|
||||||
|
Saldo: transporte de créditos de investimento de anos anteriores, repartição automática/múltiplos projectos, EP/L7/cumulações/outros benefícios/formulários oficiais continuam por implementar nos respectivos recortes. A janela corrente não declara o transporte entregue. Resíduos L1–L7, restanteL8 e L9–L16 e saldo dos módulos não fiscais preservados. Conferência visual pendente no mesmo registo; portal não integral. Próximo trabalho executável: transporte documental CBF15 dentro da mesma janela, com consumo anterior reconciliado e sem duplicar o investimento corrente.
|
||||||
|
|
||||||
|
## 10/11/2026 — CBF15 instalado na homologação
|
||||||
|
|
||||||
|
**Revisão instalada: `89e6d72c1e86df8eb86e7be74583659975585036`**, promovida às13:27:59UTC e conferida às13:28:21UTC (15:28 locais), em https://teste-investidor.tempo.co.mz. Os17hashes do delta coincidem com Git; revisão/root/up/login HTTP200, configuração preservada, zero migrations/importações. Backup fresco/restauro190tabelas com contagens/checksums iguais e369ficheiros/configuração extraídos e conferidos;7c8d4726 preservada para reversão. Recibos `outputs/cbf15-delivery-20261110/{backup,restore,files-restore,verification,delivery}.json`; pacote imutável `outputs/entrega-cbf15-20261110.zip`, SHA25613bdecef2697edcc821d6fa490586d0c2f4b171b4308c655aa60ba5632664c83.
|
||||||
|
|
||||||
|
O recorte CBF15 abaixo deixa de ser pendência de instalação: primeiro exercício2026, residente geral/MZN, activos e condições documentados, colecta integral do projecto ou quota residual documentada, cálculo/gravação/recuperação/relatórios com histórico. As notas QA anteriores ficam como histórico das provas aceites. Outros exercícios/transporte/caducidade, repartição automática/múltiplos projectos, EP/L7/cumulações/outros benefícios/formulários e restantes resíduos L1–L7/L8/L9–L16 permanecem no plano existente. Próximo trabalho: residual executável de CBF15/L8, preservando o recorte instalado e sem repetir as provas aceites. Conferência visual continua pendente no mesmo registo; portal não declarado integral.
|
||||||
|
|
||||||
# Rastreabilidade, utilização e formação
|
# Rastreabilidade, utilização e formação
|
||||||
|
|
||||||
## 10/11/2026 — acordo PT–MZ instalado; crédito CBF15 demonstrado em QA
|
## 10/11/2026 — acordo PT–MZ instalado; crédito CBF15 demonstrado em QA
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
|
## 10/11/2026 — janela de cinco exercícios CBF15 em QA
|
||||||
|
|
||||||
|
Homologação mantém `89e6d72c`; o delta seguinte ainda não está instalado. Pendência encerrada em QA: contagem inclusiva dos cinco exercícios civis do CBF15(4/7) na liquidação2026, para investimento efectivo do próprio2026. Início de exploração para projecto novo; início dos procedimentos após aprovação para projecto em funcionamento. Não usa cinco anos móveis nem reinicia o prazo pela data de aquisição. Início2022, mesmo em31/12 → quinto exercício2026, dedução200 pela quota residual, saldo500MZN. Início2021 → sexto exercício2026, prazo expirado, dedução0 e saldo700; activo e documento preservados. Datas inválidas/futuras e activos antigos apresentados como investimento corrente permanecem indeterminados/recusados, não zero por omissão.
|
||||||
|
|
||||||
|
Cálculo → POST/PUT do mesmo rascunho privado → GET → recálculo → HTML/dossier demonstrados no QA55441 existente, com rollback, cinco/seis resultados históricos e todos os anteriores preservados. Provas `outputs/irpc-cbf15-window-20261110/{fifth,expired}-verification.json` e relatórios respectivos. Modelo `IRPC-CBF15/2`, livro existente versão2 sem migration; snapshots aceites não são reescritos. Fonte já aceite APIEX2025 PDF71–72,CBF15(4/7), reutilizada sem nova validação de autenticidade. Provas anteriores reutilizadas; apenas fronteiras alteradas conferidas.
|
||||||
|
|
||||||
|
Saldo: transporte de créditos de investimento de anos anteriores, repartição automática/múltiplos projectos, EP/L7/cumulações/outros benefícios/formulários oficiais continuam por implementar nos respectivos recortes. A janela corrente não declara o transporte entregue. Resíduos L1–L7, restanteL8 e L9–L16 e saldo dos módulos não fiscais preservados. Conferência visual pendente no mesmo registo; portal não integral. Próximo trabalho executável: transporte documental CBF15 dentro da mesma janela, com consumo anterior reconciliado e sem duplicar o investimento corrente.
|
||||||
|
|
||||||
|
## 10/11/2026 — CBF15 instalado na homologação
|
||||||
|
|
||||||
|
**Revisão instalada: `89e6d72c1e86df8eb86e7be74583659975585036`**, promovida às13:27:59UTC e conferida às13:28:21UTC (15:28 locais), em https://teste-investidor.tempo.co.mz. Os17hashes do delta coincidem com Git; revisão/root/up/login HTTP200, configuração preservada, zero migrations/importações. Backup fresco/restauro190tabelas com contagens/checksums iguais e369ficheiros/configuração extraídos e conferidos;7c8d4726 preservada para reversão. Recibos `outputs/cbf15-delivery-20261110/{backup,restore,files-restore,verification,delivery}.json`; pacote imutável `outputs/entrega-cbf15-20261110.zip`, SHA25613bdecef2697edcc821d6fa490586d0c2f4b171b4308c655aa60ba5632664c83.
|
||||||
|
|
||||||
|
O recorte CBF15 abaixo deixa de ser pendência de instalação: primeiro exercício2026, residente geral/MZN, activos e condições documentados, colecta integral do projecto ou quota residual documentada, cálculo/gravação/recuperação/relatórios com histórico. As notas QA anteriores ficam como histórico das provas aceites. Outros exercícios/transporte/caducidade, repartição automática/múltiplos projectos, EP/L7/cumulações/outros benefícios/formulários e restantes resíduos L1–L7/L8/L9–L16 permanecem no plano existente. Próximo trabalho: residual executável de CBF15/L8, preservando o recorte instalado e sem repetir as provas aceites. Conferência visual continua pendente no mesmo registo; portal não declarado integral.
|
||||||
|
|
||||||
## 10/11/2026 — acordo PT–MZ instalado; crédito CBF15 demonstrado em QA
|
## 10/11/2026 — acordo PT–MZ instalado; crédito CBF15 demonstrado em QA
|
||||||
|
|
||||||
**Homologação: `7c8d4726e9f93325480e2ee6e5e790e9adbcaf19`**, promovida às12:36:07UTC e conferida às12:37:11UTC (14:37 locais). Revisão HTTPS/10hashes iguais, quatro entradasHTTP200; configuração preservada, zero migrations/importações. Backup/restauro190tabelas e369ficheiros/configuração conferidos, b10b35f3 preservada. Recibos `outputs/cdt-agreement-delivery-20261110/`. Falha inicial de recepção Git delimitada ao directório objects/7c pertencente aroot: apenas proprietário/grupo ajustados a conta do serviço,modo0755 inalterado; pedido original validado recolocado sem alterar corpo/SHA ou controlos. Recibos git-permission-repair/request-retry; não foi falha do portal nem alteração de segurança do navegador. Sessão SSH preservada para continuação autenticada, sem credencial em disco.
|
**Homologação: `7c8d4726e9f93325480e2ee6e5e790e9adbcaf19`**, promovida às12:36:07UTC e conferida às12:37:11UTC (14:37 locais). Revisão HTTPS/10hashes iguais, quatro entradasHTTP200; configuração preservada, zero migrations/importações. Backup/restauro190tabelas e369ficheiros/configuração conferidos, b10b35f3 preservada. Recibos `outputs/cdt-agreement-delivery-20261110/`. Falha inicial de recepção Git delimitada ao directório objects/7c pertencente aroot: apenas proprietário/grupo ajustados a conta do serviço,modo0755 inalterado; pedido original validado recolocado sem alterar corpo/SHA ou controlos. Recibos git-permission-repair/request-retry; não foi falha do portal nem alteração de segurança do navegador. Sessão SSH preservada para continuação autenticada, sem credencial em disco.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,3 +1,17 @@
|
|||||||
|
## 10/11/2026 — janela de cinco exercícios CBF15 em QA
|
||||||
|
|
||||||
|
Homologação mantém `89e6d72c`; o delta seguinte ainda não está instalado. Pendência encerrada em QA: contagem inclusiva dos cinco exercícios civis do CBF15(4/7) na liquidação2026, para investimento efectivo do próprio2026. Início de exploração para projecto novo; início dos procedimentos após aprovação para projecto em funcionamento. Não usa cinco anos móveis nem reinicia o prazo pela data de aquisição. Início2022, mesmo em31/12 → quinto exercício2026, dedução200 pela quota residual, saldo500MZN. Início2021 → sexto exercício2026, prazo expirado, dedução0 e saldo700; activo e documento preservados. Datas inválidas/futuras e activos antigos apresentados como investimento corrente permanecem indeterminados/recusados, não zero por omissão.
|
||||||
|
|
||||||
|
Cálculo → POST/PUT do mesmo rascunho privado → GET → recálculo → HTML/dossier demonstrados no QA55441 existente, com rollback, cinco/seis resultados históricos e todos os anteriores preservados. Provas `outputs/irpc-cbf15-window-20261110/{fifth,expired}-verification.json` e relatórios respectivos. Modelo `IRPC-CBF15/2`, livro existente versão2 sem migration; snapshots aceites não são reescritos. Fonte já aceite APIEX2025 PDF71–72,CBF15(4/7), reutilizada sem nova validação de autenticidade. Provas anteriores reutilizadas; apenas fronteiras alteradas conferidas.
|
||||||
|
|
||||||
|
Saldo: transporte de créditos de investimento de anos anteriores, repartição automática/múltiplos projectos, EP/L7/cumulações/outros benefícios/formulários oficiais continuam por implementar nos respectivos recortes. A janela corrente não declara o transporte entregue. Resíduos L1–L7, restanteL8 e L9–L16 e saldo dos módulos não fiscais preservados. Conferência visual pendente no mesmo registo; portal não integral. Próximo trabalho executável: transporte documental CBF15 dentro da mesma janela, com consumo anterior reconciliado e sem duplicar o investimento corrente.
|
||||||
|
|
||||||
|
## 10/11/2026 — CBF15 instalado na homologação
|
||||||
|
|
||||||
|
**Revisão instalada: `89e6d72c1e86df8eb86e7be74583659975585036`**, promovida às13:27:59UTC e conferida às13:28:21UTC (15:28 locais), em https://teste-investidor.tempo.co.mz. Os17hashes do delta coincidem com Git; revisão/root/up/login HTTP200, configuração preservada, zero migrations/importações. Backup fresco/restauro190tabelas com contagens/checksums iguais e369ficheiros/configuração extraídos e conferidos;7c8d4726 preservada para reversão. Recibos `outputs/cbf15-delivery-20261110/{backup,restore,files-restore,verification,delivery}.json`; pacote imutável `outputs/entrega-cbf15-20261110.zip`, SHA25613bdecef2697edcc821d6fa490586d0c2f4b171b4308c655aa60ba5632664c83.
|
||||||
|
|
||||||
|
O recorte CBF15 abaixo deixa de ser pendência de instalação: primeiro exercício2026, residente geral/MZN, activos e condições documentados, colecta integral do projecto ou quota residual documentada, cálculo/gravação/recuperação/relatórios com histórico. As notas QA anteriores ficam como histórico das provas aceites. Outros exercícios/transporte/caducidade, repartição automática/múltiplos projectos, EP/L7/cumulações/outros benefícios/formulários e restantes resíduos L1–L7/L8/L9–L16 permanecem no plano existente. Próximo trabalho: residual executável de CBF15/L8, preservando o recorte instalado e sem repetir as provas aceites. Conferência visual continua pendente no mesmo registo; portal não declarado integral.
|
||||||
|
|
||||||
## 10/11/2026 — acordo PT–MZ instalado; crédito CBF15 demonstrado em QA
|
## 10/11/2026 — acordo PT–MZ instalado; crédito CBF15 demonstrado em QA
|
||||||
|
|
||||||
**Homologação: `7c8d4726e9f93325480e2ee6e5e790e9adbcaf19`**, promovida às12:36:07UTC e conferida às12:37:11UTC (14:37 locais). Revisão HTTPS/10hashes iguais, quatro entradasHTTP200; configuração preservada, zero migrations/importações. Backup/restauro190tabelas e369ficheiros/configuração conferidos, b10b35f3 preservada. Recibos `outputs/cdt-agreement-delivery-20261110/`. Falha inicial de recepção Git delimitada ao directório objects/7c pertencente aroot: apenas proprietário/grupo ajustados a conta do serviço,modo0755 inalterado; pedido original validado recolocado sem alterar corpo/SHA ou controlos. Recibos git-permission-repair/request-retry; não foi falha do portal nem alteração de segurança do navegador. Sessão SSH preservada para continuação autenticada, sem credencial em disco.
|
**Homologação: `7c8d4726e9f93325480e2ee6e5e790e9adbcaf19`**, promovida às12:36:07UTC e conferida às12:37:11UTC (14:37 locais). Revisão HTTPS/10hashes iguais, quatro entradasHTTP200; configuração preservada, zero migrations/importações. Backup/restauro190tabelas e369ficheiros/configuração conferidos, b10b35f3 preservada. Recibos `outputs/cdt-agreement-delivery-20261110/`. Falha inicial de recepção Git delimitada ao directório objects/7c pertencente aroot: apenas proprietário/grupo ajustados a conta do serviço,modo0755 inalterado; pedido original validado recolocado sem alterar corpo/SHA ou controlos. Recibos git-permission-repair/request-retry; não foi falha do portal nem alteração de segurança do navegador. Sessão SSH preservada para continuação autenticada, sem credencial em disco.
|
||||||
|
|||||||
@@ -27,10 +27,11 @@ export function SimulationIrpcInvestment({
|
|||||||
<p>
|
<p>
|
||||||
Prepare o detalhe do investimento efectivo e da qualificação
|
Prepare o detalhe do investimento efectivo e da qualificação
|
||||||
do projecto. Substitui neste cálculo o resumo anterior dos
|
do projecto. Substitui neste cálculo o resumo anterior dos
|
||||||
benefícios, que fica conservado. O primeiro recorte cobre o
|
benefícios, que fica conservado. O recorte cobre
|
||||||
exercício inicial2026,com colecta integral ou quota residual
|
investimento efectivo de2026 dentro dos cinco exercícios do
|
||||||
documental do projecto, sem outros benefícios próprios ou
|
prazo,com colecta integral ou quota residual documental do
|
||||||
transportes anteriores.
|
projecto, sem outros benefícios próprios ou transportes
|
||||||
|
anteriores.
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -275,7 +276,7 @@ export function SimulationIrpcInvestment({
|
|||||||
{text('startDate', 'Data do início aplicável — AAAA-MM-DD')}
|
{text('startDate', 'Data do início aplicável — AAAA-MM-DD')}
|
||||||
{text(
|
{text(
|
||||||
'startReference',
|
'startReference',
|
||||||
'Suporte do início e primeiro exercício2026',
|
'Suporte do início e contagem dos cinco exercícios',
|
||||||
)}
|
)}
|
||||||
<p>
|
<p>
|
||||||
Indique investimento efectivamente realizado,
|
Indique investimento efectivamente realizado,
|
||||||
|
|||||||
@@ -2147,6 +2147,19 @@ export function annualReportRows(
|
|||||||
m.investmentCredit.version +
|
m.investmentCredit.version +
|
||||||
'; ' +
|
'; ' +
|
||||||
m.investmentCredit.source +
|
m.investmentCredit.source +
|
||||||
|
(m.investmentCredit.window
|
||||||
|
? '; JanelaCBF15/1: exercícios ' +
|
||||||
|
m.investmentCredit.window.firstYear +
|
||||||
|
'–' +
|
||||||
|
m.investmentCredit.window.lastYear +
|
||||||
|
'; exercício ' +
|
||||||
|
m.investmentCredit.window.exercise +
|
||||||
|
'/5; ' +
|
||||||
|
(m.investmentCredit.window.status ===
|
||||||
|
'expired'
|
||||||
|
? 'prazo expirado'
|
||||||
|
: 'dentro do prazo')
|
||||||
|
: '') +
|
||||||
'; projecto ' +
|
'; projecto ' +
|
||||||
m.investmentCredit.input.projectReference +
|
m.investmentCredit.input.projectReference +
|
||||||
'; ' +
|
'; ' +
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ import {
|
|||||||
fraction,
|
fraction,
|
||||||
type FiscalFraction,
|
type FiscalFraction,
|
||||||
} from './fiscal-rational.ts';
|
} from './fiscal-rational.ts';
|
||||||
export const investmentCreditVersion = 'IRPC-CBF15/1';
|
export const investmentCreditVersion = 'IRPC-CBF15/2';
|
||||||
export const investmentCreditSource =
|
export const investmentCreditSource =
|
||||||
'CBF,Lei4/2009,arts3/4/9/10/12/13/15; compilação oficial APIEX2025,PDF66–72,art15PDF71–72. Primeiro exercício2026,regime normal residente,ano civil/MZN,colecta inteiramente do projecto documentada. Activo corpóreo novo afecto à exploração nacional,sem exclusões15(6). Cidade de Maputo5%,restantes províncias10%. Menor entre crédito disponível e colecta remanescente depois dos créditos económicos/internacionais,antesPEC/RF/PPC (RIRPC21). Sem transporte/reembolso ou consumo de saldo oficial presumidos;outros exercícios,regimes e cumulações separados.';
|
'CBF,Lei4/2009,arts3/4/9/10/12/13/15; compilação oficial APIEX2025,PDF66–72,art15PDF71–72. Liquidação2026,regime normal residente,ano civil/MZN,colecta integral ou quota residual documentada do projecto. Janela de cinco exercícios desde o início aplicável15(4/7),incluindo o primeiro; activos efectivos de2026,sem transporte de activos/créditos anteriores. Activo corpóreo novo afecto à exploração nacional,sem exclusões15(6). Cidade de Maputo5%,restantes províncias10%. Menor entre crédito disponível e colecta remanescente depois dos créditos económicos/internacionais,antesPEC/RF/PPC (RIRPC21). Sem transporte/reembolso ou consumo de saldo oficial presumidos;outros exercícios,regimes e cumulações separados.';
|
||||||
type Choice = '' | 'yes' | 'no';
|
type Choice = '' | 'yes' | 'no';
|
||||||
export type InvestmentAsset = {
|
export type InvestmentAsset = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -197,7 +197,7 @@ const zero = () => fraction(0n);
|
|||||||
const minimum = (a: FiscalFraction, b: FiscalFraction) =>
|
const minimum = (a: FiscalFraction, b: FiscalFraction) =>
|
||||||
creditCompare(a, b) < 0n ? a : b;
|
creditCompare(a, b) < 0n ? a : b;
|
||||||
const iso = (s: string) =>
|
const iso = (s: string) =>
|
||||||
/^2026-\d{2}-\d{2}$/.test(s) &&
|
/^\d{4}-\d{2}-\d{2}$/.test(s) &&
|
||||||
Number.isFinite(Date.parse(s)) &&
|
Number.isFinite(Date.parse(s)) &&
|
||||||
new Date(s).toISOString().slice(0, 10) === s;
|
new Date(s).toISOString().slice(0, 10) === s;
|
||||||
export const investmentAssetIdentity = (r: InvestmentAsset) =>
|
export const investmentAssetIdentity = (r: InvestmentAsset) =>
|
||||||
@@ -222,6 +222,15 @@ export function calculateInvestmentCredit(
|
|||||||
available: FiscalFraction | null = null,
|
available: FiscalFraction | null = null,
|
||||||
rate: number | null = null;
|
rate: number | null = null;
|
||||||
let projectCeiling: FiscalFraction | undefined;
|
let projectCeiling: FiscalFraction | undefined;
|
||||||
|
let window:
|
||||||
|
| {
|
||||||
|
firstYear: number;
|
||||||
|
lastYear: number;
|
||||||
|
currentYear: number;
|
||||||
|
exercise: number;
|
||||||
|
status: 'active' | 'expired';
|
||||||
|
}
|
||||||
|
| undefined;
|
||||||
const rows: {
|
const rows: {
|
||||||
input: InvestmentAsset;
|
input: InvestmentAsset;
|
||||||
value: FiscalFraction | null;
|
value: FiscalFraction | null;
|
||||||
@@ -284,8 +293,20 @@ export function calculateInvestmentCredit(
|
|||||||
!c.startReference.trim()
|
!c.startReference.trim()
|
||||||
)
|
)
|
||||||
throw Error(
|
throw Error(
|
||||||
'Local efectivo do investimento e primeiro exercício2026 por documentar: início de exploração para projecto novo,procedimentos após aprovação para projecto em funcionamento.',
|
'Local efectivo do investimento e início aplicável por documentar: início de exploração para projecto novo,procedimentos após aprovação para projecto em funcionamento.',
|
||||||
);
|
);
|
||||||
|
const firstYear = Number(c.startDate.slice(0, 4));
|
||||||
|
if (firstYear < 1 || firstYear > Number(b.year))
|
||||||
|
throw Error(
|
||||||
|
'Ano inicial inválido ou posterior à liquidação2026; não antecipar o crédito.',
|
||||||
|
);
|
||||||
|
window = {
|
||||||
|
firstYear,
|
||||||
|
lastYear: firstYear + 4,
|
||||||
|
currentYear: Number(b.year),
|
||||||
|
exercise: Number(b.year) - firstYear + 1,
|
||||||
|
status: Number(b.year) > firstYear + 4 ? 'expired' : 'active',
|
||||||
|
};
|
||||||
if (!c.assets.length)
|
if (!c.assets.length)
|
||||||
throw Error(
|
throw Error(
|
||||||
'Investimento conhecido exige activo/documento; zero não se presume.',
|
'Investimento conhecido exige activo/documento; zero não se presume.',
|
||||||
@@ -321,6 +342,7 @@ export function calculateInvestmentCredit(
|
|||||||
!r.issuer.trim() ||
|
!r.issuer.trim() ||
|
||||||
!r.document.trim() ||
|
!r.document.trim() ||
|
||||||
!iso(r.date) ||
|
!iso(r.date) ||
|
||||||
|
!r.date.startsWith(b.year + '-') ||
|
||||||
!r.reference.trim() ||
|
!r.reference.trim() ||
|
||||||
r.newTangible !== 'yes' ||
|
r.newTangible !== 'yes' ||
|
||||||
r.domesticUse !== 'yes' ||
|
r.domesticUse !== 'yes' ||
|
||||||
@@ -362,10 +384,12 @@ export function calculateInvestmentCredit(
|
|||||||
}
|
}
|
||||||
rows.push({
|
rows.push({
|
||||||
input: structuredClone(r),
|
input: structuredClone(r),
|
||||||
value,
|
value: value && window?.status === 'expired' ? zero() : value,
|
||||||
reason: issues.length
|
reason: issues.length
|
||||||
? issues.join(' ')
|
? issues.join(' ')
|
||||||
: 'Activo elegível documentado.',
|
: window?.status === 'expired'
|
||||||
|
? 'Prazo15(4) terminado no quinto exercício; investimento2026 conservado,sem crédito.'
|
||||||
|
: 'Activo elegível documentado.',
|
||||||
pending: issues,
|
pending: issues,
|
||||||
});
|
});
|
||||||
pending.push(...issues);
|
pending.push(...issues);
|
||||||
@@ -437,6 +461,7 @@ export function calculateInvestmentCredit(
|
|||||||
investment,
|
investment,
|
||||||
...(projectCeiling === undefined ? {} : { projectCeiling }),
|
...(projectCeiling === undefined ? {} : { projectCeiling }),
|
||||||
rate,
|
rate,
|
||||||
|
...(window ? { window } : {}),
|
||||||
nominal,
|
nominal,
|
||||||
priorUse,
|
priorUse,
|
||||||
available: pending.length ? null : available,
|
available: pending.length ? null : available,
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
<?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 IrpcInvestmentWindowDraftTest extends TestCase
|
||||||
|
{
|
||||||
|
use DatabaseTransactions;
|
||||||
|
|
||||||
|
public function test_derived_national_fraction_round_trips_through_the_existing_private_draft_writer(): 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 (['fifth', 'expired'] as $name) {
|
||||||
|
$snapshot = json_decode(file_get_contents(base_path('outputs/irpc-cbf15-window-20261110/'.$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']);
|
||||||
|
$this->assertSame($snapshot['business_plan_snapshot'], $back['business_plan_snapshot']);
|
||||||
|
$book = json_decode($back['modelo_a_snapshot'], true, flags: JSON_THROW_ON_ERROR)['irpcAnnual'][$snapshot['context_opportunity']];
|
||||||
|
$this->assertSame(2, $book['version']);
|
||||||
|
$this->assertCount(array_search($name, ['fifth', 'expired'], true) + 5, $book['history']);
|
||||||
|
$model = $book['result']['model'];
|
||||||
|
$this->assertSame($name === 'fifth' ? 'active' : 'expired', $model['investmentCredit']['window']['status']);
|
||||||
|
$this->assertSame('IRPC-CBF15/2', $model['investmentCredit']['version']);
|
||||||
|
$this->assertSame(['numerator' => $name === 'fifth' ? '200' : '0', 'denominator' => '1'], $model['investmentCredit']['deducted']);
|
||||||
|
$this->assertSame(['numerator' => $name === 'fifth' ? '500' : '700', 'denominator' => '1'], $model['arithmeticBalance']);
|
||||||
|
file_put_contents(base_path('outputs/irpc-cbf15-window-20261110/recovered-'.$name.'.json'), json_encode($back, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -84,7 +84,7 @@ void test('CBF15 requires actual project,source and first-exercise conditions wi
|
|||||||
{ soleProject: '' },
|
{ soleProject: '' },
|
||||||
{ noOtherBenefits: 'no' },
|
{ noOtherBenefits: 'no' },
|
||||||
{ assessmentKey: 'stale' },
|
{ assessmentKey: 'stale' },
|
||||||
{ startDate: '2025-01-01' },
|
{ startDate: '2027-01-01' },
|
||||||
]) {
|
]) {
|
||||||
const c = investmentCase();
|
const c = investmentCase();
|
||||||
Object.assign(c.b.investmentCredit, patch);
|
Object.assign(c.b.investmentCredit, patch);
|
||||||
@@ -188,7 +188,7 @@ void test('CBF15 calculation-save-recovery-HTML and dossier reports retain sourc
|
|||||||
numerator: '200',
|
numerator: '200',
|
||||||
denominator: '1',
|
denominator: '1',
|
||||||
});
|
});
|
||||||
assert.match(annualHtml(d), /IRPC-CBF15\/1/);
|
assert.match(annualHtml(d), /IRPC-CBF15\/2/);
|
||||||
assert.match(
|
assert.match(
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
createProjectDossierReport(d, {
|
createProjectDossierReport(d, {
|
||||||
@@ -196,7 +196,7 @@ void test('CBF15 calculation-save-recovery-HTML and dossier reports retain sourc
|
|||||||
generatedAt: '2026-11-10T19:00:00+02:00',
|
generatedAt: '2026-11-10T19:00:00+02:00',
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
/IRPC-CBF15\/1/,
|
/IRPC-CBF15\/2/,
|
||||||
);
|
);
|
||||||
const old = internationalCase();
|
const old = internationalCase();
|
||||||
assert.equal(calculateAnnual(old.d, old.b).investmentCredit, undefined);
|
assert.equal(calculateAnnual(old.d, old.b).investmentCredit, undefined);
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
import test from 'node:test';
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import { investmentCase } from './helpers/irpc-investment-case.mjs';
|
||||||
|
import {
|
||||||
|
calculateAnnual,
|
||||||
|
recordAnnual,
|
||||||
|
saveAnnualBook,
|
||||||
|
annualBook,
|
||||||
|
annualCurrent,
|
||||||
|
annualHtml,
|
||||||
|
} from '../../resources/js/lib/irpc-annual-settlement.ts';
|
||||||
|
import { createProjectDossierReport } from '../../resources/js/lib/project-dossier-report.ts';
|
||||||
|
void test('CBF15 includes first through fifth civil fiscal exercises, including a late-year start', () => {
|
||||||
|
for (const [start, exercise] of [
|
||||||
|
['2026-06-01', 1],
|
||||||
|
['2025-12-31', 2],
|
||||||
|
['2024-12-31', 3],
|
||||||
|
['2023-12-31', 4],
|
||||||
|
['2022-12-31', 5],
|
||||||
|
]) {
|
||||||
|
const c = investmentCase();
|
||||||
|
c.b.investmentCredit.startDate = start;
|
||||||
|
const m = calculateAnnual(c.d, c.b);
|
||||||
|
assert.deepEqual(m.pending, []);
|
||||||
|
assert.equal(m.investmentCredit.window.exercise, exercise);
|
||||||
|
assert.equal(m.investmentCredit.window.status, 'active');
|
||||||
|
assert.equal(
|
||||||
|
m.investmentCredit.window.lastYear,
|
||||||
|
Number(start.slice(0, 4)) + 4,
|
||||||
|
);
|
||||||
|
assert.deepEqual(m.investmentCredit.deducted, {
|
||||||
|
numerator: '500',
|
||||||
|
denominator: '1',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
void test('CBF15 sixth exercise expires without erasing actual investment or creating a carry/refund', () => {
|
||||||
|
const c = investmentCase();
|
||||||
|
c.b.investmentCredit.startDate = '2021-12-31';
|
||||||
|
const m = calculateAnnual(c.d, c.b);
|
||||||
|
assert.deepEqual(m.pending, []);
|
||||||
|
assert.equal(m.investmentCredit.window.exercise, 6);
|
||||||
|
assert.equal(m.investmentCredit.window.status, 'expired');
|
||||||
|
assert.deepEqual(m.investmentCredit.deducted, {
|
||||||
|
numerator: '0',
|
||||||
|
denominator: '1',
|
||||||
|
});
|
||||||
|
assert.deepEqual(m.arithmeticBalance, {
|
||||||
|
numerator: '700',
|
||||||
|
denominator: '1',
|
||||||
|
});
|
||||||
|
assert.equal(m.investmentCredit.rows[0].input.amount, '10000');
|
||||||
|
assert.match(m.investmentCredit.rows[0].reason, /quinto/);
|
||||||
|
assert.equal(m.investmentCredit.carryCreated, false);
|
||||||
|
});
|
||||||
|
void test('CBF15 does not admit future/invalid start dates or import older assets as current-year investments', () => {
|
||||||
|
for (const start of ['2027-01-01', '2025-02-29', '0000-01-01', '']) {
|
||||||
|
const c = investmentCase();
|
||||||
|
c.b.investmentCredit.startDate = start;
|
||||||
|
assert.equal(calculateAnnual(c.d, c.b).arithmeticBalance, null);
|
||||||
|
}
|
||||||
|
const c = investmentCase();
|
||||||
|
c.b.investmentCredit.startDate = '2022-01-01';
|
||||||
|
c.b.investmentCredit.assets[0].date = '2025-12-31';
|
||||||
|
assert.equal(calculateAnnual(c.d, c.b).arithmeticBalance, null);
|
||||||
|
});
|
||||||
|
void test('CBF15 existing project uses procedural start; new project can invest before operations within the same window', () => {
|
||||||
|
const c = investmentCase();
|
||||||
|
c.b.investmentCredit.startKind = 'existing';
|
||||||
|
c.b.investmentCredit.startDate = '2022-12-31';
|
||||||
|
assert.deepEqual(calculateAnnual(c.d, c.b).pending, []);
|
||||||
|
c.b.investmentCredit.startDate = '2026-06-01';
|
||||||
|
assert.equal(calculateAnnual(c.d, c.b).arithmeticBalance, null);
|
||||||
|
c.b.investmentCredit.startKind = 'new';
|
||||||
|
assert.deepEqual(calculateAnnual(c.d, c.b).pending, []);
|
||||||
|
});
|
||||||
|
void test('CBF15 fifth-year then expiry recovers and reports window while preserving earlier result', () => {
|
||||||
|
const c = investmentCase();
|
||||||
|
c.b.investmentCredit.startDate = '2022-12-31';
|
||||||
|
let b = recordAnnual(c.d, c.b, '2026-11-10T14:00:00Z');
|
||||||
|
b.investmentCredit.startDate = '2021-12-31';
|
||||||
|
b = recordAnnual(c.d, b, '2026-11-10T14:01:00Z');
|
||||||
|
const d = { ...c.d, modelo_a_snapshot: saveAnnualBook(c.d, b) },
|
||||||
|
back = annualBook(d);
|
||||||
|
assert.equal(annualCurrent(d, back), true);
|
||||||
|
assert.equal(back.history.length, 1);
|
||||||
|
assert.deepEqual(back.history[0].model.investmentCredit.deducted, {
|
||||||
|
numerator: '500',
|
||||||
|
denominator: '1',
|
||||||
|
});
|
||||||
|
assert.deepEqual(
|
||||||
|
calculateAnnual(d, back).investmentCredit,
|
||||||
|
back.result.model.investmentCredit,
|
||||||
|
);
|
||||||
|
assert.match(annualHtml(d), /JanelaCBF15\/1/);
|
||||||
|
assert.match(annualHtml(d), /prazo expirado/);
|
||||||
|
assert.match(
|
||||||
|
JSON.stringify(
|
||||||
|
createProjectDossierReport(d, {
|
||||||
|
selectedSections: new Set(['finance']),
|
||||||
|
generatedAt: '2026-11-10T14:01:00Z',
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
/JanelaCBF15/,
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -18,7 +18,7 @@ $identity = $pdo->query('select current_database() as name,inet_server_port() as
|
|||||||
if ($identity['name'] !== 'proactive_investment_20261027_test' || (int) $identity['port'] !== 55441) {
|
if ($identity['name'] !== 'proactive_investment_20261027_test' || (int) $identity['port'] !== 55441) {
|
||||||
throw new RuntimeException('Isolated QA identity required.');
|
throw new RuntimeException('Isolated QA identity required.');
|
||||||
}
|
}
|
||||||
$case = ['quota' => 'IrpcEconomicQuotaDraftTest', 'inclusion' => 'IrpcEconomicInclusionDraftTest', 'liquidation' => 'IrpcEconomicLiquidationDraftTest', 'association' => 'IrpcEconomicAssociationDraftTest', 'special-inclusion' => 'IrpcEconomicSpecialInclusionDraftTest', 'pt-royalty' => 'IrpcPtMzCreditDraftTest', 'pt-capital' => 'IrpcPtMzCapitalDraftTest', 'pt-government' => 'IrpcPtMzGovernmentDraftTest', 'economic-fraction' => 'IrpcEconomicFractionDraftTest', 'pt-agreement' => 'IrpcPtMzAgreementDraftTest', 'investment' => 'IrpcInvestmentCreditDraftTest', 'investment-quota' => 'IrpcInvestmentQuotaDraftTest'][$argv[2] ?? ''] ?? throw new RuntimeException('Choose an explicit supported QA case.');
|
$case = ['quota' => 'IrpcEconomicQuotaDraftTest', 'inclusion' => 'IrpcEconomicInclusionDraftTest', 'liquidation' => 'IrpcEconomicLiquidationDraftTest', 'association' => 'IrpcEconomicAssociationDraftTest', 'special-inclusion' => 'IrpcEconomicSpecialInclusionDraftTest', 'pt-royalty' => 'IrpcPtMzCreditDraftTest', 'pt-capital' => 'IrpcPtMzCapitalDraftTest', 'pt-government' => 'IrpcPtMzGovernmentDraftTest', 'economic-fraction' => 'IrpcEconomicFractionDraftTest', 'pt-agreement' => 'IrpcPtMzAgreementDraftTest', 'investment' => 'IrpcInvestmentCreditDraftTest', 'investment-quota' => 'IrpcInvestmentQuotaDraftTest', 'investment-window' => 'IrpcInvestmentWindowDraftTest'][$argv[2] ?? ''] ?? throw new RuntimeException('Choose an explicit supported QA case.');
|
||||||
$argv = ['phpunit', '-c', 'phpunit.investmoz.xml', '--do-not-cache-result', 'tests/Feature/'.$case.'.php'];
|
$argv = ['phpunit', '-c', 'phpunit.investmoz.xml', '--do-not-cache-result', 'tests/Feature/'.$case.'.php'];
|
||||||
$argc = count($argv);
|
$argc = count($argv);
|
||||||
$_SERVER['argv'] = $argv;
|
$_SERVER['argv'] = $argv;
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import fs from 'node:fs';
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import { investmentCase } from '../tests/frontend/helpers/irpc-investment-case.mjs';
|
||||||
|
import { recordAnnual,saveAnnualBook,annualBook,annualCurrent,calculateAnnual,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-cbf15-window-20261110/',import.meta.url);fs.mkdirSync(out,{recursive:true});
|
||||||
|
const original=JSON.parse(fs.readFileSync(new URL('../outputs/irpc-cbf15-20261110/recovered-partial.json',import.meta.url),'utf8'));let current=annualBook(original);
|
||||||
|
for(const name of ['fifth','expired']){
|
||||||
|
if(process.argv[2]==='recovered'){
|
||||||
|
const d=JSON.parse(fs.readFileSync(new URL('recovered-'+name+'.json',out),'utf8')),b=annualBook(d),m=b.result.model;
|
||||||
|
assert.equal(annualCurrent(d,b),true);assert.deepEqual(calculateAnnual(d,b).investmentCredit,m.investmentCredit);assert.equal(b.history.length,name==='fifth'?5:6);assert.equal(b.history[0].model.investmentCredit?.version??'legacy','legacy');
|
||||||
|
const html=annualHtml(d),report=createProjectDossierReport(d,{selectedSections:new Set(['finance']),generatedAt:new Date().toISOString()});assert.match(html,/JanelaCBF15/);assert.match(JSON.stringify(report),/JanelaCBF15/);
|
||||||
|
fs.writeFileSync(new URL(name+'-report.html',out),html);fs.writeFileSync(new URL(name+'-verification.json',out),JSON.stringify({synthetic:true,apiRecovered:true,currentRecalculation:true,html:true,dossierReport:true,history:b.history.length,window:m.investmentCredit.window,deducted:m.investmentCredit.deducted,balance:m.arithmeticBalance,activeChanged:false},null,2));
|
||||||
|
}else{
|
||||||
|
current=structuredClone(current);current.investmentCredit.startDate=name==='fifth'?'2022-12-31':'2021-12-31';current.investmentCredit.startReference='SYNTHETIC documented original start, civil exercises inclusive; not a new administrative decision';current=recordAnnual(original,current,new Date().toISOString());fs.writeFileSync(new URL(name+'.json',out),JSON.stringify({...original,person_type:investmentCase().d.person_type,modelo_a_snapshot:saveAnnualBook(original,current)}));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user