Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
569a1cd750 | ||
|
|
aae67c169d | ||
|
|
6ededb597a | ||
|
|
d134a92d16 | ||
|
|
cf4e0638c1 | ||
|
|
838ad941a2 | ||
|
|
814fe0a9b7 | ||
|
|
e182d1d79d | ||
|
|
9a093046e5 | ||
|
|
4ee427bad4 | ||
|
|
669cb8979b | ||
|
|
7fdb6b424b | ||
|
|
dc14f01e14 | ||
|
|
1ee0c6d21d | ||
|
|
9fda6d6e12 |
@@ -71,7 +71,6 @@ class PortalServicesController extends Controller
|
||||
$props = ['section' => $page, 'filters' => $filters, 'countries' => app(CountryCatalogue::class)->all()];
|
||||
if ($page === 'sectors') {
|
||||
$props['sectors'] = \App\Services\SectorOrganization::catalog();
|
||||
$props['sectorDetails'] = $props['sectors']->map(fn (Sector $sector) => app(\App\Services\SectorInvestment::class)->page($sector))->values();
|
||||
}
|
||||
if (in_array($page, ['documents', 'legislation'], true)) {
|
||||
$query = $this->resources();
|
||||
|
||||
@@ -1403,3 +1403,208 @@ O relatório comum tinha um conflito de imports. Foram conservados ambos e as ch
|
||||
Retomar na sessão local com SSH autenticado, usando a autorização já registada: medir espaço/inodes na partição efectiva, comparar com compilação e backup, conferir main e revisão activa, criar e ensaiar backup fresco, instalar L8 e conferir apenas o seu delta. Depois repetir as guardas necessárias para a candidata conjunta e conferir o delta proactivo preservando L8. Manter a versão activa até a nova estar pronta. Excluir fixtures QA dos inputs de instalação; não executar seeders/importações de amostras, não publicar pilotos privados nem usar o pacote QA como cópia da base activa. SGF TEMPO separado.
|
||||
|
||||
Recibo de preparação: `docs/investmoz/unification/sequential-delivery-preparation-20261027.json`. Provas e instrução de passagem: `outputs/sequential-delivery-20261027/`. Os recibos de execução e as duas revisões servidas serão registados apenas depois de execução real na sessão autenticada; este recibo não os substitui.
|
||||
|
||||
|
||||
## 27/10/2026 — correcção delimitada da navegação dos sectores
|
||||
|
||||
Reprodução local autorizada da intenção de `88b39b527859bd420f2e223a7afea89bea38de54`, indisponível no origin/outro ambiente. Base exacta `9fda6d6e12832a3cf25e7314f1f895dd430238c5`; novo ramo `codex/sector-navigation-fix-local-20261027`. Não se afirma identidade binária com o pacote externo. QA concluída; instalação ainda pendente nesta revisão documental.
|
||||
|
||||
**Causa e alteração:** o índice `/sectores` carregava `sectorDetails` para todos os sectores e apresentava as páginas completas abaixo dos cartões; os cartões passavam a âncoras locais. Removidos esse carregamento e a apresentação agregada. Os cartões usam `sector.url`, preservando as páginas individuais, a especialização Fintech e os caminhos históricos existentes. O índice mantém descrições curtas, contagens e acesso aos sectores, sem repetir guias e oportunidades de todos eles.
|
||||
|
||||
**Preservação:** página individual, `SectorContent`, `SectorEntry`, `SectorInvestment`, `SectorOrganization`, filtros, dados mestres, originais e continuidade para o mesmo dossier não foram alterados. PEC `7fdb6b42` continua no seu ramo QA; não é integrado nesta correcção. L8 instalado e percurso proactivo de `9fda6d6e` preservados. Songo/Gurué privados; sem fixtures QA no activo, publicação, importações, migrations ou alterações de permissões.
|
||||
|
||||
**Delta demonstrado:** actualizados os dois testes afectados de `SectorCatalogueContentTest` e acrescentado um caso de isolamento/regresso/recarregamento:3 testes/319 asserções, base isolada `proactive_investment_20261027_test`, loopback55441, transacções com rollback; sem `migrate:fresh`. Wayfinder `--with-form`, TypeScript e build QA aprovados. Navegador real:10 cartões, acesso separado a Agronegócio e Turismo, oportunidades próprias, filtro Agronegócio mantido no catálogo, recarregamento da página individual e regresso ao índice sem repetição. Entrada Turismo para o dossier conserva o contexto sectorial na ligação e encaminha o visitante para a autenticação existente; não se repetiu a prova completa de gravação do dossier já aceite. A amostra QA não tem guias editoriais carregados; a preservação dos guias assenta na comparação dos consumidores/fontes, e será conferida no destino sem republicar conteúdo. A captura de imagem do browser devolveu «0 width»; árvore de acessibilidade e testes guardados, sem alegação de captura visual inexistente.
|
||||
|
||||
Requisitos existentes actualizados apenas neste delta: `MASTER-471012ab-0007` e `MASTER-9dee08d0-0017`; não se encerra automaticamente o âmbito mais amplo dessas linhas. Recibo QA: `docs/investmoz/unification/sector-navigation-qa-20261027.json`. Provas locais: `outputs/sector-navigation-fix-20261027/`. Entrega autorizada seguirá o processo Git existente, com capacidade, backup/restauro fresco, comparação de fonte/dados/schema/configuração/permissões e revisão efectivamente servida.
|
||||
|
||||
|
||||
## 27/10/2026 — L8 e percurso proactivo instalados pela sequência autorizada
|
||||
|
||||
Entrega1: **L8 `c85e772d`**, anterior `f8d57bdd`. Entrega2, apenas após conferir a primeira: **percurso proactivo `9fda6d6e`**, incluindo `56c0c6a7` e preservando L8. As duas revisões foram conferidas no registo do instalador e no recurso público `/build/revision.json`, com hash do manifest e marcadores compilados dos deltas. `c85e772d` conserva o código aceite de `14360748`; o merge proactivo mantém ambos os fornecedores de linhas do relatório. A referência final servida passa a `9fda6d6e`.
|
||||
|
||||
A tentativa proactiva em `56c0c6a7` não foi activada: um teste dependia de um ficheiro de outputs existente apenas no QA local. L8 permaneceu em serviço. A correcção `9fda6d6e` torna esse teste portátil com um exemplar mínimo sintético da projecção pública da API, sem perfis, credenciais ou pilotos reais e sem escrita em outputs. O teste dirigido passou; o código da aplicação é byte a byte idêntico a `56c0c6a7`. Não se transportou a base QA nem se saltou a guarda do instalador. A nova tentativa usou o backup/restauro ainda fresco, com capacidade e fonte activa novamente conferidas.
|
||||
|
||||
A ligação SSH foi restabelecida pelo responsável na janela local, sem credenciais na conversa ou nos recibos. A medição inicial encontrou91,6GiB livres e inodes suficientes; a capacidade foi novamente conferida depois de cada backup. Instalador existente e suas guardas preservados; a versão anterior permaneceu activa até a nova release estar pronta. Os controlos automáticos do instalador foram mantidos, sem nova ronda manual das provas funcionais/económicas aceites.
|
||||
|
||||
**Backup/restauro fresco em cada etapa:** `/var/backups/investmoz-sequential-20261027/l8/backup` e `/var/backups/investmoz-sequential-20261027/proactive/backup`. Em cada uma, dump consistente e369ficheiros persistentes/configuração verificados; restauro das174tabelas num contentor sem rede, porta55441, com contagens/checksums iguais. Recibos `backup-receipt.json`, `restore-receipt.json`, `schema-permissions-before.json`, `schema-permissions-after.json`, `preservation-receipt.json` e `operational-delta-receipt.json` conservados nesses directórios. Hashes exactos no recibo consolidado.
|
||||
|
||||
**Preservação demonstrada:**172tabelas de negócio intactas em cada comparação; apenas cache/sessões operacionais reconciliados individualmente por identidade/timeouts e pedidos públicos de entrega. Schema, papéis, grants, permissões e originais persistentes conservados. Fontes instaladas comparadas aos commits:996ficheiros após L8 e1000após o proactivo. Sem migrations na base activa, importação de amostras QA, seeders ou publicação dos pilotos. O instalador manteve base e storage sintéticos isolados; o storage QA fica fora da árvore servida, e a release usa os mesmos `.env` e storage persistentes por ligação.
|
||||
|
||||
**Âmbito:** L8 continua parcial, com PEC e créditos de regra própria separados; os resultados continuam simulações. Songo permanece privado e pendente de respostas externas; Gurué continua hipótese agrícola privada. Disponibilização técnica não confirma clientes, procura, sustentabilidade ou publicação comercial. L3, ISPC, câmbio, L6/L7 e SGF TEMPO preservados.
|
||||
|
||||
Recibo consolidado: `docs/investmoz/unification/sequential-delivery-installed-20261027.json`. Provas locais: `outputs/l8-proactive-install-20261027/`. Os recibos históricos de preparação/suspensão mantêm-se como histórico e deixam de representar pendência de instalação destas duas candidatas.
|
||||
|
||||
Recibo remoto consolidado: `/var/backups/investmoz-sequential-20261027/installed-receipt.json`;SHA256 `62584b5abb50a05303a24379a0563e8dc147e25e682dc3566bb8edf015ba9d0b`. A fixture sintética portátil é apenas input de teste, sem importação na aplicação e sem dados dos pilotos.
|
||||
|
||||
|
||||
## 27/10/2026 — utilização real do percurso proactivo: exercício preparado, execução por observar
|
||||
|
||||
Reutilizadas a entrega `9fda6d6e` e as provas técnicas aceites. Roteiro no manual existente, secção «avaliação de utilização pela equipa, sem Codex»: análise privada de trabalho → evidências com proveniência → ideia com hipótese/pendências → guardar/recuperar → mesmo dossier → revisão sem duplicação. A equipa utiliza a própria conta autorizada e devolve referências internas, passo, dificuldade concreta e apoio consultado. Estado desta avaliação: **pendente de execução real**, sem resultados de participantes inventados e sem nova aprovação obrigatória da entrega.
|
||||
|
||||
As quatro linhas proactivas existentes (RF-CLU-003, MASTER-471012ab-0010, MASTER-9dee08d0-0067 e MASTER-471012ab-0067) conservam as provas e limitações; esta nota não promove os requisitos globais a concluídos. Não houve testes técnicos, nova instalação, alteração no portal ou contacto externo. Songo/Gurué privados; L8 parcial, PEC/créditos próprios separados. Registo: `docs/investmoz/unification/proactive-team-use-evaluation-20261027.json`. Retoma: tratar apenas os obstáculos observados pela equipa.
|
||||
|
||||
|
||||
## 27/10/2026 — L8: PEC pago no próprio exercício, incremento em QA
|
||||
|
||||
**Estado:** implementado e demonstrado em QA, ainda sem instalação. O portal mantém a entrega `9fda6d6e`, segundo o recibo anterior; esta passagem não acedeu ao servidor nem alterou `main`. A avaliação real da equipa sobre o percurso proactivo continua pendente de retorno; Songo e Gurué permanecem privados.
|
||||
|
||||
**Recorte:** residente no regime geral, ano civil 2026, MZN; colecta corrente válida L6/L7, créditos anteriores ausentes ou conhecidos zero, sem imposto autónomo positivo. PEC é pagamento efectivamente comprovado do próprio sujeito/exercício, não estimativa de tesouraria. Reutilizados o livro anual, o vínculo à colecta, os PPC, as retenções L5 e o mecanismo de gravação/recuperação/relatório existentes.
|
||||
|
||||
**Regra implementada:** CIRPC, art. 66(1–2), exemplar oficial Lei34/2007, PDF18/p768-(72), conferido visualmente. A dedução PEC é a menor entre as parcelas disponíveis comprovadas e a colecta disponível antes de retenções/PPC. A colecta após PEC não fica negativa. O excedente PEC é apresentado como parcela seleccionada não deduzida: **não constitui reembolso nem transporte oficial criado pelo simulador**. Retenções/PPC ainda podem originar saldo simulado a recuperar. Créditos precedentes positivos ou desconhecidos mantêm o caso indeterminado, sem ordem ou limite presumidos.
|
||||
|
||||
O detalhe PEC é uma extensão opcional versionada do snapshot anual. A acção explícita «Preparar detalhe PEC» reutiliza estado/suporte anteriores, conservando o resumo e os resultados históricos. Cada prova identifica emitente/documento, pagamento efectivo, NUIT, exercício/data, moeda, montante comprovado, utilizações anteriores documentadas e parcela seleccionada. Zero utilizado exige valor e suporte; vazio não vira zero. Ausência declarada, pagamento conhecido e informação em falta permanecem distintos. Contradições não são apagadas. A identidade física do comprovativo ignora mudanças de data e confere PEC/PPC/retenções neste e noutros contextos do mesmo snapshot; parcelas mais utilização anterior não podem ultrapassar o comprovado. Não há consulta/consumo de conta fiscal oficial nem deduplicação global entre dossiers independentes.
|
||||
|
||||
| Caso sintético novo | Efeito demonstrado |
|
||||
| --- | --- |
|
||||
| Colecta1 408; PEC200; PPC500; ausência de retenções | Dedução PEC200; colecta após PEC1 208; saldo simulado a pagar708 MZN. |
|
||||
| Mesmo caso com retenção L5 efectivamente sofrida300 | Saldo simulado408 MZN; prova L5 reutilizada, sem alterar o cálculo L5. |
|
||||
| Colecta1 408; PEC2 000; sem PPC/retenções | Dedução1 408; saldo zero; PEC592 não deduzido, sem reembolso PEC. |
|
||||
| Mesmo PEC2 000 e PPC500 | Saldo simulado a recuperar500; PEC592 continua separado. |
|
||||
| Prova PEC300; já utilizado100; parcela200; sem PPC/retenções | Saldo simulado a pagar1 208; utilizações anteriores não deduzidas outra vez. |
|
||||
| Utilização anterior desconhecida, prova incompatível/duplicada ou crédito precedente positivo | Saldo indeterminado; subtotal conhecido não substitui os dados em falta. |
|
||||
|
||||
**Provas do delta:**12 testes novos PEC;19 testes do módulo anual directamente afectado preservados e aprovados. API privada:1 teste/102 asserções, cinco casos, base isolada `proactive_investment_20261027_test` em loopback55441 e transacção com rollback. Os cinco snapshots recuperados geraram HTML e dossier concordantes; histórico anterior conservado. Hashes dos registos existentes em sete tabelas conservaram55 oportunidades,64 documentos,1 vínculo documental,19 rascunhos,40 utilizadores,3 organizações e409 eventos anteriores. Não houve nova conta/grant, publicação ou importação de pilotos. Wayfinder pelo procedimento existente, TypeScript e build isolado aprovados. Interface PEC implementada; nesta passagem não se declara nova prova manual de utilização por browser/equipa. Não se repetiram suites independentes L3/ISPC/câmbio/L5–L7.30 PDFs do arquivo comparados byte a byte ao baseline `dc14f01e`, sem alterações; schema/configuração e módulos proactivos preservados.
|
||||
|
||||
### Saldo concreto do PEC e dependências de L8
|
||||
|
||||
| Código ainda por implementar | Exemplo / condição concreta de retoma |
|
||||
| --- | --- |
|
||||
| Cálculo da obrigação PEC e prestações | Distinguir volume anterior, PPC do ano anterior e PEC pago. Exemplo de domínio a desenvolver:volume10 000 000 ×0,5%=50 000, antes dos PPC anteriores. Reutilizar vendas/pagamentos só após reconciliação de entidade/exercício; aplicar limites/calendário da edição pertinente e representar dispensas/rectificações próprias. Nenhum vencimento/valor a pagar foi gerado neste incremento. |
|
||||
| Transporte, ordem de utilização e caducidade de PEC de exercícios anteriores | Exemplo:PEC2025 ainda não deduzido no fecho2026. Implementar proveniência por ano, utilizações, remanescente e prazo; não aceitar esse saldo como PEC2026 nem criar crédito oficial a partir dos592 do exemplo. |
|
||||
| Outros créditos, imposto autónomo, períodos especiais, outras moedas e transporte/submissão M20/M22 | Mantêm os grupos L8 anteriores, com condições e regras próprias; o PEC corrente independente não espera por esses casos. O mecanismo cambial existente será reutilizado quando corresponder a regra do pagamento. |
|
||||
|
||||
| Esclarecimento documental aguardado | Exemplo / condição concreta de retoma |
|
||||
| --- | --- |
|
||||
| Edição regulamentar após o anúncio governamental de alterações IRPC em2026 | Recuperar diploma publicado, artigos alterados e entrada em vigor para os calendários/obrigações afectados. O anúncio não substitui norma nem activa parâmetros novos. Não se reabrem as fontes aceites ou cálculos independentes do art66 já demonstrados. |
|
||||
| Aplicação do prazo do art66 aos saldos anteriores e eventuais regras transitórias | Fixar para um PEC2025 as datas/último exercício elegível e documentação das utilizações; manter a regra PEC distinta do transporte de cinco exercícios do crédito por dupla tributação internacional do art65. Não presumir reembolso, expiração ou saldo oficial. |
|
||||
| Crédito próprio específico positivo | Exemplo:imposto estrangeiro10 MZN exige rendimento/base/titular, imposto efectivamente pago, limite, convenção e período próprios antes de concorrer com PEC. Segue a dependência existente do crédito, não uma pesquisa geral de L5. |
|
||||
|
||||
Registo do incremento: `docs/investmoz/simulations/irpc-l8-pec-qa-20261027.json`; provas sintéticas locais em `outputs/irpc-l8-pec-20261027/`. **L8 permanece parcial.** Não há nova estimativa global de prazo nem declaração de IRPC integralmente concluído. Dificuldades da equipa serão tratadas como correcções concretas do percurso proactivo quando forem comunicadas.
|
||||
|
||||
|
||||
## 27/10/2026 — navegação dos sectores instalada e conferida
|
||||
|
||||
**Disponível no portal em `669cb8979b0c372659fc2ad3876aebc919d5df19`**, sobre `9fda6d6e`. Reproduzida localmente a correcção solicitada, com novo identificador; `88b39b5` não foi importado e deixa de ser condição de transferência/instalação. O índice mostra10cartões sem os detalhes completos de todos os sectores; cada cartão usa a URL própria. Páginas individuais, guias, especialização Fintech, filtro e continuidade do dossier preservados. Comparação pública antes/depois:os mesmos10cartões e as mesmas3páginas de Agronegócio/Turismo/Fintech, cada uma com1guia e respectivas oportunidades; só o índice perdeu `sectorDetails`, conforme pedido. Navegador no portal confirmou o índice servido sem repetição.
|
||||
|
||||
QA dirigida:3testes/319asserções, transacções com rollback em loopback55441;Wayfinder/tipos/build aprovados. Instalador existente preservado e controlos automáticos aprovados:PostgreSQL1/3106,Unit59/317,frontend1080pass/0fail,TypeScript ebuild31,42s. Integração Git normal,sem force;release anterior mantida até compilação e verificações prontas,seguida de troca atómica e conferência da revisão/manifest servidos. Delta de aplicação limitado a2ficheiros;1000ficheiros de fonte/recursos/documentos conferidos com o Git. Apenas2registos previstos da matriz actualizados,demais registos semanticamente intactos.
|
||||
|
||||
Capacidade antes:88,3GiB e23,76milhões de inodes livres;novamente conferida após backup. Backup fresco `/var/backups/investmoz-sector-navigation-20261027/backup`,dumpSHA256`9b2437017495fe449cfe0c869da62870bd0fc4bddde596660a37392b8dbdaaba`;restauro das174tabelas com contagens/checksums idênticos,em contentor sem rede,porta55441.369ficheiros persistentes/configuração verificados;172tabelas de negócio intactas depois da instalação;cache/sessões operacionais reconciliados por identidade/expiração/pedidos públicos. Schema,papéis,grants epermissões iguais. Sem migrations,importações ou publicação de pilotos;storageQA fica separado da release servida. Songo/Gurué privados;L8 e percurso proactivo anteriores preservados. PEC`7fdb6b42` continua emQA separado e não foi instalado nesta passagem. Avaliação real da equipa continua dependente do retorno dos participantes.
|
||||
|
||||
Recibo consolidado: `docs/investmoz/unification/sector-navigation-delivery-20261027.json`. Cópia remota: `/var/backups/investmoz-sector-navigation-20261027/installed-receipt.json`,SHA256`cd95142b7f85e40a0f86e0f7d3dca513e7bc208c89aa91b8e3ad213191d8465f`. Provas locais: `outputs/sector-navigation-delivery-20261027/`. Os registos anteriores de QA/transferência são históricos;esta correcção já não está pendente de instalação. Não se declara concluído o âmbito global dos requisitos sectoriais ou do simulador.
|
||||
|
||||
|
||||
## 27/10/2026 — PEC entregue e retoma dos créditos próprios de L8
|
||||
|
||||
**Instalado e conferido em `e182d1d79dc945f35d2079c6b8ca2ac93048cec3`**, incluindo o PEC aceite emQA`7fdb6b42` e preservando integralmente a correcção dos sectores`669cb897`. A reconciliação alterou apenas conflitos documentais, conservando os históricos das duas linhas e o recibo sectorial`4ee427ba`. Os4ficheiros de aplicação PEC ficaram idênticos à QA;os ficheiros sectoriais e seus testes idênticos ao instalado. Fonte/configuração/routes/schema fora do delta preservados. A matriz mantém as4actualizações documentais aceites da avaliação pela equipa,além da linhaPEC;sem nova revisão funcional do percurso proactivo.
|
||||
|
||||
**Âmbito entregue:** residente geral,ano civil2026,MZN;PEC efectivamente pago do próprio exercício,titular/data/prova/parcelas e uso anterior documentados. Dedução até à colecta disponível antes de retenções/PPC;créditos precedentes ausentes ou conhecidos zero. ExcedentePEC não gera reembolso nem saldo oficial transportado. Cálculo da obrigação,prestações,transporte/caducidade anteriores,créditos próprios positivos e imposto autónomo continuam separados. Informação em falta não vira zero;históricos não são recalculados. Reutilizadas as5recuperações e relatórios QA aceites:1 408−200−500=708 a pagar;PEC2 000 deduz1 408 e deixa592 não deduzidos;os500 a recuperar no caso comPPC provêm doPPC,sem reembolsar oPEC.
|
||||
|
||||
**Entrega:** capacidade87,16GiB e inodes suficientes antes,e novamente conferida após backup. Dump frescoSHA256`eff57769cda1e9d0f3d507348b57fc455c0bbc6e38beeb3688135109a6e219fd`,em`/var/backups/investmoz-irpc-l8-pec-20261027/backup`;restauro174tabelas com contagens/checksums iguais,contentor sem rede,porta55441.369ficheiros persistentes/configuração e172tabelas de negócio intactos;schema,papéis,grants e permissões iguais. Cache/sessões operacionais reconciliados:inclui1sessão anónima preexistente,sem mudança de identidade,com token efémero/URL anterior e IP/actividade actualizados,correlacionados individualmente comGET200de documento público pelo mesmoUA/IP/instante;nenhuma sessão sobrescrita pelo comparador. Sem migrations,importações,fixtures activas ou publicação de pilotos. Guardas do instalador mantidas;release anterior em serviço até troca atómica.1000fontes antes/1002depois conferidas comGit;delta de aplicação4ficheiros. Manifest e5marcadoresPEC servidos conferidos;10cartões dos sectores,3páginas/guias/oportunidades e filtroAgronegócio idênticos antes/depois.
|
||||
|
||||
**Provas reutilizadas:**12testesPEC+19do anual,API102asserções/5casos e relatórios aceites,sem outra ronda manual. Controlos obrigatórios do instalador aprovados:PostgreSQL1/3106,Unit59/317,frontend1092pass/0fail,TypeScript ebuild30,51s. Recibo consolidado:`docs/investmoz/simulations/irpc-l8-pec-delivery-20261027.json`;cópia remota:`/var/backups/investmoz-irpc-l8-pec-20261027/installed-receipt.json`,SHA256`a970266625807803ce73f1e63d0da1a540cff264ab88a19d3a8a387a85b26c34`. Provas locais:`outputs/irpc-l8-pec-delivery-20261027/`. Os recibosQA anteriores permanecem históricos;este PEC deixa de ser pendência de instalação.
|
||||
|
||||
### Retoma concreta — créditos próprios e saldo restante
|
||||
|
||||
O primeiro recorte para implementaçãoQA será o crédito por dupla tributação internacional do próprio exercício. Retoma desta passagem:fonte e contrato de dados/cálculo delimitados,**ainda sem novo código ou cálculo de crédito activado**. Reutilizado oCIRPC65,Lei34/2007PDF18/p768-(72),já no acervo aceite;ordem face aoPEC em66/RIRPC21. Não se exige revalidação genérica da fonte. Rendimento estrangeiro incluído na matéria colectável,imposto sobre o rendimento efectivamente pago,titular/país/exercício/prova e montante disponível devem corresponder. O crédito é limitado pela fracção nacional correspondente e,pela convenção quando aplicável;esse limite não pode ser presumido a partir do montante recebido. Exemplo sintético para provar o novo delta,**não implementado**:imposto estrangeiro10,limite nacional documentado8,ausência documentada deCDTaplicável→crédito8;colecta1 408→1 400;PEC200→1 200;PPC500→saldo700,sem retenções. Os2acima do limite não se tornam automaticamente crédito a transportar.
|
||||
|
||||
| Código ainda por implementar | Condição concreta de retoma |
|
||||
| --- | --- |
|
||||
| Qualificação,limites e dedução do crédito internacional corrente | Implementar o contrato acima,com estado conhecido/ausente/em falta,identidade da prova,ordem antes doPEC,limites e prevenção de uso duplicado comPEC/PPC/RF;demonstrar só novos efeitos,gravação/recuperação/relatório e preservação dos históricos. |
|
||||
| Crédito por dupla tributação económica e crédito de investimento/benefício | Grupos distintos;art64 e fundamentoCBFpróprio,qualificaçãoL7,titular/período,lucro/imposto/activos elegíveis,limite,cumulação e utilização documentados. Localização não concede benefício automaticamente. |
|
||||
| Transporte/utilização/caducidade dePEC e créditos anteriores | Proveniência por exercício e utilizações reais;PEC66 distinto do crédito65 e seu prazo próprio. Não confundir excesso sobre limite de elegibilidade com falta de colecta,nem criar saldos oficiais a partir de simulações. |
|
||||
| ObrigaçãoPPC/PEC,calendário,juros/multas/guias | Volume/PPC anteriores e demais factos pertinentes,edição normativa/vencimentos próprios;separado da dedução dePECjá pago agora entregue. |
|
||||
| Imposto autónomo,períodos especiais,outras moedas eM20/M22/submissão | Mantêm as dependências próprias já delimitadas;reutilizar periodização/câmbio quando a regra do caso estiver correspondida,sem compensação ou códigos oficiais presumidos. |
|
||||
|
||||
| Evidência/documento do caso em falta | Exemplo e condição concreta de retoma |
|
||||
| --- | --- |
|
||||
| Prova e inclusão do rendimento estrangeiro | Imposto10MZN sem rendimento identificado ou fracção nacional demonstrada:reutilizar dados/documentos existentes e identificar a correspondência;subtotal não resolve o saldo dependente. Não é falta de autenticação da fonte normativa. |
|
||||
| Convenção aplicável e limite | País/CDT conhecidos mas artigo,período ou imposto pago nos termos da convenção em falta:corresponder esses elementos;bloqueia apenas o caso dependente. |
|
||||
| Regulamentação2026para obrigação/calendário | Anúncio sem articulado publicado:retomar com diploma,artigos e vigência dos parâmetros afectados;sem bloquear oPECcorrente ou crédito independente cujo fundamento já está disponível. |
|
||||
|
||||
L8 permanece parcial. A avaliação real do percurso proactivo continua a aguardar retorno da equipa;dificuldades observadas serão correcções concretas. Songo eGurué permanecem privados;L3/ISPC/câmbio/L6/L7 eSGFTEMPO preservados. Sem nova estimativa global ou declaração de IRPC completo.
|
||||
|
||||
|
||||
## 27/10/2026 — Crédito internacional corrente: percurso implementado em QA
|
||||
|
||||
**Estado actual: implementado e demonstrado em QA, sem instalação.** Base `814fe0a9`, descendente do PEC instalado `e182d1d7` e da correcção dos sectores `669cb897`. Os registos anteriores de delimitação são históricos: o crédito corrente abaixo já tem código executável; os restantes grupos continuam separados. Ramo `codex/irpc-l8-international-credit-qa-20261027`. Recibo: `docs/investmoz/simulations/irpc-l8-international-credit-qa-20261027.json`.
|
||||
|
||||
**Âmbito e origem das restrições:** pedido expresso da Tempo para implementar o crédito internacional em QA, preservando o PEC e os históricos. Reutilizado CIRPC65(1–2), Lei34/2007, PDF18/p768-(72), e ordem antes do PEC em66/RIRPC21, do acervo aceite. Escolha incremental de implementação: residente geral, ano civil2026 e valores comprovados emMZN; inclusão do rendimento e fracção nacional documentadas, sem determinação automática dessa fracção, de vigência/aplicabilidade deCDT ou de conversão cambial. A selecção do país não concede crédito. A mudança de país/colecta exige reconfirmar a correspondência dos limites, conservando os valores.
|
||||
|
||||
**Percurso:** a interface permite preparar explicitamente o detalhe, reutilizando entidade/exercício/colecta e o resumo anterior quando este declara ausência ou zero; mantém esse resumo e os resultados anteriores. Uma posição por país/exercício agrega rendimentos e provas sob um só limite nacional. Regista natureza do imposto, pagamento efectivo, titular, suporte e utilização anterior real; data fora do exercício exige correspondência documental expressa, sem inferir transporte. O crédito elegível é o menor entre imposto seleccionado comprovado, fracção nacional e limiteCDT quando aplicável; desconta utilização anterior e limita a dedução à colecta. Seguem PEC, retenções ePPC existentes. Não transforma simulações históricas em utilização fiscal. Limites admitem fracção exacta; apresentação decimal não é regra de arredondamento fiscal.
|
||||
|
||||
| Caso sintético recuperado da API | Novo efeito demonstrado em MZN |
|
||||
| --- | --- |
|
||||
| Imposto estrangeiro10, limite nacional8, não aplicaçãoCDT documentada | Crédito8; 1 408−8=1 400; PEC200→1 200; PPC500→**700 a pagar, simulado**. Os2 acima do limite não são transporte. |
|
||||
| LimiteCDT documentado6 | Crédito6; saldo simulado702. Os dados de país/CDT são artificiais, sem afirmar uma convenção real. |
|
||||
| Utilização anterior comprovada3 | Elegível8, disponível5; saldo simulado703. Não somar utilizações de simulações. |
|
||||
| Crédito disponível2 000 e colecta1 408 | Deduz1 408; insuficiência592 identificada, sem transporte; PEC200 não deduzido. Os500 a recuperar provêm doPPC, sem reembolso do crédito/PEC. |
|
||||
| Ausência internacional expressa e documentada | Crédito0, saldo708; não confundir ausência com informação em falta. |
|
||||
| AplicabilidadeCDT em falta ou mesma prova usada comoPPC e imposto estrangeiro | Saldo e dedução dependentes indeterminados, com motivo; não preencher por presunção. |
|
||||
|
||||
**Provas do delta:**18testes novos e31anuais/PEC afectados,49aprovados; API privada1teste/171asserções com transacção revertida na baseQA guardada, porta55441. Sete casos porPOST/PUT/GET, recalculados após recuperação e exportados pelos relatóriosHTML e dossier existentes; histórico anterior congelado. Wayfinder `routes/actions`, TypeScript e compilação isolada aprovados. Esta passagem não inclui uma nova prova manual de utilização no browser nem uma avaliação pela equipa. Não se repetiram as suites independentes aceites. As7tabelas de registos anteriores ficaram intactas;30PDFs originais e11consumidores protegidos iguais ao instalado. Sem migrations, novas permissões ou configuração activa; nada enviado para a base do portal. Os testes usam fixtures sintéticas portáteis, sem dados comerciais ou pilotos reais.
|
||||
|
||||
**Prevenção de dupla dedução:** identidade física da prova por emitente/documento, independentemente da data; confronto entre imposto estrangeiro,RF,PPC ePEC e entre mapas do mesmo snapshot; país/entidade/exercício e montantes disponíveis coerentes. Posições repetidas do mesmo país/ano exigem consolidação, evitando repetir a quota nacional. Não existe consumo de saldos oficiais nem reconciliação global entre dossiers independentes. Valores conhecidos, ausência declarada e informação em falta permanecem distintos. Créditos económicos e benefícios positivos ou desconhecidos deixam apenas o saldo dependente indeterminado, preservando a qualificação internacional conhecida.
|
||||
|
||||
### Saldo concreto de L8 após este incremento
|
||||
|
||||
| Código por implementar | Condição de retoma |
|
||||
| --- | --- |
|
||||
| Determinação automática da fracção nacional e limites específicosCDT | Corresponder componentes da base/imposto e rendimento estrangeiro e regras específicas do caso; substituir entradas documentadas apenas quando essa correspondência estiver demonstrada. A regra corrente do menor/ordem já está implementada. |
|
||||
| Crédito económico e créditos de investimento/benefício | Aplicar art64/CBF próprio, qualificaçãoL7, base elegível, limite, cumulação e uso real; não os converter em crédito internacional. |
|
||||
| Transporte/caducidade e utilização interanual | Proveniência por exercício, utilização real e prazo próprio de65(3), distinto doPEC66; sem transformar imposto acima do limite em crédito transportável. PEC excedente/transporte continuam fora do âmbito entregue. |
|
||||
| Obrigação/calendárioPPC/PEC, autónomos, períodos especiais, outras moedas eM20/M22 | Retomar cada grupo com factos e regra de período/compensação/câmbio/campo correspondidos; reutilizar motores existentes sem alterar o cálculo independente aceite. |
|
||||
|
||||
| Informação documental externa em falta | Exemplo e condição de retoma |
|
||||
| --- | --- |
|
||||
| Inclusão e fracção nacional do caso | Imposto10 sem prova da inclusão/base: identificar o rendimento incluído e o suporte da quota; apenas o saldo desse caso fica indeterminado. Não exige nova autenticação da fonte oficial. |
|
||||
| Convenção, período e imposto elegível | País conhecido sem artigo/limite aplicável: corresponder artigo, vigência e imposto pago nos termos daCDT. O limite6 da QA não é uma correspondência oficial. |
|
||||
| Pagamento/titular/uso anterior e correspondência temporal | Pagamento em2027 afecto a2026 sem suporte: anexar/identificar correspondência expressa; sem presumir transporte ou utilização zero. |
|
||||
|
||||
**L8 permanece parcial.** Crédito internacional apenas emQA; PEC instalado em`e182d1d7` mantém o âmbito demonstrado. Songo eGurué privados; L3,ISPC,câmbio,L5–L7 eSGFTEMPO preservados. Avaliação de utilização do percurso proactivo continua a aguardar retorno da equipa. Sem instalação, publicação ou nova estimativa global nesta passagem.
|
||||
|
||||
|
||||
## 27/10/2026 — Crédito internacional corrente entregue pelo Git
|
||||
|
||||
**Instalado e conferido em `838ad941a24ae76ec4bf91ccc3f92012668fe8cf`**, sobre o PEC instalado`e182d1d7`, preservando a correcção dos sectores`669cb897`. A candidata é a própria revisãoQA aceite`838ad941`, sem alteração funcional na preparação; integração normal em`main`, sem sobrescrever outro trabalho. Os registosQA anteriores são históricos: este recorte deixa de estar pendente de instalação. Não encerra L8 ou o IRPC.
|
||||
|
||||
**Delta instalado:**5ficheiros de aplicação — detalhe internacional, projecção dos códigos do catálogo de países existente e integração no saldo anual. Todos idênticos à QA. Conferidas1002fontes antes/1005depois comGit; restantes fontes comparadas conservadas, incluindoPEC,L3,ISPC,câmbio,L5–L7,sectores,catálogoISO,rotas e configuração. Sem novas permissões,migrations ou importações. Fixtures sintéticas apenas no código de testes; não importadas para dados activos nem publicadas. Songo eGurué permanecem privados; SGFTEMPO separado.
|
||||
|
||||
**Recorte:** residente geral,ano civil2026,MZN comprovados. Inclusão do rendimento,fracção nacional,aplicabilidade/limiteCDT e utilizações anteriores documentados. Menor entre imposto pago e limites aplicáveis,menos uso anterior e até à colecta,antesPEC/RF/PPC;sem concessão pela escolha do país,conversão/arredondamento fiscal ou transporte presumidos. Exemplo condicionado aos dados e suportes sintéticos demonstrados:1 408−8de crédito−200PEC−500PPC=**700MZN de saldo simulado a pagar**. Os2acima do limite de elegibilidade não são transporte; insuficiência de colecta fica separada. Caso com500a recuperar deriva doPPC,sem reembolso do crédito ouPEC. Informação em falta e prova duplicada deixam o saldo dependente indeterminado.
|
||||
|
||||
**Recuperação e preservação:** capacidade antes superior a85GiB e23,69milhões de inodes; mínimo existente do instalador5GiB mais backup confrontado com espaço real e novamente conferido após backup. Backup fresco`/var/backups/investmoz-irpc-l8-international-20261027/backup`,dumpSHA256`b1e9740653a8664c23268e228efd3ed6a0514bf3e8786dd5adf43342cdb475e1`;restauro174tabelas com contagens/checksums iguais em contentor sem rede,porta55441.369ficheiros persistentes/configuração e172tabelas de negócio intactos depois da instalação;schema,papéis,grants e permissões iguais. Cache/sessões operacionais reconciliados individualmente com o backup e pedidos públicos;nenhum registo de negócio ou sessão sobrescrito pelo comparador. Release anterior mantida até compilação/verificações e troca atómica;runtimeQA fora da árvore servida.
|
||||
|
||||
**Provas:** reutilizadas18novas+31anuais/PEC,API171asserções e7recuperações comHTML/dossier e históricos conservados. Sem nova ronda manual das funcionalidades não afectadas. Controlos automáticos obrigatórios do instalador:OK (1 test, 3106 assertions); ✓ built in 31.55s; OK (59 tests, 317 assertions); # tests 1110; # pass 1110; # fail 0;TypeScript/Wayfinder/build e guardas aprovados. Revisão pública e hash do manifest conferidos,HTTP200do simulador e6marcadores compilados do novo delta/PEC identificados. Não foram criados casosQA no portal.
|
||||
|
||||
**SaldoL8 preservado:** derivação automática da fracção nacional/CDT específicas;créditos económicos/CBF;utilização interanual/transporte/caducidade próprios;obrigação/calendárioPPC/PEC;autónomos,períodos especiais,moedas eM20/M22 conforme dependências já discriminadas. Excedente/transportePEC fora do âmbito entregue. Informação de inclusão/CDT/prova/titular/uso bloqueia apenas o caso dependente. As condições concretas de retoma mantêm-se no saldo curtoQA anterior;sem repetir pesquisas ou exigir nova autenticação de fontes oficiais aceites.
|
||||
|
||||
Recibo:`docs/investmoz/simulations/irpc-l8-international-credit-delivery-20261027.json`. Cópia remota:`/var/backups/investmoz-irpc-l8-international-20261027/installed-receipt.json`,SHA256`d80324b898fd160ab3e7a2fd0b11ea2decd0b99a9324255850062ee4a13e0f0f`. Provas locais:`outputs/irpc-l8-international-delivery-20261027/`. Resolvida a colisão local`inspect.py`→`remote_delivery_inspection.py`;módulo padrão/Paramiko verificados no mesmoPython,guardaLinux/geteuid mantida,sem alteração de credenciais ou do servidor por essa correcção. A avaliação real do percurso proactivo continua dependente do retorno da equipa. **L8 permanece parcial.**
|
||||
|
||||
|
||||
## 28/10/2026 — saldos anteriores internacionais e PEC: delta QA concluído
|
||||
|
||||
Implementado e demonstrado em QA isolada, **sem instalação**. Base `cf4e0638`, com internacional `838ad941`, PEC `e182d1d7` e sectores `669cb897` preservados. A pasta original estava noutro ramo; isso não foi tratado como eliminação de funcionalidades. Recibo: `docs/investmoz/simulations/irpc-l8-carry-qa-20261028.json`.
|
||||
|
||||
Extensão opcional do livro anual existente: uma origem consolidada por natureza/país/exercício, apuramento e regra de origem documentados, elegível menos dedução inicial e utilizações efectivas subsequentes. Internacional: cinco exercícios seguintes; PEC: três. Origem2021 internacional e PEC2023 chegam a2026; origem2020 internacional/PEC2022 ficam fora do prazo ordinário, conservadas com explicação e saldo histórico. Não se converte imposto acima do limite em crédito transportável. Situações transitórias/especiais não são qualificadas pela regra ordinária.
|
||||
|
||||
Escolha expressa das parcelas correntes e anteriores, vinculada à colecta seleccionada, sem ordem FIFO imposta nem redução automática. A soma não ultrapassa a colecta e os créditos internacionais precedem o PEC. Valores não escolhidos ficam separados da insuficiência de colecta; simulações não criam nem consomem saldos oficiais. Exemplo sintético:1408−8corrente internacional−50anterior internacional−200PECcorrente−100PECanterior−500PPC=**550MZN a pagar, simulado**.
|
||||
|
||||
**Provas:**16novos+49afectados=65testes aprovados; API1teste/119asserções,6casos POST/PUT/GET na base guardada `proactive_investment_20261027_test`,loopback55441,com rollback. Seis recuperações recalculadas integralmente,HTML/dossier concordantes e resultados anteriores congelados.55oportunidades,64documentos,1vínculo,19rascunhos,40utilizadores,3organizações e409eventos anteriores preservados.31ficheiros do acervo byte a byte iguais,sem nova importação/publicação. TypeScript,build QA com exit0 ePint aprovados. Não foi feita nova prova manual de interface ou avaliação pela equipa. Sem migrations,grants,contas,alteração activa ou operações de servidor.
|
||||
|
||||
**Saldo concreto:**L8 continua parcial. Próximo incremento independente:crédito económico64,com gross-up53(1)a/exclusão40,qualificação dos lucros/pagador/beneficiário/imposto e limite proporcional correspondidos; fonte aceite já disponível. Permanecem fracção nacional/CDT automática,créditos/benefícios próprios,obrigação/calendárioPPC/PEC,autónomos,períodos especiais,outras moedas eM20/M22. Não declarar IRPC concluído nem repetir a validação das entregas independentes aceites.
|
||||
|
||||
|
||||
## 28/10/2026 — crédito económico ordinário demonstrado em QA; agrupamento compatível
|
||||
|
||||
**Implementado e demonstrado em QA isolada, sem instalação.** Ramo `codex/irpc-l8-economic-qa-20261028`, base `6ededb59`, incluindo os saldos anteriores `d134a92d`. A última referência instalada documentada continua `838ad941`; não foi feita nova leitura do servidor. Não se reabre a aceitação dos históricos ou das provas anteriores.
|
||||
|
||||
**Cobertura:**CIRPC64(1–2),53(1)a,40(1)na redacção20/2009 eRIRPC21(2/5). Lucros ordinários distribuídos e IRPC correspondente documentados,beneficiário/distribuidor com sede/direcção efectiva emMoçambique,distribuidor sujeito/não isento,exclusão40(1)não aplicável com suporte. A selecção explícita da colecta corrente reutiliza NUIT/exercício/residência válidos e conserva edições divergentes. Novo detalhe opcional; resumo económico deliberado anterior e resultados congelados conservados.
|
||||
|
||||
**Cálculo:**60%doIRPCdocumentadamente correspondente aos lucros;confere o acréscimo53 já incluído emL6 e limita pela quota proporcional documentada,menos utilização efectiva anterior,até à colecta. O portal não deriva IRPC do dividendo líquido nem altera a base/colecta silenciosamente;inclusão/acréscimo e quota exigem correspondência do caso. A existência de perdas que reduzam a base abaixo dos lucros não é tratada como impedimento genérico. Crédito económico precede o internacional ePEC,sem reembolso ou transporte económico presumido. Exemplo sintético condicionado aos suportes:IRPC20→nominal12;1408−12−8internacional−200PEC−500PPC=**688MZN a pagar,simulado**.
|
||||
|
||||
**Mesma parcela:**identidade por emitente/documento/parcela com suporte;duplicação dos lucros também conferida independentemente de trocar o comprovativo de imposto,a data ou oID. O mesmo documento pode suportar parcelas de imposto distintas quando a separação estiver documentada em ambos os registos. Um comprovativo anterior sem parcela continua a abranger o documento inteiro,não se torna livre por receber novo nome noutro mapa. Atribuições anteriores e capacidade do comprovativo conferidas neste e noutros contextos do snapshot;divergências não são apagadas. ProtecçõesL5 conservadas. Não é reconciliação global de conta fiscal entre dossiers independentes.
|
||||
|
||||
**Agrupamento:**composição demonstrada com os saldos anteriores:1408−12económico−8internacionalcorrente−50internacionalanterior−200PECcorrente−100PECanterior−500PPC=**538MZN a pagar,simulado**. A prioridade económica não reduz automaticamente a afectação entre exercícios já escolhida;se exceder a colecta remanescente,o caso pede reconciliação conservando as escolhas. Os dois incrementos podem integrar uma entrega Git comum;nenhum foi instalado nesta passagem.
|
||||
|
||||
**Provas do delta:**20testes económicos novos+65afectados aceites=85aprovados;API1teste/210asserções,11casos POST/PUT/GET na base guardada `proactive_investment_20261027_test`,loopback55441,transacção com rollback. Onze recuperações recalculadas integralmente,HTML/dossier concordantes e históricos anteriores conservados.55oportunidades,64documentos,1vínculo,19rascunhos,40utilizadores,3organizações e409eventos anteriores intactos. TypeScript,build QA ePint aprovados.31ficheiros do acervo preservados por bytes,sem novas importações,publicações,scan declarado,migrations,contas ougrants. Não foi feita nova prova manual da interface nem avaliação pela equipa.
|
||||
|
||||
**Pendências concretas:**inclusão/correcção automática do dividendo/acréscimo emL6 e derivação da quota proporcional;exclusão40(1)na base com todas as suas condições;liquidação64(3)/associações64(4);créditos/benefícios específicos;fracção internacional/CDT automática;obrigação/calendárioPPC/PEC;autónomos,períodos especiais,outras moedas eM20/M22. Correspondências e parâmetros em falta afectam o caso dependente,sem revalidação genérica de fontes oficiais. Saldos anteriores mantêm o recorte ordinário já demonstrado. **L8 permanece parcial.** Recibo:`docs/investmoz/simulations/irpc-l8-economic-qa-20261028.json`.
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
{
|
||||
"date": "2026-10-28",
|
||||
"status": "implemented and demonstrated in isolated QA; not installed",
|
||||
"branch": "codex/irpc-l8-carry-qa-20261028",
|
||||
"base": "cf4e0638",
|
||||
"installed_reference_unchanged": "838ad941a24ae76ec4bf91ccc3f92012668fe8cf",
|
||||
"scope": {
|
||||
"year": "2026",
|
||||
"subject": "same subject, resident general",
|
||||
"period": "annual ordinary origins; calendar-year current close",
|
||||
"currency": "documented MZN; exact foreign-credit fractions supported",
|
||||
"international": "eligible origin credit minus origin deduction and documented actual later uses; five subsequent exercises, art65(3)",
|
||||
"pec": "actual PEC eligible origin minus origin deduction and actual later uses; three subsequent exercises, art66(1–2)",
|
||||
"allocation": "explicit documented selection between current and prior exercises, confirmed against current assessment; no presumed FIFO or automatic reduction",
|
||||
"expiry": "ordinary window ended excludes current deduction but keeps source and arithmetic historical remainder",
|
||||
"origin_rules": "origin assessment, inclusion/limits/insufficient collection and applicable period documented; exceptional/transitional rules outside scope"
|
||||
},
|
||||
"restriction_origins": {
|
||||
"Tempo": "preserve approved sources, accepted deliveries, data/history, QA isolation; continue L8",
|
||||
"accepted_requirement": "CIRPC65(3),66(1–2), source already accepted; no new provenance approval",
|
||||
"technical_choices": "optional versioned snapshot extension; consolidated nature/country/origin-year balance; explicit selection avoids invented interyear priority"
|
||||
},
|
||||
"verification": {
|
||||
"new_frontend_tests": 16,
|
||||
"affected_previously_accepted_tests": 49,
|
||||
"total_frontend_pass": 65,
|
||||
"frontend_fail": 0,
|
||||
"API_tests": 1,
|
||||
"API_assertions": 119,
|
||||
"API_cases": 6,
|
||||
"API_database": "proactive_investment_20261027_test",
|
||||
"API_port": 55441,
|
||||
"API_transactions_rolled_back": true,
|
||||
"recovered_recalculated_HTML_dossier": 6,
|
||||
"frozen_prior_results_preserved": true,
|
||||
"typescript": "passed",
|
||||
"build": "passed, actual subprocess exit0",
|
||||
"PHP_formatting": "pint passed",
|
||||
"browser_manual": "not performed; no manual UI acceptance claimed"
|
||||
},
|
||||
"preservation": {
|
||||
"previous_deliveries": [
|
||||
"international838ad941",
|
||||
"PECe182d1d7",
|
||||
"sectors669cb897"
|
||||
],
|
||||
"source_files_byte_identical": 31,
|
||||
"existing_QA_records": {
|
||||
"database": "proactive_investment_20261027_test",
|
||||
"tables": {
|
||||
"opportunities": {
|
||||
"existing_rows": 55,
|
||||
"unchanged": true
|
||||
},
|
||||
"documents": {
|
||||
"existing_rows": 64,
|
||||
"unchanged": true
|
||||
},
|
||||
"opportunity_documents": {
|
||||
"existing_rows": 1,
|
||||
"unchanged": true
|
||||
},
|
||||
"simulation_form_drafts": {
|
||||
"existing_rows": 19,
|
||||
"unchanged": true
|
||||
},
|
||||
"users": {
|
||||
"existing_rows": 40,
|
||||
"unchanged": true
|
||||
},
|
||||
"organizations": {
|
||||
"existing_rows": 3,
|
||||
"unchanged": true
|
||||
},
|
||||
"audit_events": {
|
||||
"existing_rows": 409,
|
||||
"unchanged": true
|
||||
}
|
||||
},
|
||||
"transaction_rollback": true
|
||||
},
|
||||
"matrix": "existing AUD-SIM-INT02 updated; all other rows semantically unchanged",
|
||||
"new_accounts_permissions_migrations": false,
|
||||
"active_data_or_configuration_written": false,
|
||||
"remote_deployment_or_publication": false,
|
||||
"Songo_Gurue": "no pilot changes; remain private",
|
||||
"other_dossiers": "same-snapshot origins checked, no global official account reconciliation"
|
||||
},
|
||||
"cases": [
|
||||
{
|
||||
"name": "payable",
|
||||
"status": "payable",
|
||||
"balance": {
|
||||
"numerator": "550",
|
||||
"denominator": "1"
|
||||
},
|
||||
"apiRecovered": true,
|
||||
"recalculationEqual": true,
|
||||
"html": true,
|
||||
"dossier": true,
|
||||
"frozenHistoryPreserved": true
|
||||
},
|
||||
{
|
||||
"name": "expired",
|
||||
"status": "indeterminate",
|
||||
"balance": null,
|
||||
"apiRecovered": true,
|
||||
"recalculationEqual": true,
|
||||
"html": true,
|
||||
"dossier": true,
|
||||
"frozenHistoryPreserved": true
|
||||
},
|
||||
{
|
||||
"name": "missing-use",
|
||||
"status": "indeterminate",
|
||||
"balance": null,
|
||||
"apiRecovered": true,
|
||||
"recalculationEqual": true,
|
||||
"html": true,
|
||||
"dossier": true,
|
||||
"frozenHistoryPreserved": true
|
||||
},
|
||||
{
|
||||
"name": "overallocated",
|
||||
"status": "indeterminate",
|
||||
"balance": null,
|
||||
"apiRecovered": true,
|
||||
"recalculationEqual": true,
|
||||
"html": true,
|
||||
"dossier": true,
|
||||
"frozenHistoryPreserved": true
|
||||
},
|
||||
{
|
||||
"name": "fractional",
|
||||
"status": "payable",
|
||||
"balance": {
|
||||
"numerator": "1822",
|
||||
"denominator": "3"
|
||||
},
|
||||
"apiRecovered": true,
|
||||
"recalculationEqual": true,
|
||||
"html": true,
|
||||
"dossier": true,
|
||||
"frozenHistoryPreserved": true
|
||||
},
|
||||
{
|
||||
"name": "recoverable",
|
||||
"status": "recoverable",
|
||||
"balance": {
|
||||
"numerator": "-500",
|
||||
"denominator": "1"
|
||||
},
|
||||
"apiRecovered": true,
|
||||
"recalculationEqual": true,
|
||||
"html": true,
|
||||
"dossier": true,
|
||||
"frozenHistoryPreserved": true
|
||||
}
|
||||
],
|
||||
"remaining_L8": [
|
||||
"automatic national-fraction derivation and case-specific treaty correspondence",
|
||||
"economic credit art64/53 and benefit-specific credits with own qualification/limits",
|
||||
"PPC/PEC obligation and calendar, interest/penalties and payment guides with applicable edition",
|
||||
"autonomous taxation, special periods, other currencies, M20/M22 transport/submission",
|
||||
"exceptional or transitional interyear cases outside ordinary source window; no official ledger created"
|
||||
],
|
||||
"next_independent_increment": {
|
||||
"item": "economic double-tax credit",
|
||||
"source": "existing accepted CIRPC64,53(1)a and exclusion40; BR52 original",
|
||||
"required_correspondence": "distributed profits included, payer/recipient residence and IRPC nonexemption, art40 distinction, source tax, gross-up53, proportional cap64, actual use and nonduplication; do not infer from declared dividend alone"
|
||||
},
|
||||
"L8_complete": false,
|
||||
"evidence": {
|
||||
"api.log": "ef604fbe7a289930cb0b6dfa35407e0f115092894d32fd98c04a9e1f3b732415",
|
||||
"build-result.json": "80bb1b6bb0ccadaf94f241ace9b0b19f77461b193b8b7522afa655d414d36198",
|
||||
"build.log": "d93839f94f37e181acafe3f6295c10982e036475b1f5186aedba9a9ae3cd6bd9",
|
||||
"existing-records-preservation.json": "b45b3306d3c9dee63fbbaf9a04bba31008019e65892bd16322460b5ef53eda7d",
|
||||
"frontend-tests.log": "86a878a794a8f1ed7db56d689f0b9d5a2bdbc6079aa66e3cfe4b65ae64e656f6",
|
||||
"protected-consumers.json": "9f147ea688a79a77a8d7507cf1c322a6a3c03b959be8fc5ed114db63dac05761",
|
||||
"recovered-report-receipt.json": "82df86eaabbb79d760e4f2f1efb0f900d9e9607e11ea49502746dbafc46fed03",
|
||||
"source-preservation.json": "c28df37db83989fe5746dcb5382a75c4f39471a16a7cbe64670260209cf6ad79"
|
||||
},
|
||||
"qa_commit": "d134a92d169f9159a692ed158e3a051889f7ab3a",
|
||||
"qa_branch_pushed": false
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
{
|
||||
"date": "2026-10-28",
|
||||
"status": "ordinary economic credit implemented and demonstrated in isolated QA; not installed",
|
||||
"branch": "codex/irpc-l8-economic-qa-20261028",
|
||||
"base": "6ededb59",
|
||||
"last_installed_receipt_reference": "838ad941a24ae76ec4bf91ccc3f92012668fe8cf",
|
||||
"remote_status_read_this_turn": false,
|
||||
"scope": {
|
||||
"current_year": "2026",
|
||||
"current_period": "calendar year, resident general",
|
||||
"currency": "documented MZN; exact fractions without fiscal rounding",
|
||||
"profit_kind": "ordinary distributions art64(1–2)",
|
||||
"eligibility": "same-country seat/effective management; payer subject to IRPC and nonexempt; nonapplication of40(1) documented, including2009 version",
|
||||
"nominal_credit": "60 percent of documented IRPC corresponding to included distributed profits; no assumed rate on net dividend",
|
||||
"gross_up": "expected nominal60 percent reconciled with53(1)a amount already included in current L6; does not overwrite or recalculate source base",
|
||||
"proportional_ceiling": "case-specific documented input tied to current assessment; not automatically derived",
|
||||
"prior_use": "actual documented economic credit use, distinct from simulation history and prior source-tax attributions",
|
||||
"order": [
|
||||
"economic credit up to collecta",
|
||||
"international credit current/prior according to existing explicit selection",
|
||||
"PEC current/prior according to existing explicit selection",
|
||||
"own withholdings and PPC"
|
||||
],
|
||||
"refund": "no economic-credit refund or carry created; PPC can create simulated recoverable balance"
|
||||
},
|
||||
"proof_parcels": {
|
||||
"stable_identity": "issuer/document/parcel; distribution identity also independent of source-tax document,date and row id",
|
||||
"economic_vs_international": "same physical tax parcel cannot fund both credit natures",
|
||||
"same_paper_different_tax": "supported distinct parcels can coexist; old proof without a parcel overlaps the whole paper until reconciled",
|
||||
"capacity": "same proof parcel amounts and prior attributions must agree and total allocation remains within available amount across snapshot contexts",
|
||||
"L5": "existing withholding source/allocation checks preserved; parcel relabelling cannot turn repeated L5 use into new tax",
|
||||
"partial_information": "saved and recovered as indeterminate, never reset or silently assumed absent",
|
||||
"global_official_account_reconciliation": false
|
||||
},
|
||||
"compatibility_group": {
|
||||
"included_prior_QA": "d134a92d169f9159a692ed158e3a051889f7ab3a; receipt6ededb59",
|
||||
"candidate_contains_carry_increment": true,
|
||||
"combined_API_case": "1408-12economic-8currentInternational-50priorInternational-200currentPEC-100priorPEC-500PPC=538MZN payable simulated",
|
||||
"previous_carry_history_preserved": "550 MZN prior simulated result retained unchanged",
|
||||
"changed_allocation": "economic priority checks remaining collecta but keeps user prior/current allocations; over-allocation becomes pending rather than reduced automatically",
|
||||
"installation": false
|
||||
},
|
||||
"restriction_origins": {
|
||||
"Tempo": "next economic increment, distinction from international, no duplicate same parcel, preserve approved evidence/history; compatible grouping allowed",
|
||||
"accepted_requirement": "CIRPC64(1–2),53(1)a,40(1) amended by20/2009; RIRPC21(2/5)",
|
||||
"implementation_choices": "optional versioned detail; normal resident general2026/MZN scope; documentary inclusion/gross-up/proportional quota; optional parcel annotations on existing proof types; no additional organizational authority"
|
||||
},
|
||||
"verification": {
|
||||
"economic_new_frontend_tests": 20,
|
||||
"previous_affected_tests": 65,
|
||||
"frontend_pass": 85,
|
||||
"frontend_fail": 0,
|
||||
"API_tests": 1,
|
||||
"API_assertions": 210,
|
||||
"API_cases": 11,
|
||||
"database": "proactive_investment_20261027_test",
|
||||
"loopback_port": 55441,
|
||||
"API_transactions_rolled_back": true,
|
||||
"recovered_cases_recalculated_with_HTML_and_dossier": 11,
|
||||
"frozen_histories_preserved": true,
|
||||
"typescript": "passed",
|
||||
"build": "isolated QA, subprocess exit0, final component hashes recorded",
|
||||
"PHP_formatting": "pint passed",
|
||||
"new_browser_manual_proof": false,
|
||||
"team_usability_evaluation": "unchanged; awaiting team feedback"
|
||||
},
|
||||
"preservation": {
|
||||
"protected_files_same_git_content": 558,
|
||||
"raw_checkout_difference": {
|
||||
"kind": "LF/CRLF environment difference only, no semantic loss",
|
||||
"files": [
|
||||
"docs/investmoz/simulations/irpc-l8-carry-qa-20261028.json"
|
||||
]
|
||||
},
|
||||
"source_originals_same_bytes": 31,
|
||||
"existing_QA_records": {
|
||||
"database": "proactive_investment_20261027_test",
|
||||
"tables": {
|
||||
"opportunities": {
|
||||
"existing_rows": 55,
|
||||
"unchanged": true
|
||||
},
|
||||
"documents": {
|
||||
"existing_rows": 64,
|
||||
"unchanged": true
|
||||
},
|
||||
"opportunity_documents": {
|
||||
"existing_rows": 1,
|
||||
"unchanged": true
|
||||
},
|
||||
"simulation_form_drafts": {
|
||||
"existing_rows": 19,
|
||||
"unchanged": true
|
||||
},
|
||||
"users": {
|
||||
"existing_rows": 40,
|
||||
"unchanged": true
|
||||
},
|
||||
"organizations": {
|
||||
"existing_rows": 3,
|
||||
"unchanged": true
|
||||
},
|
||||
"audit_events": {
|
||||
"existing_rows": 409,
|
||||
"unchanged": true
|
||||
}
|
||||
},
|
||||
"transaction_rollback": true
|
||||
},
|
||||
"existing_matrix": "only AUD-SIM-INT02 evidence updated; other records unchanged semantically",
|
||||
"old_carry_receipt_and_fixture": "unchanged",
|
||||
"schema_permissions_configuration": "no schema/configuration/migration/grant changes; no new live comparison claimed",
|
||||
"active_data_writes": false,
|
||||
"new_accounts": false,
|
||||
"new_imports_or_publication": false,
|
||||
"remote_operations": false,
|
||||
"Songo_Gurue_SGF_TEMPO": "untouched"
|
||||
},
|
||||
"cases": [
|
||||
{
|
||||
"name": "payable",
|
||||
"status": "payable",
|
||||
"balance": {
|
||||
"numerator": "688",
|
||||
"denominator": "1"
|
||||
},
|
||||
"economicDeduction": {
|
||||
"numerator": "12",
|
||||
"denominator": "1"
|
||||
},
|
||||
"apiRecovered": true,
|
||||
"recalculationEqual": true,
|
||||
"html": true,
|
||||
"dossier": true,
|
||||
"frozenHistoryPreserved": true
|
||||
},
|
||||
{
|
||||
"name": "combined",
|
||||
"status": "payable",
|
||||
"balance": {
|
||||
"numerator": "538",
|
||||
"denominator": "1"
|
||||
},
|
||||
"economicDeduction": {
|
||||
"numerator": "12",
|
||||
"denominator": "1"
|
||||
},
|
||||
"apiRecovered": true,
|
||||
"recalculationEqual": true,
|
||||
"html": true,
|
||||
"dossier": true,
|
||||
"frozenHistoryPreserved": true
|
||||
},
|
||||
{
|
||||
"name": "quota",
|
||||
"status": "payable",
|
||||
"balance": {
|
||||
"numerator": "695",
|
||||
"denominator": "1"
|
||||
},
|
||||
"economicDeduction": {
|
||||
"numerator": "5",
|
||||
"denominator": "1"
|
||||
},
|
||||
"apiRecovered": true,
|
||||
"recalculationEqual": true,
|
||||
"html": true,
|
||||
"dossier": true,
|
||||
"frozenHistoryPreserved": true
|
||||
},
|
||||
{
|
||||
"name": "prior-use",
|
||||
"status": "payable",
|
||||
"balance": {
|
||||
"numerator": "691",
|
||||
"denominator": "1"
|
||||
},
|
||||
"economicDeduction": {
|
||||
"numerator": "9",
|
||||
"denominator": "1"
|
||||
},
|
||||
"apiRecovered": true,
|
||||
"recalculationEqual": true,
|
||||
"html": true,
|
||||
"dossier": true,
|
||||
"frozenHistoryPreserved": true
|
||||
},
|
||||
{
|
||||
"name": "duplicate",
|
||||
"status": "indeterminate",
|
||||
"balance": null,
|
||||
"economicDeduction": null,
|
||||
"apiRecovered": true,
|
||||
"recalculationEqual": true,
|
||||
"html": true,
|
||||
"dossier": true,
|
||||
"frozenHistoryPreserved": true
|
||||
},
|
||||
{
|
||||
"name": "missing-gross-up",
|
||||
"status": "indeterminate",
|
||||
"balance": null,
|
||||
"economicDeduction": null,
|
||||
"apiRecovered": true,
|
||||
"recalculationEqual": true,
|
||||
"html": true,
|
||||
"dossier": true,
|
||||
"frozenHistoryPreserved": true
|
||||
},
|
||||
{
|
||||
"name": "article40",
|
||||
"status": "indeterminate",
|
||||
"balance": null,
|
||||
"economicDeduction": null,
|
||||
"apiRecovered": true,
|
||||
"recalculationEqual": true,
|
||||
"html": true,
|
||||
"dossier": true,
|
||||
"frozenHistoryPreserved": true
|
||||
},
|
||||
{
|
||||
"name": "shared-paper",
|
||||
"status": "payable",
|
||||
"balance": {
|
||||
"numerator": "688",
|
||||
"denominator": "1"
|
||||
},
|
||||
"economicDeduction": {
|
||||
"numerator": "12",
|
||||
"denominator": "1"
|
||||
},
|
||||
"apiRecovered": true,
|
||||
"recalculationEqual": true,
|
||||
"html": true,
|
||||
"dossier": true,
|
||||
"frozenHistoryPreserved": true
|
||||
},
|
||||
{
|
||||
"name": "partial-parcel",
|
||||
"status": "indeterminate",
|
||||
"balance": null,
|
||||
"economicDeduction": null,
|
||||
"apiRecovered": true,
|
||||
"recalculationEqual": true,
|
||||
"html": true,
|
||||
"dossier": true,
|
||||
"frozenHistoryPreserved": true
|
||||
},
|
||||
{
|
||||
"name": "fractional",
|
||||
"status": "payable",
|
||||
"balance": {
|
||||
"numerator": "349997",
|
||||
"denominator": "500"
|
||||
},
|
||||
"economicDeduction": {
|
||||
"numerator": "3",
|
||||
"denominator": "500"
|
||||
},
|
||||
"apiRecovered": true,
|
||||
"recalculationEqual": true,
|
||||
"html": true,
|
||||
"dossier": true,
|
||||
"frozenHistoryPreserved": true
|
||||
},
|
||||
{
|
||||
"name": "recoverable",
|
||||
"status": "recoverable",
|
||||
"balance": {
|
||||
"numerator": "-500",
|
||||
"denominator": "1"
|
||||
},
|
||||
"economicDeduction": {
|
||||
"numerator": "12",
|
||||
"denominator": "1"
|
||||
},
|
||||
"apiRecovered": true,
|
||||
"recalculationEqual": true,
|
||||
"html": true,
|
||||
"dossier": true,
|
||||
"frozenHistoryPreserved": true
|
||||
}
|
||||
],
|
||||
"remaining": {
|
||||
"economic": [
|
||||
"automatic inclusion/correction of distributed profits and53 addition in L6",
|
||||
"automatic proportional ceiling derivation and complete40(1) exclusion treatment at base stage",
|
||||
"liquidation64(3),associations64(4),special-period and other-currency cases"
|
||||
],
|
||||
"other_L8": [
|
||||
"specific benefit credits and their qualification,cumulation and limits",
|
||||
"automatic international national-fraction and case-specific treaty matching",
|
||||
"PPC/PEC obligation/calendar,interest/penalties and payment guides with applicable edition",
|
||||
"autonomous taxation,special periods,other currencies and M20/M22 transport/submission",
|
||||
"exceptional/transitional interyear cases beyond accepted ordinary carry scope"
|
||||
]
|
||||
},
|
||||
"L8_complete": false,
|
||||
"evidence": {
|
||||
"api-result.json": "124fd50cb6d223cdd18fedc166d00af7254199b6e6dedf9be82f71db87af6d01",
|
||||
"api.log": "e5cb58ba25c82feb1a14a8259cd112a58455b8cc1c17d8540b08ec30b87583c7",
|
||||
"build-result.json": "256bb54e1d47ec73de37810b9673f10464c65fb970ea3b7ae28bbea36ba222e3",
|
||||
"build.log": "69f17893b47b8d748651933f6a88a5f4206d58173397c1695d58472db9a26cf6",
|
||||
"existing-records-preservation.json": "b45b3306d3c9dee63fbbaf9a04bba31008019e65892bd16322460b5ef53eda7d",
|
||||
"frontend-result.json": "f73293b96cda7c81c86a6716a04a18d632e10361bf7d3f616695dbdc7695aeb0",
|
||||
"frontend.log": "ff784389cda3f2c232f4760f74c053bc16b6d134ef975d0aad13132373e5e275",
|
||||
"pint-result.json": "124fd50cb6d223cdd18fedc166d00af7254199b6e6dedf9be82f71db87af6d01",
|
||||
"pint.log": "cd1a94fc2cf6a965b86e1a4809d6c7fb9148b1ee374e1010ed2ac96ff4876ec2",
|
||||
"protected-consumers.json": "172542788e44fe604ca90d95a71d87f47a3baeae0c61950bc50d342658aa82ad",
|
||||
"recovered-report-receipt.json": "ecce91a9c00c3a88d234486c020b5aa1681928eb1b1aeaeef0e7a569a2e17654",
|
||||
"source-preservation.json": "6aceebe1cf883ca709ccbbbef7f2a756ee5dc51c12b3281c3659e83abfd90b70",
|
||||
"types-result.json": "256bb54e1d47ec73de37810b9673f10464c65fb970ea3b7ae28bbea36ba222e3",
|
||||
"types.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"qa_commit": "aae67c169dc0d14f95cf2ee6fa28e5ff05341dcf",
|
||||
"qa_branch_pushed": false,
|
||||
"grouped_delivery_manifest": "outputs/irpc-l8-economic-20261028/grouped-delivery-prepared.json"
|
||||
}
|
||||
@@ -0,0 +1,722 @@
|
||||
{
|
||||
"date": "2026-10-27",
|
||||
"status": "installed_and_verified",
|
||||
"served_commit": "838ad941a24ae76ec4bf91ccc3f92012668fe8cf",
|
||||
"accepted_qa": "838ad941a24ae76ec4bf91ccc3f92012668fe8cf",
|
||||
"previous_commit": "e182d1d79dc945f35d2079c6b8ca2ac93048cec3",
|
||||
"sector_fix_preserved": "669cb8979b0c372659fc2ad3876aebc919d5df19",
|
||||
"scope": {
|
||||
"year": "2026",
|
||||
"resident": "general",
|
||||
"period": "calendar year",
|
||||
"currency": "documented MZN",
|
||||
"foreign_tax": "actual income tax paid; inclusion in taxable base documented",
|
||||
"national_fraction": "documented exact input, not automatically derived",
|
||||
"treaty": "case-specific applicability and ceiling documented, not inferred from country",
|
||||
"order": [
|
||||
"international credit limited to eligible available amount and collection",
|
||||
"PEC limited to remaining collection",
|
||||
"withholdings and PPC"
|
||||
],
|
||||
"known_absent_missing_distinct": true,
|
||||
"proof_deduplication": "issuer/document across tax natures, dates and snapshot contexts; consolidate country-year quota",
|
||||
"officialBalancesConsumed": false,
|
||||
"carryCreated": false
|
||||
},
|
||||
"example": {
|
||||
"foreign_tax_paid": "10 MZN",
|
||||
"documented_national_limit": "8 MZN",
|
||||
"documented_treaty_nonapplication": "synthetic only, not actual country entitlement",
|
||||
"credit": "8 MZN",
|
||||
"calculation": "1408 - 8 = 1400; - PEC200 = 1200; - PPC500 = 700 MZN simulated payable",
|
||||
"above_eligibility_limit": "2 MZN, not a carry",
|
||||
"insufficient_collection_example": "available2000, deducted1408, insufficient592; PEC200 not deducted; recoverable500 derives from PPC only"
|
||||
},
|
||||
"accepted_QA_reused": {
|
||||
"frontend_new": 18,
|
||||
"frontend_affected_annual_pec": 31,
|
||||
"api_assertions": 171,
|
||||
"recovered_HTML_dossier_cases": 7
|
||||
},
|
||||
"cases": [
|
||||
{
|
||||
"name": "payable",
|
||||
"status": "payable",
|
||||
"balance": {
|
||||
"numerator": "700",
|
||||
"denominator": "1"
|
||||
},
|
||||
"current": true,
|
||||
"history_records": 0,
|
||||
"apiRoundTrip": true,
|
||||
"dossier": true
|
||||
},
|
||||
{
|
||||
"name": "treaty",
|
||||
"status": "payable",
|
||||
"balance": {
|
||||
"numerator": "702",
|
||||
"denominator": "1"
|
||||
},
|
||||
"current": true,
|
||||
"history_records": 1,
|
||||
"apiRoundTrip": true,
|
||||
"dossier": true
|
||||
},
|
||||
{
|
||||
"name": "missing",
|
||||
"status": "indeterminate",
|
||||
"balance": null,
|
||||
"current": true,
|
||||
"history_records": 1,
|
||||
"apiRoundTrip": true,
|
||||
"dossier": true
|
||||
},
|
||||
{
|
||||
"name": "limited-collecta",
|
||||
"status": "recoverable",
|
||||
"balance": {
|
||||
"numerator": "-500",
|
||||
"denominator": "1"
|
||||
},
|
||||
"current": true,
|
||||
"history_records": 1,
|
||||
"apiRoundTrip": true,
|
||||
"dossier": true
|
||||
},
|
||||
{
|
||||
"name": "duplicate",
|
||||
"status": "indeterminate",
|
||||
"balance": null,
|
||||
"current": true,
|
||||
"history_records": 1,
|
||||
"apiRoundTrip": true,
|
||||
"dossier": true
|
||||
},
|
||||
{
|
||||
"name": "absent",
|
||||
"status": "payable",
|
||||
"balance": {
|
||||
"numerator": "708",
|
||||
"denominator": "1"
|
||||
},
|
||||
"current": true,
|
||||
"history_records": 1,
|
||||
"apiRoundTrip": true,
|
||||
"dossier": true
|
||||
},
|
||||
{
|
||||
"name": "prior-use",
|
||||
"status": "payable",
|
||||
"balance": {
|
||||
"numerator": "703",
|
||||
"denominator": "1"
|
||||
},
|
||||
"current": true,
|
||||
"history_records": 1,
|
||||
"apiRoundTrip": true,
|
||||
"dossier": true
|
||||
}
|
||||
],
|
||||
"compatibility": {
|
||||
"fast_forward_over": "e182d1d79dc945f35d2079c6b8ca2ac93048cec3",
|
||||
"runtime_files_byte_identical_to_accepted_QA": true,
|
||||
"runtime_delta": [
|
||||
{
|
||||
"file": "resources/js/components/simulation-irpc-annual.tsx",
|
||||
"before_sha256": "7f18abcaa1100251c5c2780fdfac32e21979d8e6ffed52f732e4a0d345ab4f40",
|
||||
"after_sha256": "307725306df321fc89caf3d1a9efcf7463bb93d9d515d44347f8e14d4fd3136b"
|
||||
},
|
||||
{
|
||||
"file": "resources/js/components/simulation-irpc-international.tsx",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "6a9f796cc1e678823ed1dc14fbc28ca0e270752b2650518de239424dc75d167c"
|
||||
},
|
||||
{
|
||||
"file": "resources/js/lib/country-iso-codes.ts",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "1c4e2b6d67457ea2dba3dda28a9b6ce09242c36dfcf04855a8bc1f44506a9fac"
|
||||
},
|
||||
{
|
||||
"file": "resources/js/lib/irpc-annual-settlement.ts",
|
||||
"before_sha256": "6d80889677c1904b3236b5e43fa5a3dc28327745679392529dfb5fbf5a273f7d",
|
||||
"after_sha256": "47d4167d0f80c649e6d89a586e0d206d2d2a12b8553a7e98be362ea56203a540"
|
||||
},
|
||||
{
|
||||
"file": "resources/js/lib/irpc-international-credit.ts",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "40b46fbaaff6071ce6a35fd82de586158bf1f58fae8f46bcf6e4ddbbf84287f1"
|
||||
}
|
||||
],
|
||||
"all_other_compared_sources_unchanged": true,
|
||||
"schema_config_routes_permissions_unchanged": true,
|
||||
"legacy_annual_and_PEC_history_preserved": true,
|
||||
"no_new_manual_retest_of_independent_accepted_proofs": true
|
||||
},
|
||||
"source_files_verified": {
|
||||
"before": 1002,
|
||||
"after": 1005
|
||||
},
|
||||
"backup": {
|
||||
"stage": "/var/backups/investmoz-irpc-l8-international-20261027/backup",
|
||||
"created_at": "2026-10-27T13:15:39Z",
|
||||
"target": {
|
||||
"database": "teste_investidor",
|
||||
"port": 5432
|
||||
},
|
||||
"tables": 174,
|
||||
"files_verified": 369,
|
||||
"dump_sha256": "b1e9740653a8664c23268e228efd3ed6a0514bf3e8786dd5adf43342cdb475e1",
|
||||
"files_sha256": "9d3a790f9c7f4a35ebf660d304e92d69f86557d20d2f8be1e996c828711b19ac",
|
||||
"restore_verified": false
|
||||
},
|
||||
"restore": {
|
||||
"restore_verified": true,
|
||||
"tables_verified": 174,
|
||||
"container_network": "none",
|
||||
"database": "irpc_international_restore_20261027",
|
||||
"port": 55441,
|
||||
"source_dump_sha256": "b1e9740653a8664c23268e228efd3ed6a0514bf3e8786dd5adf43342cdb475e1",
|
||||
"active_database_writes": 0
|
||||
},
|
||||
"capacity_before": {
|
||||
"checked_at_UTC": "2026-10-27T13:15:02.501911+00:00",
|
||||
"installed": {
|
||||
"status": "installed",
|
||||
"commit": "e182d1d79dc945f35d2079c6b8ca2ac93048cec3",
|
||||
"release": "/var/www/investmoz-homologacao/releases/e182d1d79dc945f35d2079c6b8ca2ac93048cec3-e2a185b3",
|
||||
"previous_release": "/var/www/investmoz-homologacao/releases/669cb8979b0c372659fc2ad3876aebc919d5df19-6a2f00c6",
|
||||
"database_migrations": 0,
|
||||
"content_imports": 0
|
||||
},
|
||||
"current_link": "/var/www/investmoz-homologacao/releases/e182d1d79dc945f35d2079c6b8ca2ac93048cec3-e2a185b3",
|
||||
"release_revision": {
|
||||
"commit": "e182d1d79dc945f35d2079c6b8ca2ac93048cec3",
|
||||
"branch": "main",
|
||||
"built_at": "2026-10-27T12:10:18.923517+00:00",
|
||||
"manifest_sha256": "47bbdd09864d0587133deec179781018e439f9c83b7b328c9dcd52482eaf6d31"
|
||||
},
|
||||
"service": "MainPID=0\nExecMainStatus=0\nActiveState=inactive\nSubState=dead\n",
|
||||
"volumes": [
|
||||
{
|
||||
"path": "/var/www/investmoz-homologacao/releases",
|
||||
"mount": "/dev/md2 / ext4",
|
||||
"device": 2306,
|
||||
"available_bytes": 92293206016,
|
||||
"available_inodes": 23699087,
|
||||
"total_inodes": 29089792
|
||||
},
|
||||
{
|
||||
"path": "/var/lib/investmoz-deploy",
|
||||
"mount": "/dev/md2 / ext4",
|
||||
"device": 2306,
|
||||
"available_bytes": 92293206016,
|
||||
"available_inodes": 23699087,
|
||||
"total_inodes": 29089792
|
||||
},
|
||||
{
|
||||
"path": "/var/backups",
|
||||
"mount": "/dev/md2 / ext4",
|
||||
"device": 2306,
|
||||
"available_bytes": 92293206016,
|
||||
"available_inodes": 23699087,
|
||||
"total_inodes": 29089792
|
||||
},
|
||||
{
|
||||
"path": "/tmp",
|
||||
"mount": "/dev/md2 / ext4",
|
||||
"device": 2306,
|
||||
"available_bytes": 92293206016,
|
||||
"available_inodes": 23699087,
|
||||
"total_inodes": 29089792
|
||||
},
|
||||
{
|
||||
"path": "/var/lib/docker",
|
||||
"mount": "/dev/md2 / ext4",
|
||||
"device": 2306,
|
||||
"available_bytes": 92293206016,
|
||||
"available_inodes": 23699087,
|
||||
"total_inodes": 29089792
|
||||
}
|
||||
],
|
||||
"free_bytes": 92293206016,
|
||||
"existing_pipeline_minimum_bytes": 5368709120,
|
||||
"observed_previous_backup_bytes": 175920446,
|
||||
"same_partition_release_and_backup": true,
|
||||
"space_guard_satisfied": true,
|
||||
"current_release_entry_reference": 31315,
|
||||
"inodes_available_vs_reference": true,
|
||||
"inode_note": "Observed current release footprint for scale;no new arbitrary inode threshold. Capacity rechecked after fresh backup.",
|
||||
"pipeline_sha256": "adec1c17654f3a8658b83b134f1e75f95391b235738552721906c3236e388604",
|
||||
"active_writes": 0
|
||||
},
|
||||
"capacity_after_backup": {
|
||||
"at": "2026-10-27T13:16:13.360261+00:00",
|
||||
"baseline": "e182d1d79dc945f35d2079c6b8ca2ac93048cec3",
|
||||
"capacity_after_backup_satisfied": true,
|
||||
"volumes": [
|
||||
{
|
||||
"path": "/var/www/investmoz-homologacao/releases",
|
||||
"free_bytes": 92114915328,
|
||||
"free_inodes": 23699075
|
||||
},
|
||||
{
|
||||
"path": "/var/lib/investmoz-deploy",
|
||||
"free_bytes": 92114915328,
|
||||
"free_inodes": 23699075
|
||||
},
|
||||
{
|
||||
"path": "/var/backups",
|
||||
"free_bytes": 92114915328,
|
||||
"free_inodes": 23699075
|
||||
},
|
||||
{
|
||||
"path": "/tmp",
|
||||
"free_bytes": 92114915328,
|
||||
"free_inodes": 23699075
|
||||
},
|
||||
{
|
||||
"path": "/var/lib/docker",
|
||||
"free_bytes": 92114915328,
|
||||
"free_inodes": 23699075
|
||||
}
|
||||
],
|
||||
"backup_age_seconds": 34.36022,
|
||||
"backup_dump_sha256": "b1e9740653a8664c23268e228efd3ed6a0514bf3e8786dd5adf43342cdb475e1",
|
||||
"restore_verified": true,
|
||||
"tables_verified": 174,
|
||||
"files_verified": 369,
|
||||
"backup_paths": {
|
||||
"dump": "/var/backups/investmoz-irpc-l8-international-20261027/backup/database.dump",
|
||||
"files": "/var/backups/investmoz-irpc-l8-international-20261027/backup/files-before.tar.gz"
|
||||
},
|
||||
"active_writes": 0
|
||||
},
|
||||
"schema_permissions": {
|
||||
"schema_sha256": "5045b3e51d2185377aa3d9ac7fa07a44fad3ec620cdfa83ffe31c8884ee1601d",
|
||||
"roles_grants_sha256": "1530c33cb016e67bfdc64891ddfce7f185b13b56b2cd87c012871a685e118109",
|
||||
"filesystem_permissions": {
|
||||
".env": {
|
||||
"mode": 416,
|
||||
"uid": 1001,
|
||||
"gid": 33
|
||||
},
|
||||
"storage": {
|
||||
"mode": 1533,
|
||||
"uid": 1001,
|
||||
"gid": 33
|
||||
},
|
||||
"storage/app": {
|
||||
"mode": 1533,
|
||||
"uid": 1001,
|
||||
"gid": 33
|
||||
},
|
||||
"bootstrap/cache": {
|
||||
"mode": 1528,
|
||||
"uid": 1001,
|
||||
"gid": 33
|
||||
}
|
||||
},
|
||||
"comparison_identity": "postgres;catalogue read only;no passwords collected",
|
||||
"unchanged": true
|
||||
},
|
||||
"preservation": {
|
||||
"checked_at": "2026-10-27T13:21:25.836367+00:00",
|
||||
"installed": "838ad941a24ae76ec4bf91ccc3f92012668fe8cf",
|
||||
"business_tables_unchanged": 172,
|
||||
"total_tables": 174,
|
||||
"operational_tables_to_reconcile": {
|
||||
"cache": {
|
||||
"before": {
|
||||
"rows": 108,
|
||||
"checksum": "c5e94e4bb71bf085e36447c1da35696e"
|
||||
},
|
||||
"after": {
|
||||
"rows": 108,
|
||||
"checksum": "ddbdb9e11a50777bd84bc353d178608a"
|
||||
}
|
||||
},
|
||||
"sessions": {
|
||||
"before": {
|
||||
"rows": 53,
|
||||
"checksum": "fe52dca3e79f71d1f5a3bd474c7d72d1"
|
||||
},
|
||||
"after": {
|
||||
"rows": 55,
|
||||
"checksum": "359f4b80ea80b807a1f3a50687fe2309"
|
||||
}
|
||||
}
|
||||
},
|
||||
"persistent_files_unchanged": 369,
|
||||
"original_configuration_preserved": true,
|
||||
"synthetic_QA_storage_not_served": true,
|
||||
"QA_storage_saved_outside_active": "/var/lib/investmoz-deploy/qa-runtime/838ad941a24ae76ec4bf91ccc3f92012668fe8cf-ca7817df",
|
||||
"fixture_or_content_imports": 0,
|
||||
"migrations": 0,
|
||||
"comparison_identity": "postgres;UTC;consistent before snapshot and after read-only transaction",
|
||||
"active_database_writes": 0
|
||||
},
|
||||
"operational_delta": {
|
||||
"status": "operational_delta_reconciled",
|
||||
"comparison_identity": "postgres before/after;isolated restored baseline;no RLS visibility inference",
|
||||
"cache": "queue:restart;requestthrottle;identical home-sector cached value with refreshed expiry(HomeController remember10min);no deletedcache",
|
||||
"sessions": "anonymous sessions from delivery user-agent or exactUA/time correlated with nginx public request;deleted sessions individually expired beyond configured lifetime,normal database session garbage collection(config/session lottery2/100)",
|
||||
"delta": {
|
||||
"cache": {
|
||||
"before_rows": 108,
|
||||
"after_rows": 108,
|
||||
"changes": [
|
||||
{
|
||||
"key_sha256": "7f7dd83f47f5bf404d55e6e9f1367948721579970b195680645e356031a070ff",
|
||||
"kind": "updated",
|
||||
"fields": [
|
||||
"value",
|
||||
"expiration"
|
||||
],
|
||||
"key_pattern": "investemoz-cache-illuminate:queue:restart",
|
||||
"is_queue_restart_key": true,
|
||||
"expiration_unchanged": false,
|
||||
"throttle_timer_pair": false,
|
||||
"same_value_home_cache_expiry_refresh": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"sessions": {
|
||||
"before_rows": 53,
|
||||
"after_rows": 55,
|
||||
"changes": [
|
||||
{
|
||||
"key_sha256": "cc9234289e8c94733fb352f52407a2863d547165e1ee7f6eb4523c1ed7b33c1a",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "dbf3b24ccc55e4bf0a14aad872309de7d890942acb4f68b0c1ec608ff5e49035",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"business_tables_to_confirm_separately": 172,
|
||||
"active_data_writes_by_comparison": 0,
|
||||
"restore_network": "none"
|
||||
},
|
||||
"installer": {
|
||||
"service": "MainPID=0\nExecMainStatus=0\nActiveState=inactive\nSubState=dead\n",
|
||||
"installed": {
|
||||
"status": "installed",
|
||||
"commit": "838ad941a24ae76ec4bf91ccc3f92012668fe8cf",
|
||||
"release": "/var/www/investmoz-homologacao/releases/838ad941a24ae76ec4bf91ccc3f92012668fe8cf-ca7817df",
|
||||
"previous_release": "/var/www/investmoz-homologacao/releases/e182d1d79dc945f35d2079c6b8ca2ac93048cec3-e2a185b3",
|
||||
"database_migrations": 0,
|
||||
"content_imports": 0
|
||||
},
|
||||
"latest_report": {
|
||||
"status": "installed",
|
||||
"commit": "838ad941a24ae76ec4bf91ccc3f92012668fe8cf",
|
||||
"release": "/var/www/investmoz-homologacao/releases/838ad941a24ae76ec4bf91ccc3f92012668fe8cf-ca7817df",
|
||||
"previous_release": "/var/www/investmoz-homologacao/releases/e182d1d79dc945f35d2079c6b8ca2ac93048cec3-e2a185b3",
|
||||
"database_migrations": 0,
|
||||
"content_imports": 0,
|
||||
"requested_commits": [
|
||||
"838ad941a24ae76ec4bf91ccc3f92012668fe8cf"
|
||||
],
|
||||
"finished_at": "2026-10-27T13:20:20.796156+00:00"
|
||||
},
|
||||
"log_file": "1793107006281021543.log",
|
||||
"log_bytes": 313361,
|
||||
"mandatory_summaries": [
|
||||
"OK (1 test, 3106 assertions)",
|
||||
"✓ built in 31.55s",
|
||||
"OK (59 tests, 317 assertions)",
|
||||
"# tests 1110",
|
||||
"# pass 1110",
|
||||
"# fail 0"
|
||||
]
|
||||
},
|
||||
"public_delta": {
|
||||
"phase": "after",
|
||||
"revision": {
|
||||
"commit": "838ad941a24ae76ec4bf91ccc3f92012668fe8cf",
|
||||
"branch": "main",
|
||||
"built_at": "2026-10-27T13:20:18.055286+00:00",
|
||||
"manifest_sha256": "b873c14f3e0e43435fa8f67fa9a65ff8978123cd8792c5f7c2e0cf6f8f84a3b2"
|
||||
},
|
||||
"manifest_verified": true,
|
||||
"simulator_HTTP": 200,
|
||||
"simulator_component": "portal/services",
|
||||
"active_QA_records_created": false,
|
||||
"compiled_markers": {
|
||||
"IRPC-international-credit/1": [
|
||||
"assets/project-dossier-bSv3Ugr2.js"
|
||||
],
|
||||
"Preparar crédito internacional": [
|
||||
"assets/services-YUy8XgPr.js"
|
||||
],
|
||||
"Vincular inclusão e limites ao país": [
|
||||
"assets/services-YUy8XgPr.js"
|
||||
],
|
||||
"Crédito internacional — antes do PEC": [
|
||||
"assets/project-dossier-bSv3Ugr2.js"
|
||||
],
|
||||
"Não determina a fracção nacional": [
|
||||
"assets/project-dossier-bSv3Ugr2.js"
|
||||
],
|
||||
"IRPC-PEC/1": [
|
||||
"assets/project-dossier-bSv3Ugr2.js"
|
||||
]
|
||||
}
|
||||
},
|
||||
"remote_backup": "/var/backups/investmoz-irpc-l8-international-20261027/backup",
|
||||
"remote_receipt": "/var/backups/investmoz-irpc-l8-international-20261027/installed-receipt.json",
|
||||
"limits": [
|
||||
"L8 partial",
|
||||
"no new PEC excess/carry scope",
|
||||
"no automatic foreign exchange or official rounding rule",
|
||||
"no official tax balance consumption, submission or refund execution",
|
||||
"same-snapshot deduplication is not a global inter-dossier official ledger",
|
||||
"synthetic QA examples do not assert any actual foreign treaty",
|
||||
"L8 partial after this bounded delivery",
|
||||
"Songo/Gurue remain private; no QA fixture or content import into active data"
|
||||
],
|
||||
"remaining_code": [
|
||||
"automatic national fraction and case-specific treaty matching",
|
||||
"economic double-tax and CBF benefit credits with own rules",
|
||||
"international carry/expiry65(3), separate from PEC66 carry/expiry",
|
||||
"PPC/PEC obligation/calendar; autonomous tax, special periods, other currencies and M20/M22 transport/submission"
|
||||
],
|
||||
"document_dependencies": [
|
||||
{
|
||||
"missing": "income inclusion and national fraction",
|
||||
"example": "tax10 but no included income/base correspondence",
|
||||
"resume_when": "income/base and documented fraction matched"
|
||||
},
|
||||
{
|
||||
"missing": "treaty article/period/ceiling",
|
||||
"example": "country selected but no applicable article or eligible tax",
|
||||
"resume_when": "case-specific article, effective period and eligible ceiling identified"
|
||||
},
|
||||
{
|
||||
"missing": "proof/titular/previous actual use/temporal link",
|
||||
"example": "payment2027 selected for2026 without correspondence",
|
||||
"resume_when": "actual payment and explicit2026 link supported, prior use reconciled"
|
||||
}
|
||||
],
|
||||
"local_startup_fix": {
|
||||
"cause": "Local inspect.py shadowed Python stdlib before SSH connection",
|
||||
"renamed_to": "remote_delivery_inspection.py",
|
||||
"inspect_module": "C:/laragon/bin/python/python-3.10/lib/inspect.py",
|
||||
"python": "C:/laragon/bin/python/python-3.10/python.exe",
|
||||
"paramiko": "5.1.0",
|
||||
"remote_linux_geteuid_ssh_guard_preserved": true,
|
||||
"credentials_changed": false,
|
||||
"server_failure_inferred": false
|
||||
},
|
||||
"local_evidence_directory": "outputs/irpc-l8-international-delivery-20261027",
|
||||
"local_artifacts": [
|
||||
{
|
||||
"file": "authenticate.ps1",
|
||||
"bytes": 237,
|
||||
"sha256": "89492b8a16b8d5ec8dbe1d6a1a44af0bfbf8e99ccef2abfc5568a831623c05ba"
|
||||
},
|
||||
{
|
||||
"file": "backup.py",
|
||||
"bytes": 5460,
|
||||
"sha256": "ef5406ffa735d3d563e9b4da0e8ac067c6978710df455d24e2c8e2cbc305fa49"
|
||||
},
|
||||
{
|
||||
"file": "capacity-after.py",
|
||||
"bytes": 1621,
|
||||
"sha256": "9f1e2df54963469c670098b77392020f2719bb3f795cd04e5ecdad6f1fad1b84"
|
||||
},
|
||||
{
|
||||
"file": "diagnose-session-delta.py",
|
||||
"bytes": 7395,
|
||||
"sha256": "312e2446a489a99bf9060797e785de4d16d28866b6f9a05a9d99fb60e3a9c838"
|
||||
},
|
||||
{
|
||||
"file": "int-backup-20261027.json",
|
||||
"bytes": 443,
|
||||
"sha256": "5314107e4438ea22bc6c3261dda9b893faac3cf131c596f30dc6517bc35cd30f"
|
||||
},
|
||||
{
|
||||
"file": "int-capacity-after-20261027.json",
|
||||
"bytes": 1210,
|
||||
"sha256": "72934b91dcc7ecf5370d7a667ef26ecf8a78a0ceeeeeb71a3062abab33925767"
|
||||
},
|
||||
{
|
||||
"file": "int-operational-20261027.json",
|
||||
"bytes": 2436,
|
||||
"sha256": "f8936062bdf569740ea4bafc618c88c08ef8a1def30dcf1240c6afa3113ec1c3"
|
||||
},
|
||||
{
|
||||
"file": "int-preflight-20261027.json",
|
||||
"bytes": 2595,
|
||||
"sha256": "cd339ef9913f8d89644e45ee5d337003cecb758d827764a79f4cfacc1b350dca"
|
||||
},
|
||||
{
|
||||
"file": "int-preserve-20261027.json",
|
||||
"bytes": 1139,
|
||||
"sha256": "110ec10adf5d2a4de426dd3e6f947026dbff6ef828a56c0353b1e7a0426e2c78"
|
||||
},
|
||||
{
|
||||
"file": "int-read-20261027.json",
|
||||
"bytes": 2919,
|
||||
"sha256": "12ba15343b9485e08f654a7abb0305e4f10f396b1d20f7aaca844e9ea5317fdc"
|
||||
},
|
||||
{
|
||||
"file": "int-restore-20261027.json",
|
||||
"bytes": 288,
|
||||
"sha256": "7d9c3fb60ea87407441db867edf8e872e58aeb9aaf209c1b89830b0e0eedea59"
|
||||
},
|
||||
{
|
||||
"file": "int-schema-after-20261027.json",
|
||||
"bytes": 668,
|
||||
"sha256": "e1767424312345a28a28223c67c424df1ac68d9d1aecec30a4856a71fbe91bfc"
|
||||
},
|
||||
{
|
||||
"file": "int-schema-before-20261027.json",
|
||||
"bytes": 646,
|
||||
"sha256": "da666867fc18a60b0473e04bcd7966328135dcb684fb974b405b66e53e5a4377"
|
||||
},
|
||||
{
|
||||
"file": "int-source-after-20261027.json",
|
||||
"bytes": 165,
|
||||
"sha256": "46c3a8f356197988ebbb4325a7a43807a0e5f097eb5eee5c09c063f68dc022cc"
|
||||
},
|
||||
{
|
||||
"file": "int-source-before-20261027.json",
|
||||
"bytes": 166,
|
||||
"sha256": "3130bda62b9b5b736d8b3a884c34973e2c7387564c1f3e9edfcf626075d4fbbd"
|
||||
},
|
||||
{
|
||||
"file": "int-status-a1-20261027.json",
|
||||
"bytes": 1442,
|
||||
"sha256": "c895209842c6f59f552a5b9af9542e0a4c58515d91213b188c1ee4300037b3ba"
|
||||
},
|
||||
{
|
||||
"file": "int-status-final-20261027.json",
|
||||
"bytes": 1795,
|
||||
"sha256": "2c57678c23eabf455295695214f9d87cca99be25245417310c12d25a6ac69e41"
|
||||
},
|
||||
{
|
||||
"file": "preflight.py",
|
||||
"bytes": 2328,
|
||||
"sha256": "2fccdc6dcd727db3b9599f5c4882b3ca389036ccbf12b91f2953d3789726e13c"
|
||||
},
|
||||
{
|
||||
"file": "preparation.json",
|
||||
"bytes": 247525,
|
||||
"sha256": "61d83eb63dd5f14d99cd0cce2f460150812b1831a27850c876a991d847b7febc"
|
||||
},
|
||||
{
|
||||
"file": "preserve-data.py",
|
||||
"bytes": 2304,
|
||||
"sha256": "e56d27b726c5e710b9bd5721d729cc019d57011b2ea1e2699fee6685f1f1bfbd"
|
||||
},
|
||||
{
|
||||
"file": "public-after.json",
|
||||
"bytes": 986,
|
||||
"sha256": "65a39cc9e37575d5da4731e185728d8762387d28abf34c7991c1f81b8c24479d"
|
||||
},
|
||||
{
|
||||
"file": "public-before.json",
|
||||
"bytes": 414,
|
||||
"sha256": "010725fedaa5b820efaea13d52140f07b450221f14d71624b6141fbdf5bb6b33"
|
||||
},
|
||||
{
|
||||
"file": "reconcile-operational-delta.py",
|
||||
"bytes": 5342,
|
||||
"sha256": "1fa90ff8496d2210dee1e01c0ee409abe0f4f1713f844ac99fa6f4b4c889e645"
|
||||
},
|
||||
{
|
||||
"file": "remote_delivery_inspection.py",
|
||||
"bytes": 1947,
|
||||
"sha256": "7c95ba88a9d5a62a15ec2bf581fa62f5151e548014d106f081487889e56b2703"
|
||||
},
|
||||
{
|
||||
"file": "restore.py",
|
||||
"bytes": 2828,
|
||||
"sha256": "fcfd6eb0adfff107ee9259b5825ac1afc550c9bd8eabf111fa0b1739d7dd3efd"
|
||||
},
|
||||
{
|
||||
"file": "schema-permissions-after.py",
|
||||
"bytes": 1868,
|
||||
"sha256": "d71436183c0cf121381e4119560d70f1bcaf850439830b822b728b6e86a04005"
|
||||
},
|
||||
{
|
||||
"file": "schema-permissions-before.py",
|
||||
"bytes": 1711,
|
||||
"sha256": "10c8acceabaebaeeb5d2e5161d37c3b7927b9f4e44e725e19f0a2eb738638b07"
|
||||
},
|
||||
{
|
||||
"file": "source-after-input.json",
|
||||
"bytes": 237267,
|
||||
"sha256": "b735f3c10108a40de67eba37cdac203329baea164fe8e48020087ee636462481"
|
||||
},
|
||||
{
|
||||
"file": "source-before-input.json",
|
||||
"bytes": 237268,
|
||||
"sha256": "3835b8ab18392212a54c4560be32807b5573b22378ff3403ecd10637ff81d59f"
|
||||
},
|
||||
{
|
||||
"file": "source-check.py",
|
||||
"bytes": 676,
|
||||
"sha256": "1cec620043b013e6b6f79b9f67e4e5588117d91ee7dc0c93592fc6b8d6d0557f"
|
||||
},
|
||||
{
|
||||
"file": "ssh-import-collision-resolution.json",
|
||||
"bytes": 411,
|
||||
"sha256": "a997a96b0ab7fcd9cb8797c788f4ec27b46df8e70a7008f59400a3af57b99286"
|
||||
},
|
||||
{
|
||||
"file": "ssh-session.py",
|
||||
"bytes": 3058,
|
||||
"sha256": "f506635c84dcc7c75c1b1cb244f8d1ffa98bcb6a1d906d6c70c5bfc5771bf177"
|
||||
},
|
||||
{
|
||||
"file": "status.py",
|
||||
"bytes": 1195,
|
||||
"sha256": "2282805a03dcbc4c82d5f8be3504c6408f49a5ddb63d7a3deeed1dbd650d021f"
|
||||
},
|
||||
{
|
||||
"file": "store-receipt.py",
|
||||
"bytes": 870,
|
||||
"sha256": "e407ff7db3b36fd554f6760dbd05c574c656770a18bf4631b430a8457293b6ea"
|
||||
},
|
||||
{
|
||||
"file": "update-docs.py",
|
||||
"bytes": 8322,
|
||||
"sha256": "be77002ecad1640fc1999a05d5a50584290e03294c2328cf403768005d4e2b35"
|
||||
},
|
||||
{
|
||||
"file": "verify-public.py",
|
||||
"bytes": 2037,
|
||||
"sha256": "df9aa7ec8b8581192abf8b3c726668205b4c50413005faa872a3fc8ecefbd5ad"
|
||||
}
|
||||
],
|
||||
"remote_receipt_sha256": "d80324b898fd160ab3e7a2fd0b11ea2decd0b99a9324255850062ee4a13e0f0f"
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
{
|
||||
"date": "2026-10-27",
|
||||
"status": "QA demonstrated; not installed",
|
||||
"branch": "codex/irpc-l8-international-credit-qa-20261027",
|
||||
"base": "814fe0a9",
|
||||
"installed_reference": "e182d1d79dc945f35d2079c6b8ca2ac93048cec3",
|
||||
"sector_fix_preserved": "669cb8979b0c372659fc2ad3876aebc919d5df19",
|
||||
"scope": {
|
||||
"year": "2026",
|
||||
"resident": "general",
|
||||
"period": "calendar year",
|
||||
"currency": "documented MZN",
|
||||
"foreign_tax": "actual income tax paid; inclusion in taxable base documented",
|
||||
"national_fraction": "documented exact input, not automatically derived",
|
||||
"treaty": "case-specific applicability and ceiling documented, not inferred from country",
|
||||
"order": [
|
||||
"international credit limited to eligible available amount and collection",
|
||||
"PEC limited to remaining collection",
|
||||
"withholdings and PPC"
|
||||
],
|
||||
"known_absent_missing_distinct": true,
|
||||
"proof_deduplication": "issuer/document across tax natures, dates and snapshot contexts; consolidate country-year quota",
|
||||
"officialBalancesConsumed": false,
|
||||
"carryCreated": false
|
||||
},
|
||||
"sources": {
|
||||
"reuse": "accepted CIRPC65(1–2), Lei34/2007 BR52 PDF18/printed768-(72); CIRPC66/RIRPC21 order",
|
||||
"originals_preserved": 30,
|
||||
"source_preservation_artifact": "outputs/irpc-l8-international-20261027/source-preservation.json",
|
||||
"automatic_treaty_entitlement": false
|
||||
},
|
||||
"verification": {
|
||||
"frontend_new": 18,
|
||||
"frontend_annual_pec_affected": 31,
|
||||
"frontend_pass": 49,
|
||||
"frontend_fail": 0,
|
||||
"api_tests": 1,
|
||||
"api_assertions": 171,
|
||||
"api_cases": 7,
|
||||
"api_path": "POST/PUT/GET private simulation drafts; DatabaseTransactions rollback in isolated guarded PostgreSQL55441",
|
||||
"recovered_reports": "fresh recalculation of all7 API recoveries; existing HTML and dossier finance report; frozen previous result",
|
||||
"wayfinder_routes_actions": "passed --with-form",
|
||||
"typescript": "passed",
|
||||
"isolated_build": "passed",
|
||||
"new_browser_manual_proof": false,
|
||||
"team_usability_evaluation": "awaiting team return; separate"
|
||||
},
|
||||
"cases": [
|
||||
{
|
||||
"name": "payable",
|
||||
"status": "payable",
|
||||
"balance": {
|
||||
"numerator": "700",
|
||||
"denominator": "1"
|
||||
},
|
||||
"current": true,
|
||||
"history_records": 0,
|
||||
"apiRoundTrip": true,
|
||||
"dossier": true
|
||||
},
|
||||
{
|
||||
"name": "treaty",
|
||||
"status": "payable",
|
||||
"balance": {
|
||||
"numerator": "702",
|
||||
"denominator": "1"
|
||||
},
|
||||
"current": true,
|
||||
"history_records": 1,
|
||||
"apiRoundTrip": true,
|
||||
"dossier": true
|
||||
},
|
||||
{
|
||||
"name": "missing",
|
||||
"status": "indeterminate",
|
||||
"balance": null,
|
||||
"current": true,
|
||||
"history_records": 1,
|
||||
"apiRoundTrip": true,
|
||||
"dossier": true
|
||||
},
|
||||
{
|
||||
"name": "limited-collecta",
|
||||
"status": "recoverable",
|
||||
"balance": {
|
||||
"numerator": "-500",
|
||||
"denominator": "1"
|
||||
},
|
||||
"current": true,
|
||||
"history_records": 1,
|
||||
"apiRoundTrip": true,
|
||||
"dossier": true
|
||||
},
|
||||
{
|
||||
"name": "duplicate",
|
||||
"status": "indeterminate",
|
||||
"balance": null,
|
||||
"current": true,
|
||||
"history_records": 1,
|
||||
"apiRoundTrip": true,
|
||||
"dossier": true
|
||||
},
|
||||
{
|
||||
"name": "absent",
|
||||
"status": "payable",
|
||||
"balance": {
|
||||
"numerator": "708",
|
||||
"denominator": "1"
|
||||
},
|
||||
"current": true,
|
||||
"history_records": 1,
|
||||
"apiRoundTrip": true,
|
||||
"dossier": true
|
||||
},
|
||||
{
|
||||
"name": "prior-use",
|
||||
"status": "payable",
|
||||
"balance": {
|
||||
"numerator": "703",
|
||||
"denominator": "1"
|
||||
},
|
||||
"current": true,
|
||||
"history_records": 1,
|
||||
"apiRoundTrip": true,
|
||||
"dossier": true
|
||||
}
|
||||
],
|
||||
"example": {
|
||||
"foreign_tax_paid": "10 MZN",
|
||||
"documented_national_limit": "8 MZN",
|
||||
"documented_treaty_nonapplication": "synthetic only, not actual country entitlement",
|
||||
"credit": "8 MZN",
|
||||
"calculation": "1408 - 8 = 1400; - PEC200 = 1200; - PPC500 = 700 MZN simulated payable",
|
||||
"above_eligibility_limit": "2 MZN, not a carry",
|
||||
"insufficient_collection_example": "available2000, deducted1408, insufficient592; PEC200 not deducted; recoverable500 derives from PPC only"
|
||||
},
|
||||
"preservation": {
|
||||
"existing_records": {
|
||||
"database": "proactive_investment_20261027_test",
|
||||
"tables": {
|
||||
"opportunities": {
|
||||
"existing_rows": 55,
|
||||
"unchanged": true
|
||||
},
|
||||
"documents": {
|
||||
"existing_rows": 64,
|
||||
"unchanged": true
|
||||
},
|
||||
"opportunity_documents": {
|
||||
"existing_rows": 1,
|
||||
"unchanged": true
|
||||
},
|
||||
"simulation_form_drafts": {
|
||||
"existing_rows": 19,
|
||||
"unchanged": true
|
||||
},
|
||||
"users": {
|
||||
"existing_rows": 40,
|
||||
"unchanged": true
|
||||
},
|
||||
"organizations": {
|
||||
"existing_rows": 3,
|
||||
"unchanged": true
|
||||
},
|
||||
"audit_events": {
|
||||
"existing_rows": 409,
|
||||
"unchanged": true
|
||||
}
|
||||
},
|
||||
"transaction_rollback": true
|
||||
},
|
||||
"originals": 30,
|
||||
"protected_consumers_count": 11,
|
||||
"no_migrations_or_permissions_changes": true,
|
||||
"legacy_summary_and_frozen_history_preserved": true,
|
||||
"private_pilots": "Songo/Gurue unchanged and private",
|
||||
"production_written": false,
|
||||
"SGF_TEMPO": "unchanged"
|
||||
},
|
||||
"remaining_code": [
|
||||
"automatic national fraction and case-specific treaty matching",
|
||||
"economic double-tax and CBF benefit credits with own rules",
|
||||
"international carry/expiry65(3), separate from PEC66 carry/expiry",
|
||||
"PPC/PEC obligation/calendar; autonomous tax, special periods, other currencies and M20/M22 transport/submission"
|
||||
],
|
||||
"document_dependencies": [
|
||||
{
|
||||
"missing": "income inclusion and national fraction",
|
||||
"example": "tax10 but no included income/base correspondence",
|
||||
"resume_when": "income/base and documented fraction matched"
|
||||
},
|
||||
{
|
||||
"missing": "treaty article/period/ceiling",
|
||||
"example": "country selected but no applicable article or eligible tax",
|
||||
"resume_when": "case-specific article, effective period and eligible ceiling identified"
|
||||
},
|
||||
{
|
||||
"missing": "proof/titular/previous actual use/temporal link",
|
||||
"example": "payment2027 selected for2026 without correspondence",
|
||||
"resume_when": "actual payment and explicit2026 link supported, prior use reconciled"
|
||||
}
|
||||
],
|
||||
"limits": [
|
||||
"L8 partial",
|
||||
"no new PEC excess/carry scope",
|
||||
"no automatic foreign exchange or official rounding rule",
|
||||
"no official tax balance consumption, submission or refund execution",
|
||||
"same-snapshot deduplication is not a global inter-dossier official ledger",
|
||||
"synthetic QA examples do not assert any actual foreign treaty",
|
||||
"no production deployment or publication of pilots"
|
||||
],
|
||||
"local_evidence": {
|
||||
"outputs\\irpc-l8-international-20261027\\frontend-tests.log": "7f981dc819ffe0947565994c9a29a72c408eb94a9cd687dba1d1b93903949b87",
|
||||
"outputs\\irpc-l8-international-20261027\\types.log": "82962651e47f564de653ad2883f8fd34e7dae4b4468e4ac459cb3331962a3879",
|
||||
"outputs\\irpc-l8-international-20261027\\build.log": "d9a173ef26392275e42f6ef8927c2c9a79039a16c98a371e8bbcccbeea17776b",
|
||||
"outputs\\irpc-l8-international-20261027\\existing-records-preservation.json": "b45b3306d3c9dee63fbbaf9a04bba31008019e65892bd16322460b5ef53eda7d",
|
||||
"outputs\\irpc-l8-international-20261027\\source-preservation.json": "d82c68f709793103373f2110b3b55ef226fd5e6a51a6835c62425c7407bcf0bd",
|
||||
"outputs\\irpc-l8-international-20261027\\recovered-report-receipt.json": "080d61ad1e3e4a849ea8b71642fb256881aa05a9fef81144cb7cf56848383cb2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,901 @@
|
||||
{
|
||||
"date": "2026-10-27",
|
||||
"status": "installed_and_verified",
|
||||
"served_commit": "e182d1d79dc945f35d2079c6b8ca2ac93048cec3",
|
||||
"includes_accepted_qa": "7fdb6b424b70ef6462a4f88338e52e2750b9e222",
|
||||
"previous_commit": "669cb8979b0c372659fc2ad3876aebc919d5df19",
|
||||
"sector_regression_preserved": "669cb897",
|
||||
"remote_backup": "/var/backups/investmoz-irpc-l8-pec-20261027/backup",
|
||||
"remote_receipt": "/var/backups/investmoz-irpc-l8-pec-20261027/installed-receipt.json",
|
||||
"scope": {
|
||||
"subject": "resident general",
|
||||
"year": "2026",
|
||||
"period": "calendar",
|
||||
"currency": "MZN",
|
||||
"treatment": "actual documented PEC of own exercise,selected available amount after prior use,capped to valid collecta before RF/PPC",
|
||||
"preceding_credits": "absent or documented known zero;positive/missing remains indeterminate",
|
||||
"no_automatic": [
|
||||
"PEC obligation",
|
||||
"prior-year carry",
|
||||
"expiry",
|
||||
"excess PEC refund",
|
||||
"official balance consumption",
|
||||
"special positive credits",
|
||||
"declaration submission"
|
||||
]
|
||||
},
|
||||
"accepted_qa_reused": {
|
||||
"new_PEC_tests": 12,
|
||||
"affected_annual_tests": 19,
|
||||
"api_assertions": 102,
|
||||
"cases_recovered_to_HTML_and_dossier": 5,
|
||||
"runtime_files_byte_identical_to_QA": 4
|
||||
},
|
||||
"compatibility": {
|
||||
"runtime_delta": [
|
||||
"resources/js/components/simulation-irpc-annual.tsx",
|
||||
"resources/js/components/simulation-irpc-pec.tsx",
|
||||
"resources/js/lib/irpc-annual-settlement.ts",
|
||||
"resources/js/lib/irpc-pec.ts"
|
||||
],
|
||||
"runtime_PEC_identity_to_accepted_QA": true,
|
||||
"sector_code_and_tests_identity_to669cb897": true,
|
||||
"changed_matrix_ids": [
|
||||
"RF-CLU-003",
|
||||
"MASTER-9dee08d0-0067",
|
||||
"MASTER-471012ab-0010",
|
||||
"MASTER-471012ab-0067",
|
||||
"AUD-SIM-INT02"
|
||||
],
|
||||
"matrix_note": "Four accepted team-use documentation updates from PEC ancestry preserved;plus PEC AUD-SIM-INT02. Sector rows unchanged.",
|
||||
"other_records_unchanged": true,
|
||||
"schema_config_routes_unchanged": true
|
||||
},
|
||||
"runtime_delta": [
|
||||
{
|
||||
"file": "resources/js/components/simulation-irpc-annual.tsx",
|
||||
"before_sha256": "91ac4ed53ab3f05129f2173cbcf196cef2db1da080a5de5f37e0cd3c5ef74554",
|
||||
"after_sha256": "7f18abcaa1100251c5c2780fdfac32e21979d8e6ffed52f732e4a0d345ab4f40"
|
||||
},
|
||||
{
|
||||
"file": "resources/js/components/simulation-irpc-pec.tsx",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "c3f0ea9ff9389cf856ecda3894c7a1486f44193eb893c341ef10c67f888a2fc6"
|
||||
},
|
||||
{
|
||||
"file": "resources/js/lib/irpc-annual-settlement.ts",
|
||||
"before_sha256": "8cca5316259e3ebabfa75be9f64143a8664a447ac30b23b882318edfd70a68f1",
|
||||
"after_sha256": "6d80889677c1904b3236b5e43fa5a3dc28327745679392529dfb5fbf5a273f7d"
|
||||
},
|
||||
{
|
||||
"file": "resources/js/lib/irpc-pec.ts",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "eb40b0601b6a1a69a6e81ad210589cc8744d74c6ef65e12132de13a5fcd6a90e"
|
||||
}
|
||||
],
|
||||
"source_files_verified": {
|
||||
"before": 1000,
|
||||
"after": 1002
|
||||
},
|
||||
"installer": {
|
||||
"service": "ExecMainStatus=0\nActiveState=inactive\nSubState=dead\n",
|
||||
"installed": {
|
||||
"status": "installed",
|
||||
"commit": "e182d1d79dc945f35d2079c6b8ca2ac93048cec3",
|
||||
"release": "/var/www/investmoz-homologacao/releases/e182d1d79dc945f35d2079c6b8ca2ac93048cec3-e2a185b3",
|
||||
"previous_release": "/var/www/investmoz-homologacao/releases/669cb8979b0c372659fc2ad3876aebc919d5df19-6a2f00c6",
|
||||
"database_migrations": 0,
|
||||
"content_imports": 0
|
||||
},
|
||||
"latest_report": {
|
||||
"status": "installed",
|
||||
"commit": "e182d1d79dc945f35d2079c6b8ca2ac93048cec3",
|
||||
"release": "/var/www/investmoz-homologacao/releases/e182d1d79dc945f35d2079c6b8ca2ac93048cec3-e2a185b3",
|
||||
"previous_release": "/var/www/investmoz-homologacao/releases/669cb8979b0c372659fc2ad3876aebc919d5df19-6a2f00c6",
|
||||
"database_migrations": 0,
|
||||
"content_imports": 0,
|
||||
"requested_commits": [
|
||||
"e182d1d79dc945f35d2079c6b8ca2ac93048cec3"
|
||||
],
|
||||
"finished_at": "2026-10-27T12:10:22.122557+00:00"
|
||||
},
|
||||
"log_file": "1793102808310738327.log",
|
||||
"summaries": [
|
||||
"OK (1 test, 3106 assertions)",
|
||||
"built in 30.51s",
|
||||
"OK (59 tests, 317 assertions)",
|
||||
"# tests 1092",
|
||||
"# pass 1092",
|
||||
"# fail 0"
|
||||
],
|
||||
"log_bytes": 308894
|
||||
},
|
||||
"backup": {
|
||||
"stage": "/var/backups/investmoz-irpc-l8-pec-20261027/backup",
|
||||
"created_at": "2026-10-27T12:05:00Z",
|
||||
"target": {
|
||||
"database": "teste_investidor",
|
||||
"port": 5432
|
||||
},
|
||||
"tables": 174,
|
||||
"files_verified": 369,
|
||||
"dump_sha256": "eff57769cda1e9d0f3d507348b57fc455c0bbc6e38beeb3688135109a6e219fd",
|
||||
"files_sha256": "7830fd2f7671405777d7e5001edade7d5b08b1ebc1f86defb4bf3ec62a10446e",
|
||||
"restore_verified": false
|
||||
},
|
||||
"restore": {
|
||||
"restore_verified": true,
|
||||
"tables_verified": 174,
|
||||
"container_network": "none",
|
||||
"database": "irpc_pec_restore_20261027",
|
||||
"port": 55441,
|
||||
"source_dump_sha256": "eff57769cda1e9d0f3d507348b57fc455c0bbc6e38beeb3688135109a6e219fd",
|
||||
"active_database_writes": 0
|
||||
},
|
||||
"capacity_after_backup": {
|
||||
"at": "2026-10-27T12:06:10.511691+00:00",
|
||||
"baseline": "669cb8979b0c372659fc2ad3876aebc919d5df19",
|
||||
"capacity_after_backup_satisfied": true,
|
||||
"volumes": [
|
||||
{
|
||||
"path": "/var/www/investmoz-homologacao/releases",
|
||||
"free_bytes": 93404454912,
|
||||
"free_inodes": 23730955
|
||||
},
|
||||
{
|
||||
"path": "/var/lib/investmoz-deploy",
|
||||
"free_bytes": 93404454912,
|
||||
"free_inodes": 23730955
|
||||
},
|
||||
{
|
||||
"path": "/var/backups",
|
||||
"free_bytes": 93404454912,
|
||||
"free_inodes": 23730955
|
||||
},
|
||||
{
|
||||
"path": "/tmp",
|
||||
"free_bytes": 93404454912,
|
||||
"free_inodes": 23730955
|
||||
},
|
||||
{
|
||||
"path": "/var/lib/docker",
|
||||
"free_bytes": 93404454912,
|
||||
"free_inodes": 23730955
|
||||
}
|
||||
],
|
||||
"backup_age_seconds": 70.511645,
|
||||
"backup_dump_sha256": "eff57769cda1e9d0f3d507348b57fc455c0bbc6e38beeb3688135109a6e219fd",
|
||||
"restore_verified": true,
|
||||
"tables_verified": 174,
|
||||
"files_verified": 369,
|
||||
"backup_paths": {
|
||||
"dump": "/var/backups/investmoz-irpc-l8-pec-20261027/backup/database.dump",
|
||||
"files": "/var/backups/investmoz-irpc-l8-pec-20261027/backup/files-before.tar.gz"
|
||||
},
|
||||
"active_writes": 0
|
||||
},
|
||||
"schema_permissions": {
|
||||
"schema_sha256": "5045b3e51d2185377aa3d9ac7fa07a44fad3ec620cdfa83ffe31c8884ee1601d",
|
||||
"roles_grants_sha256": "1530c33cb016e67bfdc64891ddfce7f185b13b56b2cd87c012871a685e118109",
|
||||
"filesystem_permissions": {
|
||||
".env": {
|
||||
"mode": 416,
|
||||
"uid": 1001,
|
||||
"gid": 33
|
||||
},
|
||||
"storage": {
|
||||
"mode": 1533,
|
||||
"uid": 1001,
|
||||
"gid": 33
|
||||
},
|
||||
"storage/app": {
|
||||
"mode": 1533,
|
||||
"uid": 1001,
|
||||
"gid": 33
|
||||
},
|
||||
"bootstrap/cache": {
|
||||
"mode": 1528,
|
||||
"uid": 1001,
|
||||
"gid": 33
|
||||
}
|
||||
},
|
||||
"comparison_identity": "postgres;catalogue read only;no passwords collected",
|
||||
"unchanged": true
|
||||
},
|
||||
"preservation": {
|
||||
"checked_at": "2026-10-27T12:11:58.610510+00:00",
|
||||
"installed": "e182d1d79dc945f35d2079c6b8ca2ac93048cec3",
|
||||
"business_tables_unchanged": 172,
|
||||
"total_tables": 174,
|
||||
"operational_tables_to_reconcile": {
|
||||
"cache": {
|
||||
"before": {
|
||||
"rows": 108,
|
||||
"checksum": "8ad8adb3b4cee975cebcb15806c60af5"
|
||||
},
|
||||
"after": {
|
||||
"rows": 108,
|
||||
"checksum": "5079060f1cdc89bf00b1bb3abdc2ff40"
|
||||
}
|
||||
},
|
||||
"sessions": {
|
||||
"before": {
|
||||
"rows": 41,
|
||||
"checksum": "738960921c69e7c0272c0fdac68ce33b"
|
||||
},
|
||||
"after": {
|
||||
"rows": 53,
|
||||
"checksum": "7a5824a4f4e5bbe8b9d745a975aad665"
|
||||
}
|
||||
}
|
||||
},
|
||||
"persistent_files_unchanged": 369,
|
||||
"original_configuration_preserved": true,
|
||||
"synthetic_QA_storage_not_served": true,
|
||||
"QA_storage_saved_outside_active": "/var/lib/investmoz-deploy/qa-runtime/e182d1d79dc945f35d2079c6b8ca2ac93048cec3-e2a185b3",
|
||||
"fixture_or_content_imports": 0,
|
||||
"migrations": 0,
|
||||
"comparison_identity": "postgres;UTC;consistent before snapshot and after read-only transaction",
|
||||
"active_database_writes": 0
|
||||
},
|
||||
"operational_delta": {
|
||||
"status": "operational_delta_reconciled",
|
||||
"comparison_identity": "postgres before/after;isolated restored baseline;no RLS visibility inference",
|
||||
"cache": "queue:restart;requestthrottle;identical home-sector cached value with refreshed expiry(HomeController remember10min);no deletedcache",
|
||||
"sessions": "Anonymous delivery sessions or individually correlated public requests;one existing anonymous session had only token/previous-URL changes in payload and IP/activity metadata,with exact UA/IP/time/HTTP200 document request proof;no identity or other payload change. Deleted sessions,if any,individually expired. No session overwritten.",
|
||||
"delta": {
|
||||
"cache": {
|
||||
"before_rows": 108,
|
||||
"after_rows": 108,
|
||||
"changes": [
|
||||
{
|
||||
"key_sha256": "7f7dd83f47f5bf404d55e6e9f1367948721579970b195680645e356031a070ff",
|
||||
"kind": "updated",
|
||||
"fields": [
|
||||
"value",
|
||||
"expiration"
|
||||
],
|
||||
"key_pattern": "investemoz-cache-illuminate:queue:restart",
|
||||
"is_queue_restart_key": true,
|
||||
"expiration_unchanged": false,
|
||||
"throttle_timer_pair": false,
|
||||
"same_value_home_cache_expiry_refresh": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"sessions": {
|
||||
"before_rows": 41,
|
||||
"after_rows": 59,
|
||||
"changes": [
|
||||
{
|
||||
"key_sha256": "4e78c6a57b8f1771f6b334d7dd2697275c222d72620007b8e86e3f049427a8b3",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "f4fa6adfd3d6918ec9e7de8022a61c9cffe66f75800cf521c12aeb98bc3f062f",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "af76f65ffd28dfed5e35dd2111184a06a4e974608cbd2cc07314dae0dfc09a7f",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "a23c33b4c0cc464b1d78f36f59fb3e63b12a476983d19692f3cc003a629f7f52",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "1b9d5c268f04935e5c4daebefd1edfc1ef88c6581d7c5ebb7034b9790a37ec87",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "805dd129ffaa6ef2e03fff399836bc1945ddee46e714fb0ab8b6ffbf62305a3b",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "d5c7afdfe907c762e30c9036fa3821a2b6ba10eab06a87db9adfbb6e3c51c0dc",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "4ed17597603a4ea407a9a48740ea36ae8831e484a18a6cac9fb8fd29e17d2bcd",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "9d33a9812dcec08990f3f5e525f1d2045a0178e55c52b01e6b766c18db970909",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "38f6c88aecd61073863027dedbdfea596cddfd088ce26bc181461381d790263d",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "6590dfcbfa25d90e3cf00b5f3ed65ebe0385c1fdb8bbd4cfefee45775f3fb394",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "82c72ffd5859310ca5d1ab4781d1c297e69f32808382491eb5dbae4739560a08",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "301015a1771a6e3142105fe8267d63716d480bf2012c62f5ee1b21e5d9018cba",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "4c3537f85bb2c588fb86f5f832f08bfafcae211c951b2b65709cb63813cc2dae",
|
||||
"kind": "updated",
|
||||
"fields": [
|
||||
"ip_address",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": false,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true,
|
||||
"payload_diagnostic": {
|
||||
"changed_keys": [
|
||||
"_token",
|
||||
"_previous"
|
||||
],
|
||||
"token_shapes_valid": true,
|
||||
"token_unchanged": false,
|
||||
"before_previous_path": "/sectores/bens-de-consumo-e-franquias",
|
||||
"after_previous_path": "/documentos/27/view"
|
||||
},
|
||||
"public_navigation_log_matches": true,
|
||||
"current_ip_matches_public_request": true,
|
||||
"both_anonymous": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "58f135bb096ab4309792562b7cef59cd64eef92cb22baf5a1fbbf3a1410106b9",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "312f863d14b15caadb4c8d69bfe56af46cc3046f6443a540de9f4fae4bddb9c7",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "f7c9c5ef85f5adbe12adba07b05275d5405cdab6b01cee0a97344939f12d6ee0",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "e34c74bd2b7e86dfb654e2b65ff97a60b146925ca76169866b31fa4954398bbf",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "63d475bd758e4fd66f1e23c5bf5424e572c1e5ce2cb4ea533261ce0858040e6a",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"business_tables_to_confirm_separately": 172,
|
||||
"active_data_writes_by_comparison": 0,
|
||||
"restore_network": "none"
|
||||
},
|
||||
"public_delta": {
|
||||
"revision": {
|
||||
"commit": "e182d1d79dc945f35d2079c6b8ca2ac93048cec3",
|
||||
"branch": "main",
|
||||
"built_at": "2026-10-27T12:10:18.923517+00:00",
|
||||
"manifest_sha256": "47bbdd09864d0587133deec179781018e439f9c83b7b328c9dcd52482eaf6d31"
|
||||
},
|
||||
"manifest_verified": true,
|
||||
"pec_compiled_markers": {
|
||||
"IRPC-PEC/1": [
|
||||
"assets/project-dossier-D001tZjC.js"
|
||||
],
|
||||
"Preparar detalhe PEC": [
|
||||
"assets/services-I_s1lj16.js"
|
||||
],
|
||||
"Registar comprovativo PEC": [
|
||||
"assets/services-I_s1lj16.js"
|
||||
],
|
||||
"Não calcula a obrigação PEC": [
|
||||
"assets/project-dossier-D001tZjC.js"
|
||||
],
|
||||
"Excedente sem reembolso PEC": [
|
||||
"assets/project-dossier-D001tZjC.js"
|
||||
]
|
||||
},
|
||||
"sector_cards_unchanged": 10,
|
||||
"three_selected_sector_pages_and_guides_unchanged": true,
|
||||
"sector_index_still_without_aggregated_details": true,
|
||||
"filter_preserved": "agronegocio",
|
||||
"active_QA_records_created": false
|
||||
},
|
||||
"l8_remainder_and_credit_resumption": {
|
||||
"scope": "L8 partial after bounded current-year PEC delivery",
|
||||
"next_increment": {
|
||||
"name": "Own-exercise international double-tax credit",
|
||||
"status": "delimited for QA implementation; not yet implemented",
|
||||
"source": "Accepted CIRPC art65,Lei34/2007,PDF18/printed768-(72);order relative to PEC in art66 and RIRPC21",
|
||||
"reuse": [
|
||||
"entity and exercise",
|
||||
"valid L6/L7 collecta",
|
||||
"annual withholding/PPC/PEC book and frozen histories",
|
||||
"existing documents and source references",
|
||||
"existing currency records when an applicable conversion is documented"
|
||||
],
|
||||
"required_case_facts": [
|
||||
"foreign-source income included in the taxable base",
|
||||
"actual foreign income tax, not projected withholding/remittance",
|
||||
"subject, country, exercise, document and available unused amount",
|
||||
"documented domestic IRPC fraction for that foreign income",
|
||||
"known convention applicability and its limit if applicable",
|
||||
"known state of preceding credit groups; no missing-as-zero"
|
||||
],
|
||||
"calculation_contract": "Eligible current credit limited by proven foreign tax and corresponding domestic IRPC fraction, and treaty limit when applicable;then residual collecta for PEC before RF/PPC. No automatic carry/refund/official balance consumption.",
|
||||
"illustrative_case": {
|
||||
"synthetic": true,
|
||||
"foreign_tax": 10,
|
||||
"domestic_limit_documented": 8,
|
||||
"convention_not_applicable_documented": true,
|
||||
"eligible_credit": 8,
|
||||
"collecta": 1408,
|
||||
"after_credit": 1400,
|
||||
"pec": 200,
|
||||
"after_pec": 1200,
|
||||
"withholdings": 0,
|
||||
"ppc": 500,
|
||||
"simulated_payable": 700,
|
||||
"implemented": false,
|
||||
"unutilized_above_limit": "2 is not automatically a carry credit"
|
||||
},
|
||||
"delta_acceptance": [
|
||||
"known/absent/missing and qualification",
|
||||
"country/subject/year and physical proof identity",
|
||||
"no double use across credit/PEC/PPC/RF in the same snapshot",
|
||||
"credit before PEC;separate amounts and limits",
|
||||
"frozen history;save/recover/report",
|
||||
"indeterminate dependent annual balance when facts or preceding credits are missing"
|
||||
]
|
||||
},
|
||||
"remaining_code": [
|
||||
{
|
||||
"item": "international credit eligibility,limits and own-year deduction",
|
||||
"condition": "Implement the documented input contract and demonstrate only new totals and persistence."
|
||||
},
|
||||
{
|
||||
"item": "economic double-tax credit",
|
||||
"condition": "Art64 own conditions,profit inclusion and corresponding tax evidence;separate from international credit."
|
||||
},
|
||||
{
|
||||
"item": "investment/benefit credit",
|
||||
"condition": "Reuse L7 qualification and specific CBF period,amount,limit and accumulation;location alone never grants credit."
|
||||
},
|
||||
{
|
||||
"item": "prior-year PEC/credit transport,usage and expiry",
|
||||
"condition": "Implement provenance by year and actual prior use;distinguish PEC art66 from credit art65;do not equate excess above eligibility limit with lack of collecta."
|
||||
},
|
||||
{
|
||||
"item": "PPC/PEC obligation,instalments,interest and guides",
|
||||
"condition": "Specific facts and applicable rule/edition;actual paid PEC deduction already delivered is separate."
|
||||
},
|
||||
{
|
||||
"item": "autonomous tax,special periods,non-MZN,M20/M22 transport and submission",
|
||||
"condition": "Keep existing separate dependencies;no silent offset or provisional official fields."
|
||||
}
|
||||
],
|
||||
"documentary_dependencies": [
|
||||
{
|
||||
"item": "actual case evidence",
|
||||
"example": "10 MZN foreign tax with no proof of income inclusion or domestic limit",
|
||||
"resume_when": "Identify the existing evidence and amount;no generic revalidation of accepted sources."
|
||||
},
|
||||
{
|
||||
"item": "international convention",
|
||||
"example": "Treaty applies but the article/ceiling and foreign tax paid under it are unknown",
|
||||
"resume_when": "Exact country,convention,article,period and limit;block only dependent case."
|
||||
},
|
||||
{
|
||||
"item": "2026 regulatory amendment for obligation/calendars",
|
||||
"example": "Announcement without published operative articles",
|
||||
"resume_when": "Published diploma,changed articles and effective date for affected obligation/calendar only."
|
||||
}
|
||||
],
|
||||
"private_pilots": "Songo/Gurue remain private",
|
||||
"team_evaluation": "Await actual feedback;do not repeat accepted technical tests."
|
||||
},
|
||||
"private_pilots": "Songo/Gurue private;no publication,fixtures or content import",
|
||||
"team_use_evaluation": "Await actual feedback;accepted technical proofs retained",
|
||||
"artifacts": [
|
||||
{
|
||||
"file": "after.json",
|
||||
"sha256": "0a156acdc9665995f99553a8ffba04e0515ee7250e5266b1115c10d641d0bef7",
|
||||
"bytes": 235814
|
||||
},
|
||||
{
|
||||
"file": "backup.py",
|
||||
"sha256": "6342cf198110b4e5adbc8f3de524667cf30cb0779b3a5208fd2d4d85cb7ae6cc",
|
||||
"bytes": 5440
|
||||
},
|
||||
{
|
||||
"file": "backup.txt",
|
||||
"sha256": "c57f4716111352a2560ebbfd03e2fc9c24745d88067ed78dd15e83b6a108c7a7",
|
||||
"bytes": 810
|
||||
},
|
||||
{
|
||||
"file": "before.json",
|
||||
"sha256": "d50f7e56cdc84aa9ebcf690a6f2614cc81b6a2268fe61da01ba3e6c8ed7a887f",
|
||||
"bytes": 235815
|
||||
},
|
||||
{
|
||||
"file": "capacity-after.py",
|
||||
"sha256": "5fc773a1f74a3f6028c2d190c452c8d58f8b4a2f56afc508c61002d4eb892371",
|
||||
"bytes": 1610
|
||||
},
|
||||
{
|
||||
"file": "capacity-after.txt",
|
||||
"sha256": "9ee2aef5d44b3fb142e0af17e6992de052bfa9e987ffbfd39ea174aca5300af8",
|
||||
"bytes": 983
|
||||
},
|
||||
{
|
||||
"file": "compatibility.json",
|
||||
"sha256": "a9579d644b1dbd4cce1acca10374addeca11971604c5f1a4b87bcde4aac98310",
|
||||
"bytes": 715
|
||||
},
|
||||
{
|
||||
"file": "diagnose-session-delta.py",
|
||||
"sha256": "b6c0ca60258b920333e124d89252fd740587eb6081d2f965c97c13c44297941d",
|
||||
"bytes": 7385
|
||||
},
|
||||
{
|
||||
"file": "inspect.py",
|
||||
"sha256": "7c95ba88a9d5a62a15ec2bf581fa62f5151e548014d106f081487889e56b2703",
|
||||
"bytes": 1947
|
||||
},
|
||||
{
|
||||
"file": "inspect.txt",
|
||||
"sha256": "e3aa850ab8f75765fba2f18777223ea217139768a5fe3b151542462fb576e3fc",
|
||||
"bytes": 4930
|
||||
},
|
||||
{
|
||||
"file": "integrated.json",
|
||||
"sha256": "e81548bd610d293a4919c3727128681edec70badb26209d372c6cc1079cd8c76",
|
||||
"bytes": 281
|
||||
},
|
||||
{
|
||||
"file": "l8-remaining-and-credit-resumption.json",
|
||||
"sha256": "9f04414ad329af20a3f6026152477457481a8ea248367a7c2c21e6a7451df667",
|
||||
"bytes": 4496
|
||||
},
|
||||
{
|
||||
"file": "operational-delta.txt",
|
||||
"sha256": "fe6e21047fe47803c9f5040e296bc78137b9ef3c564bd3edc4005e4894c3d4b8",
|
||||
"bytes": 15960
|
||||
},
|
||||
{
|
||||
"file": "preflight.py",
|
||||
"sha256": "16c4a12ffa9766cf1350351c91c0c935776ed36fc9e214cd864d22d0941bcb42",
|
||||
"bytes": 2334
|
||||
},
|
||||
{
|
||||
"file": "preflight.txt",
|
||||
"sha256": "8d2b03d441da3f46ec268fb9b9274e41cf5ac88ae81af3284f64f1d143db316d",
|
||||
"bytes": 4446
|
||||
},
|
||||
{
|
||||
"file": "preparation.json",
|
||||
"sha256": "933e276820b297a268264a47c909fd7f8b82b2ae95d0b0b657be19fb1458b13f",
|
||||
"bytes": 245902
|
||||
},
|
||||
{
|
||||
"file": "preservation.txt",
|
||||
"sha256": "80d56892cc29aee66b012fbce808e685b409fb04ea8587c2b535a18b7e49457a",
|
||||
"bytes": 945
|
||||
},
|
||||
{
|
||||
"file": "preserve-data.py",
|
||||
"sha256": "9f0c3097467fe171508e741fa1e360df7747aab15f2feb1465adaf38520e69c3",
|
||||
"bytes": 2288
|
||||
},
|
||||
{
|
||||
"file": "public-after.json",
|
||||
"sha256": "5e9f81a2b5430e944ccb9c6cbc6a8df5e1f448d6d8e296b7bb282524972bbb49",
|
||||
"bytes": 43379
|
||||
},
|
||||
{
|
||||
"file": "public-before.json",
|
||||
"sha256": "07717e8041b44bc3b39f6e2f3f3eb78a9cf8349d88bb097faa9b41e088038f80",
|
||||
"bytes": 42785
|
||||
},
|
||||
{
|
||||
"file": "reconcile-operational-delta.py",
|
||||
"sha256": "7f85064f6d733024f4233c2fd2de05504297f033db37d70c1d7fd9a068f2fd89",
|
||||
"bytes": 8276
|
||||
},
|
||||
{
|
||||
"file": "restore.py",
|
||||
"sha256": "8a2e85c85dcf37bc1169f42d4f4164421e914ba06b8630875a68a2b122b16714",
|
||||
"bytes": 2768
|
||||
},
|
||||
{
|
||||
"file": "restore.txt",
|
||||
"sha256": "21a4b64404a16d341014e310beba4a8f802ed0dea140d6c85e474d7fbe4b6277",
|
||||
"bytes": 536
|
||||
},
|
||||
{
|
||||
"file": "schema-after.txt",
|
||||
"sha256": "d5855a7a0a16c1762cce140750c3b8c984e9ea61817bdf02e684cc9ae1c01592",
|
||||
"bytes": 526
|
||||
},
|
||||
{
|
||||
"file": "schema-before.txt",
|
||||
"sha256": "64e445bb8d8546aa399a151adbfe53a64383ea23380f77991d3ddf11721c7127",
|
||||
"bytes": 507
|
||||
},
|
||||
{
|
||||
"file": "schema-permissions-after.py",
|
||||
"sha256": "03b3eafc23c7598fa90bfbcb3e8fb6ee76e5404f3594a69206661538ab9d2a1d",
|
||||
"bytes": 1858
|
||||
},
|
||||
{
|
||||
"file": "schema-permissions-before.py",
|
||||
"sha256": "bc720ff8d3c0814f8a55a5808efd1481da111b75d538e79a3fa15d4ffc2ca74e",
|
||||
"bytes": 1701
|
||||
},
|
||||
{
|
||||
"file": "session-diagnostic.txt",
|
||||
"sha256": "b6d3e7871b1c20153dce3e3182b5a618c1172725bf3be4d4c45b978c10e472ee",
|
||||
"bytes": 1384
|
||||
},
|
||||
{
|
||||
"file": "source-after.txt",
|
||||
"sha256": "756651eb3db81a1e50ee369a62d05d6cfc86e36878194cc509324dcffdda9ad8",
|
||||
"bytes": 160
|
||||
},
|
||||
{
|
||||
"file": "source-before.txt",
|
||||
"sha256": "1a349bee2c2aff9ad38ede72205dc6a1ececea89148e0d67eab126fde5945dd2",
|
||||
"bytes": 161
|
||||
},
|
||||
{
|
||||
"file": "source-check.py",
|
||||
"sha256": "2ea95b35eb8c2eb950216ab77e2a3127b2400372e5034179953edae058b0eb84",
|
||||
"bytes": 669
|
||||
},
|
||||
{
|
||||
"file": "status-a1.txt",
|
||||
"sha256": "f6f22631054fa0920a3c897db7bc6e3a00bce5420e4893f08125bd103dc5fb8f",
|
||||
"bytes": 2244
|
||||
},
|
||||
{
|
||||
"file": "status-a2.txt",
|
||||
"sha256": "6711c9d6d9f93625851c4e161d4bd56997a77626d1df045020fce30e34fd31cf",
|
||||
"bytes": 2162
|
||||
},
|
||||
{
|
||||
"file": "status-a3.txt",
|
||||
"sha256": "6711c9d6d9f93625851c4e161d4bd56997a77626d1df045020fce30e34fd31cf",
|
||||
"bytes": 2162
|
||||
},
|
||||
{
|
||||
"file": "status-a4.txt",
|
||||
"sha256": "d45f8c746654e183e058066ae494ddbe65dd60c157f0e8ba349e256df8758a0a",
|
||||
"bytes": 2244
|
||||
},
|
||||
{
|
||||
"file": "verify-public.py",
|
||||
"sha256": "c5a7535398e0c8ad79458178bd835fa68812b540363495af6abbb38ad2cdd87d",
|
||||
"bytes": 3349
|
||||
}
|
||||
],
|
||||
"remote_receipt_sha256": "a970266625807803ce73f1e63d0da1a540cff264ab88a19d3a8a387a85b26c34"
|
||||
}
|
||||
@@ -0,0 +1,505 @@
|
||||
{
|
||||
"date": "2026-10-27",
|
||||
"status": "QA implemented, not installed",
|
||||
"branch": "codex/irpc-l8-pec-qa-20261027",
|
||||
"baseline": "dc14f01e1479f6bef48be60d2fbe961d9d445e0e",
|
||||
"portal_reference_from_previous_receipt": "9fda6d6e12832a3cf25e7314f1f895dd430238c5",
|
||||
"active_changed": false,
|
||||
"main_changed": false,
|
||||
"scope": {
|
||||
"year": "2026",
|
||||
"regime": "resident_general",
|
||||
"currency": "MZN",
|
||||
"period": "calendar",
|
||||
"payment": "documented current-year PEC, available amount after prior use",
|
||||
"deduction": "min(available selected PEC, valid collecta after preceding credits), before RF/PPC",
|
||||
"special_credits": "only documented absent or known zero; positive/missing stays indeterminate",
|
||||
"excess": "selected amount not deducted, no PEC refund or official carry created"
|
||||
},
|
||||
"examples": [
|
||||
{
|
||||
"case": "payable",
|
||||
"status": "payable",
|
||||
"balance": {
|
||||
"numerator": "708",
|
||||
"denominator": "1"
|
||||
},
|
||||
"pec": {
|
||||
"version": "IRPC-PEC/1",
|
||||
"available": {
|
||||
"numerator": "200",
|
||||
"denominator": "1"
|
||||
},
|
||||
"deducted": {
|
||||
"numerator": "200",
|
||||
"denominator": "1"
|
||||
},
|
||||
"remaining": {
|
||||
"numerator": "0",
|
||||
"denominator": "1"
|
||||
},
|
||||
"afterPec": {
|
||||
"numerator": "1208",
|
||||
"denominator": "1"
|
||||
},
|
||||
"source": "CIRPC, art. 66(1–2), Lei 34/2007, exemplar BR52, p. 768-(72): dedução após créditos anteriores, até à colecta, sem reembolso do PEC. Recorte demonstrado: pagamento efectivo do próprio exercício 2026, residente geral, ano civil, MZN. Não calcula a obrigação PEC, transporte, caducidade ou consumo de saldos oficiais.",
|
||||
"officialBalancesConsumed": false,
|
||||
"carryCreated": false
|
||||
},
|
||||
"rows": 7,
|
||||
"history": 0,
|
||||
"apiRoundTrip": true,
|
||||
"current": true,
|
||||
"dossier": true
|
||||
},
|
||||
{
|
||||
"case": "capped",
|
||||
"status": "balanced",
|
||||
"balance": {
|
||||
"numerator": "0",
|
||||
"denominator": "1"
|
||||
},
|
||||
"pec": {
|
||||
"version": "IRPC-PEC/1",
|
||||
"available": {
|
||||
"numerator": "2000",
|
||||
"denominator": "1"
|
||||
},
|
||||
"deducted": {
|
||||
"numerator": "1408",
|
||||
"denominator": "1"
|
||||
},
|
||||
"remaining": {
|
||||
"numerator": "592",
|
||||
"denominator": "1"
|
||||
},
|
||||
"afterPec": {
|
||||
"numerator": "0",
|
||||
"denominator": "1"
|
||||
},
|
||||
"source": "CIRPC, art. 66(1–2), Lei 34/2007, exemplar BR52, p. 768-(72): dedução após créditos anteriores, até à colecta, sem reembolso do PEC. Recorte demonstrado: pagamento efectivo do próprio exercício 2026, residente geral, ano civil, MZN. Não calcula a obrigação PEC, transporte, caducidade ou consumo de saldos oficiais.",
|
||||
"officialBalancesConsumed": false,
|
||||
"carryCreated": false
|
||||
},
|
||||
"rows": 6,
|
||||
"history": 1,
|
||||
"apiRoundTrip": true,
|
||||
"current": true,
|
||||
"dossier": true
|
||||
},
|
||||
{
|
||||
"case": "recoverable",
|
||||
"status": "recoverable",
|
||||
"balance": {
|
||||
"numerator": "-500",
|
||||
"denominator": "1"
|
||||
},
|
||||
"pec": {
|
||||
"version": "IRPC-PEC/1",
|
||||
"available": {
|
||||
"numerator": "2000",
|
||||
"denominator": "1"
|
||||
},
|
||||
"deducted": {
|
||||
"numerator": "1408",
|
||||
"denominator": "1"
|
||||
},
|
||||
"remaining": {
|
||||
"numerator": "592",
|
||||
"denominator": "1"
|
||||
},
|
||||
"afterPec": {
|
||||
"numerator": "0",
|
||||
"denominator": "1"
|
||||
},
|
||||
"source": "CIRPC, art. 66(1–2), Lei 34/2007, exemplar BR52, p. 768-(72): dedução após créditos anteriores, até à colecta, sem reembolso do PEC. Recorte demonstrado: pagamento efectivo do próprio exercício 2026, residente geral, ano civil, MZN. Não calcula a obrigação PEC, transporte, caducidade ou consumo de saldos oficiais.",
|
||||
"officialBalancesConsumed": false,
|
||||
"carryCreated": false
|
||||
},
|
||||
"rows": 7,
|
||||
"history": 1,
|
||||
"apiRoundTrip": true,
|
||||
"current": true,
|
||||
"dossier": true
|
||||
},
|
||||
{
|
||||
"case": "missing",
|
||||
"status": "indeterminate",
|
||||
"balance": null,
|
||||
"pec": {
|
||||
"version": "IRPC-PEC/1",
|
||||
"available": {
|
||||
"numerator": "0",
|
||||
"denominator": "1"
|
||||
},
|
||||
"deducted": null,
|
||||
"remaining": null,
|
||||
"afterPec": null,
|
||||
"source": "CIRPC, art. 66(1–2), Lei 34/2007, exemplar BR52, p. 768-(72): dedução após créditos anteriores, até à colecta, sem reembolso do PEC. Recorte demonstrado: pagamento efectivo do próprio exercício 2026, residente geral, ano civil, MZN. Não calcula a obrigação PEC, transporte, caducidade ou consumo de saldos oficiais.",
|
||||
"officialBalancesConsumed": false,
|
||||
"carryCreated": false
|
||||
},
|
||||
"rows": 7,
|
||||
"history": 1,
|
||||
"apiRoundTrip": true,
|
||||
"current": true,
|
||||
"dossier": true
|
||||
},
|
||||
{
|
||||
"case": "prior-use",
|
||||
"status": "payable",
|
||||
"balance": {
|
||||
"numerator": "1208",
|
||||
"denominator": "1"
|
||||
},
|
||||
"pec": {
|
||||
"version": "IRPC-PEC/1",
|
||||
"available": {
|
||||
"numerator": "200",
|
||||
"denominator": "1"
|
||||
},
|
||||
"deducted": {
|
||||
"numerator": "200",
|
||||
"denominator": "1"
|
||||
},
|
||||
"remaining": {
|
||||
"numerator": "0",
|
||||
"denominator": "1"
|
||||
},
|
||||
"afterPec": {
|
||||
"numerator": "1208",
|
||||
"denominator": "1"
|
||||
},
|
||||
"source": "CIRPC, art. 66(1–2), Lei 34/2007, exemplar BR52, p. 768-(72): dedução após créditos anteriores, até à colecta, sem reembolso do PEC. Recorte demonstrado: pagamento efectivo do próprio exercício 2026, residente geral, ano civil, MZN. Não calcula a obrigação PEC, transporte, caducidade ou consumo de saldos oficiais.",
|
||||
"officialBalancesConsumed": false,
|
||||
"carryCreated": false
|
||||
},
|
||||
"rows": 6,
|
||||
"history": 1,
|
||||
"apiRoundTrip": true,
|
||||
"current": true,
|
||||
"dossier": true
|
||||
}
|
||||
],
|
||||
"proofs": {
|
||||
"new_frontend_tests": 12,
|
||||
"affected_annual_tests": 19,
|
||||
"api_tests": 1,
|
||||
"api_assertions": 102,
|
||||
"api_cases": 5,
|
||||
"api_round_trip_reports": 5,
|
||||
"routes": "wayfinder --with-form passed",
|
||||
"typescript": "passed",
|
||||
"isolated_build": "passed; optional fontaine/chunk-size notices only",
|
||||
"manual_browser_test": false,
|
||||
"real_team_evaluation": "pending external feedback"
|
||||
},
|
||||
"preservation": {
|
||||
"database": "proactive_investment_20261027_test",
|
||||
"tables": {
|
||||
"opportunities": {
|
||||
"existing_rows": 55,
|
||||
"unchanged": true
|
||||
},
|
||||
"documents": {
|
||||
"existing_rows": 64,
|
||||
"unchanged": true
|
||||
},
|
||||
"opportunity_documents": {
|
||||
"existing_rows": 1,
|
||||
"unchanged": true
|
||||
},
|
||||
"simulation_form_drafts": {
|
||||
"existing_rows": 19,
|
||||
"unchanged": true
|
||||
},
|
||||
"users": {
|
||||
"existing_rows": 40,
|
||||
"unchanged": true
|
||||
},
|
||||
"organizations": {
|
||||
"existing_rows": 3,
|
||||
"unchanged": true
|
||||
},
|
||||
"audit_events": {
|
||||
"existing_rows": 409,
|
||||
"unchanged": true
|
||||
}
|
||||
},
|
||||
"transaction_rollback": true
|
||||
},
|
||||
"source_preservation": {
|
||||
"baseline": "dc14f01e",
|
||||
"count": 30,
|
||||
"all_equal": true,
|
||||
"files": [
|
||||
{
|
||||
"file": "AT-Comunicado-9-2026-ISPC.pdf",
|
||||
"sha256": "106f8a9ce9255c03549c70bf07fe999d32e6e55302d009b509862aa74ccb4973"
|
||||
},
|
||||
{
|
||||
"file": "AT-Modelo-30-ISPC.pdf",
|
||||
"sha256": "6802edd06b6ff638a0b851a3ec7ecf69af8f07840faa47446becad67d35d445c"
|
||||
},
|
||||
{
|
||||
"file": "AT-Modelo-B-Geral.pdf",
|
||||
"sha256": "bd977c7c59d4d98befd92e2c566b1bf168cb1343bb519f51b41a3807f431fc57"
|
||||
},
|
||||
{
|
||||
"file": "AT-MRFTA-19-39.pdf",
|
||||
"sha256": "8ea62c53066b482f87078a7a7b1ecf15ca98a12a122e8d7abca63acfc993186b"
|
||||
},
|
||||
{
|
||||
"file": "BR-16-I-Serie-2008-04-16.pdf",
|
||||
"sha256": "fc373ff5637edf75ec6a9595dec8763a8503d4ee786e3b7d8f1c742eae9d80ee"
|
||||
},
|
||||
{
|
||||
"file": "BR-248-I-Suplemento-1-2025.pdf",
|
||||
"sha256": "ccf2a8deeca820f2dd076b4b2141b1efe3f6a806568c3a587ca59a8e92f4c251"
|
||||
},
|
||||
{
|
||||
"file": "BR-89-I-Serie-2017-06-08.pdf",
|
||||
"sha256": "11f0cab15eb6e51010952203ffa24fdc533a24fd642e19525fe4f46b17a1f5e5"
|
||||
},
|
||||
{
|
||||
"file": "BR-95-I-Suplemento-2025-05-21.pdf",
|
||||
"sha256": "e0e682e2c027c3cd9c1d46a73a3f48bc5f6bdbf05667fd97314d7a3f6a456358"
|
||||
},
|
||||
{
|
||||
"file": "BR252-I-Serie-2022-Lei20-PESOE.pdf",
|
||||
"sha256": "1159fd3ce1ba5581671796d083534da48bd3c5578298819e896f1c2a8026318d"
|
||||
},
|
||||
{
|
||||
"file": "Decreto-22-2021-BR70.pdf",
|
||||
"sha256": "a8fac38dcfe08f9c7a8e46254cff10e1ec82fc4f7a8d88ad6e4fe7d91ffc21a5"
|
||||
},
|
||||
{
|
||||
"file": "Decreto-27-2026-BR123.pdf",
|
||||
"sha256": "26249c3fa58f149c81de36e99d1aac2819a4771379d9e8833159660dabf539bb"
|
||||
},
|
||||
{
|
||||
"file": "Decreto-29-1998-BR22-5-suplemento.pdf",
|
||||
"sha256": "6242cbe675faadf085f43f95ec8c7e8868aafe67c3b2ab2bb010992288f954f8"
|
||||
},
|
||||
{
|
||||
"file": "Decreto-3-2011-BR-11-I-mineiros.pdf",
|
||||
"sha256": "5bca1133666ba9c69870b89823420175b7b5b8d0eccb667ecdf5c29716f3b6be"
|
||||
},
|
||||
{
|
||||
"file": "Decreto-37-2023-BR124.pdf",
|
||||
"sha256": "7e680b191481faa0e2e9057fa5053587eb2a29d1d8c819e0eb9f02814255a04c"
|
||||
},
|
||||
{
|
||||
"file": "Decreto-9-2008-BR16.pdf",
|
||||
"sha256": "96fccdecbfdc11c9eafb90fbc6c7bc259f88bbd4c7507d1ac3af1fa03f960697"
|
||||
},
|
||||
{
|
||||
"file": "Lei-13-2016-CIVA-BR-156-10-suplemento.pdf",
|
||||
"sha256": "b90dc7c14ecea2e378c4a62e8e5a47d54a5644c457baeb8b8053b92fd2a5710b"
|
||||
},
|
||||
{
|
||||
"file": "Lei-14-1992-BR42.pdf",
|
||||
"sha256": "089fff7423e99a70a230a73ca379e4ea3a91d124703f91016e57ff82794b9e56"
|
||||
},
|
||||
{
|
||||
"file": "Lei-17-2022-BR251-exemplar-MCNet5.pdf",
|
||||
"sha256": "14eb34f0af91a8f5cb1770c7b52d6ff839c6818302d1ed96d79afbfb9fe999d4"
|
||||
},
|
||||
{
|
||||
"file": "Lei-17-2022-IPP-BR250-Parlamento.pdf",
|
||||
"sha256": "978cd22403ffd336a631704ecd7a5bbd814a427c815b9acdaca5515591dd9e2a"
|
||||
},
|
||||
{
|
||||
"file": "Lei-19-2013-BR76.pdf",
|
||||
"sha256": "bba96d645cc935ffc3d1a79b9f0f73ac1c693d6459c2ad41f56cd2841accae76"
|
||||
},
|
||||
{
|
||||
"file": "Lei-19-2017-IRPS-BR-OCAM.pdf",
|
||||
"sha256": "c3a0186cb9ad090d7a9533006295dc9bb6a62ad0d6150173cbd49dba4ee4b52d"
|
||||
},
|
||||
{
|
||||
"file": "Lei-2-2011-BR-1-4-suplemento-MCNet.pdf",
|
||||
"sha256": "a7543a68cc3287a46a83c39a04821b40f649dd06c4e9b5dc51ff0b47fba08899"
|
||||
},
|
||||
{
|
||||
"file": "Lei-20-2009-BR36-AT-exemplar19.pdf",
|
||||
"sha256": "fc1c8144db42960cbf73e717e095896f511ece0f907f58f78819b27d6917a7ee"
|
||||
},
|
||||
{
|
||||
"file": "Lei-22-2022-IVA-republicada-BR-250-2-suplemento.pdf",
|
||||
"sha256": "ea1b0dc6f5cf6e595f6d9a19e7042f398999e0087caef1785f6e8fac571b5f53"
|
||||
},
|
||||
{
|
||||
"file": "Lei-34-2007-BR52.pdf",
|
||||
"sha256": "cdbef875ad7ccc1517b12faf5de0e893a70ed2ed81e33913cd6dc4a674474e6e"
|
||||
},
|
||||
{
|
||||
"file": "Lei-4-1994-BR37-3-suplemento.pdf",
|
||||
"sha256": "210df9db8980d0a5690cd187a6eeec7e37a900d743e39062c16f4e0d12215c7e"
|
||||
},
|
||||
{
|
||||
"file": "Lei-4-2012-BR3.pdf",
|
||||
"sha256": "b8845cef41dcbd9ba50c43dbfa743a56030dd3d348242a107e1ae30279eec121"
|
||||
},
|
||||
{
|
||||
"file": "Lei-7-1991-Partidos-IFES.pdf",
|
||||
"sha256": "6b783a0bb3330688ef10660a58060d9724aa2da6a873d234fe902f0eaaeda42d"
|
||||
},
|
||||
{
|
||||
"file": "PT-MZ-CDT-1992.pdf",
|
||||
"sha256": "d504e1024b28aee5c6acf4592acb96ec9c915a8414f67a61ab6984d6acf9df9d"
|
||||
},
|
||||
{
|
||||
"file": "PT-MZ-Protocolo-2009.pdf",
|
||||
"sha256": "f353773dcf41f09f4daa6cf5ba5a5f7994f3e02615867c2dcffc61c3d901bf9b"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rule_sources": [
|
||||
{
|
||||
"file": "Lei-34-2007-BR52.pdf",
|
||||
"pdf_page": 18,
|
||||
"printed_page": "768-(72)",
|
||||
"article": "66(1–2)",
|
||||
"sha256": "cdbef875ad7ccc1517b12faf5de0e893a70ed2ed81e33913cd6dc4a674474e6e"
|
||||
},
|
||||
{
|
||||
"file": "Decreto-9-2008-BR16.pdf",
|
||||
"pdf_pages": [
|
||||
6,
|
||||
7
|
||||
],
|
||||
"articles": "21(2/5),27;29 for deferred obligation context",
|
||||
"sha256": "96fccdecbfdc11c9eafb90fbc6c7bc259f88bbd4c7507d1ac3af1fa03f960697"
|
||||
}
|
||||
],
|
||||
"remaining_code": [
|
||||
"PEC obligation and instalments, separate from actual payment",
|
||||
"Prior-year carry, expiry and usage ledger",
|
||||
"Special credits/autonomous tax/special periods/non-MZN/M20-M22 remain own groups"
|
||||
],
|
||||
"documentary_resume_conditions": [
|
||||
"Published 2026 regulatory amendment: exact articles and effective date for affected obligation/calendar",
|
||||
"Prior-year PEC: exact last eligible exercise, transition and documented usage",
|
||||
"Specific positive credit: own legal limit, order, period, evidence and convention if relevant"
|
||||
],
|
||||
"private_pilots": "Songo/Gurue unchanged and private; no publication",
|
||||
"proactive_team_use": "await actual team feedback; implement concrete observed corrections only",
|
||||
"artifacts": [
|
||||
{
|
||||
"file": "Decreto-9-2008-BR16.pdf-6.png",
|
||||
"sha256": "109f862c8b94e15f55270ec41f90a66ed9cb3b5cf1f772615a8808a9a6034964",
|
||||
"bytes": 476800
|
||||
},
|
||||
{
|
||||
"file": "Decreto-9-2008-BR16.pdf-7.png",
|
||||
"sha256": "943f945fae0a5086fad53cc468f1105257c9b4d762220b823816874bd1878884",
|
||||
"bytes": 504338
|
||||
},
|
||||
{
|
||||
"file": "dossier-capped.json",
|
||||
"sha256": "a93f9dc6b0b2c21526dfaff9a415bbc2b6a53124dbd860ea5d1809c8868dd2d3",
|
||||
"bytes": 31771
|
||||
},
|
||||
{
|
||||
"file": "dossier-missing.json",
|
||||
"sha256": "563e015d9a9069b09dd16548efa21fa37399b24fc6634d136100080706aa6d9a",
|
||||
"bytes": 32184
|
||||
},
|
||||
{
|
||||
"file": "dossier-payable.json",
|
||||
"sha256": "b4cde0e17a9306bf08b7f54103eb499d11e4fd14d70fd38e4858013074d08a3a",
|
||||
"bytes": 32130
|
||||
},
|
||||
{
|
||||
"file": "dossier-prior-use.json",
|
||||
"sha256": "5b2f95e06711099051fc463509854b439a48f7134c6da45210ce0ca529f77622",
|
||||
"bytes": 31776
|
||||
},
|
||||
{
|
||||
"file": "dossier-recoverable.json",
|
||||
"sha256": "27d954e4e6a48980cbbd391a9352ea83456b1e61b42663389f6ada47b55d7c77",
|
||||
"bytes": 32142
|
||||
},
|
||||
{
|
||||
"file": "existing-records-preservation.json",
|
||||
"sha256": "b45b3306d3c9dee63fbbaf9a04bba31008019e65892bd16322460b5ef53eda7d",
|
||||
"bytes": 822
|
||||
},
|
||||
{
|
||||
"file": "Lei-34-2007-BR52.pdf-18.png",
|
||||
"sha256": "25a5493d2d63d492ca849b5b798b87ad792fa4f6a95493ec96fa5c974c8c4c62",
|
||||
"bytes": 467400
|
||||
},
|
||||
{
|
||||
"file": "Lei-34-2007-BR52.pdf-19.png",
|
||||
"sha256": "160f635ce804f400e73d878dff19dc0cae360a195a06952294766f00c74f78d0",
|
||||
"bytes": 483003
|
||||
},
|
||||
{
|
||||
"file": "PEC-excerpts.json",
|
||||
"sha256": "3d8795a9bde96195a59dfef0387c1294a14bd6f671d3df76ac8f7e95ebd7a1ad",
|
||||
"bytes": 19256
|
||||
},
|
||||
{
|
||||
"file": "recovered-capped.json",
|
||||
"sha256": "e9465e01490fc6f3d52ebdf74d12842d60d5bde155e863a14ec8224592dd5ab4",
|
||||
"bytes": 349126
|
||||
},
|
||||
{
|
||||
"file": "recovered-missing.json",
|
||||
"sha256": "32b4183191ed48954f6d13b8b0a2c127a1edfd7a2308370396bf1cce26f7504b",
|
||||
"bytes": 349856
|
||||
},
|
||||
{
|
||||
"file": "recovered-payable.json",
|
||||
"sha256": "8b039e9127e57a2b104f7b37dbedb60e857b82e271bee7c784cf9be06fef2731",
|
||||
"bytes": 344415
|
||||
},
|
||||
{
|
||||
"file": "recovered-prior-use.json",
|
||||
"sha256": "ae5099bc2dd54755f232567a6cf7c87080ca8fc1dfde09db6ed4426d4189d800",
|
||||
"bytes": 348899
|
||||
},
|
||||
{
|
||||
"file": "recovered-recoverable.json",
|
||||
"sha256": "cae038d1abe19bfc3101216d3e0b79a4cc3ef360f3ca4f8fa3e298a7c187d843",
|
||||
"bytes": 349415
|
||||
},
|
||||
{
|
||||
"file": "recovered-report-receipt.json",
|
||||
"sha256": "e70e0777dd92cf8ef17d199238cef3fcef8223a4dfba3b56c420ba4aca42a6f7",
|
||||
"bytes": 5362
|
||||
},
|
||||
{
|
||||
"file": "report-capped.html",
|
||||
"sha256": "ddf9e4fa8508daa86c2f5a337f57fe6d8c443018c8706d79f34c8151a6bd9465",
|
||||
"bytes": 5214
|
||||
},
|
||||
{
|
||||
"file": "report-missing.html",
|
||||
"sha256": "7313eaa4f984ae2976d7a66b81242899bb5a9cc5c55e62f85a0d4813b7710c91",
|
||||
"bytes": 5594
|
||||
},
|
||||
{
|
||||
"file": "report-payable.html",
|
||||
"sha256": "d654183e1d293f7fd025918ddaca6da0d30915e4fd6cc4b34e899b210ed6eb52",
|
||||
"bytes": 5540
|
||||
},
|
||||
{
|
||||
"file": "report-prior-use.html",
|
||||
"sha256": "113bdc44ad910a8679ec99949726d637297180f078c2b6ba4760076e8dd0da85",
|
||||
"bytes": 5219
|
||||
},
|
||||
{
|
||||
"file": "report-recoverable.html",
|
||||
"sha256": "c91511cd25706322ddfc9f081e1058325a8af617a97580c97388304f6481194a",
|
||||
"bytes": 5552
|
||||
},
|
||||
{
|
||||
"file": "source-preservation.json",
|
||||
"sha256": "621864a86ef46931a949d039d3de854c339e205310955fa886d9f879404834e2",
|
||||
"bytes": 4483
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -192,3 +192,30 @@ Reutilizados31originais sem alteração por hash. Lei34/2007PDF5/p768(59),15(1)a
|
||||
## 25/10/2026 — L6:taxa geral para a colecta normal2026
|
||||
|
||||
Sem novos originais:31exemplares preservados por hash. Conferidos visualmenteCIRPC Lei34/2007PDF17/p768(71),61(1),taxa geral32%;Lei20/2022PDF1/p2731,61(1)mantido e61(2)temporário;Lei12/2025PDF18/p2180(18),lista de alterações,ePDF20/p2180(20),61-A/vigência. A alteração61(2)daLei4/2012 já aceite é reutilizada. Índice primárioINM confirmaLei20/2022,artigos61/62,p2731. Não se transportam medidas expiradas,casos autónomos ou taxas de retenção para a colecta normal. Enquadramento do caso/ano é expresso;nenhum aprovador ou revalidação genérica criado. Sem pesquisa das pendênciasL5. Retenções/pagamentos/créditos anuais permanecemL8.
|
||||
|
||||
|
||||
## 27/10/2026 — L8: dedução PEC do próprio exercício
|
||||
|
||||
Sem novo original legal importado. Conferidos visualmente Lei34/2007PDF18/p768-(72),art66(1–2),eDecreto9/2008PDF6/p121,21(2/5),PDF7/p122,27/29;Lei34PDF19/art71 lido para delimitar obrigação separada. Lei4/2012,Lei19/2013 e acervo de alteraçõesLei12/2025 reutilizados;nenhuma alteração doart66 identificada nesses exemplares. Dedução corrente limitada à colecta,sem reembolso PEC;transporte/caducidade e obrigação separados.30PDFs presentes no arquivo source-archive iguais byte a byte a dc14f01e;recibo source-preservation.json.
|
||||
|
||||
Consulta primária dirigida identificou anúncio do Governo de alterações regulamentares2026: https://portaldogoverno.gov.mz/governo-introduz-alteracoes-nos-regulamentos-fiscais-para-reforcar-eficiencia-tributaria/ . O anúncio não fornece o articulado promulgado e contém referência editorial divergente à lei; não é tratado como nova regra. Retoma específica para obrigação/calendário: diploma publicado,artigos e vigência. Sem nova pesquisa genérica ou substituição das fontes aceites;art66 e cálculo simulado corrente permanecem no recorte explícito. Model39 oficialAT identificaPEC,mas não foi inferido transporte automáticoM20/M22: https://edeclaracao.at.gov.mz/formularios/irpc/view/formulario_irpc_m39.aspx .
|
||||
|
||||
|
||||
### 27/10/2026 — Reutilização executável: crédito internacional corrente em QA
|
||||
|
||||
CIRPC65(1–2), Lei34/2007, exemplarBR52, PDF18/p768-(72), e ordem face aoPEC em66/RIRPC21 já aceites: usados no menor entre imposto estrangeiro efectivamente pago, fracção nacional documentada e limiteCDT aplicável, antes doPEC e até à colecta. Conferência da página existente para aplicação da fonte, sem nova exigência de autenticação. Original preservado, sem activação por importação/publicação. Afracção nacional eCDT do caso continuam documentadas pelo utilizador; aQA não estabelece correspondências oficiais de convenções.65(3), transportes, créditos económicos/CBF e obrigaçãoPEC continuam separados. Ver recibo `irpc-l8-international-credit-qa-20261027.json`; não instalado.
|
||||
|
||||
|
||||
### 27/10/2026 — Crédito internacional entregue
|
||||
|
||||
A implementaçãoQA documentada acima foi instalada em`838ad941`,sem alterar fontes ou concederCDT automaticamente. CIRPC65(1–2)/66/RIRPC21 reutilizados no recorte demonstrado;65(3) e transportePEC continuam separados. Ver `irpc-l8-international-credit-delivery-20261027.json`.
|
||||
|
||||
|
||||
## 28/10/2026 — aplicação de65(3)/66 aos saldos anteriores
|
||||
|
||||
Reutilizados os31ficheiros do acervo, todos iguais por bytes a `cf4e0638`. Lei34/2007,BR52,PDF18/p768-(72),SHA256`cdbef875ad7ccc1517b12faf5de0e893a70ed2ed81e33913cd6dc4a674474e6e`: página conferida visualmente;65(3)cinco exercícios seguintes,66(1–2)três paraPEC,sem reembolso. Referência à regra/apuramento aplicáveis na origem é correspondência de período, não nova validação de autenticidade da fonte. Leitura remota do URL arquivado falhou por timeout; usado o exemplar oficial exacto já aceite. Nenhum original alterado,scan declarado ou novo cálculo activado por publicação. Recorte:origens anuais ordinárias documentadas,mesmo sujeito,fecho2026/MZN;sem situações transitórias/especiais presumidas. Recibo `irpc-l8-carry-qa-20261028.json`.
|
||||
|
||||
|
||||
## 28/10/2026 — crédito económico64 e distinção de40/65
|
||||
|
||||
Inventário anterior reutilizado:31ficheiros do acervo byte a byte iguais a6ededb59. Sem novo original. Lei34/2007BR52,SHA256`cdbef875ad7ccc1517b12faf5de0e893a70ed2ed81e33913cd6dc4a674474e6e`,PDF18/p768-(72),64(1–2),ePDF15/p768-(69),53(1)a,conferidos visualmente.40(1):Lei20/2009,exemplarAT19/2009,PDF4/p272-(16),redacção20%/dois anos e condições próprias,substitui a redacção original25%. Listas dasLeis4/2012 e19/2013 conferidas;cadeias20/2022 e12/2025 já aceites reutilizadas nos seus âmbitos. Decreto9/2008,RIRPC21(2/5),PDF6/p121:económico,internacional,benefícios,PEC,até à colecta,sem reembolso. Qualificação do caso,inclusão/acréscimo e quota proporcional documentados;sem automatizar a exclusão40 ou alterarL6por presunção. Sem nova exigência de autenticidade de fonte,scan ou publicação/importação. Pesquisa dirigida não substituiu os exemplares oficiais arquivados. Recibo:`irpc-l8-economic-qa-20261028.json`.
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"date": "2026-10-27",
|
||||
"status": "exercise_prepared_awaiting_real_team_use",
|
||||
"installed_reference_from_accepted_receipt": "9fda6d6e12832a3cf25e7314f1f895dd430238c5",
|
||||
"installation_receipt": "docs/investmoz/unification/sequential-delivery-installed-20261027.json",
|
||||
"guide": "docs/investmoz/unification/traceability-and-manual.md#27102026--avaliação-de-utilização-pela-equipa-sem-codex",
|
||||
"evaluation_scope": [
|
||||
"prepare a private work analysis using an existing authorised source",
|
||||
"record evidence with source,period,coverage and distinction between fact and hypothesis",
|
||||
"structure a complementary idea with clients/payer,conditions and unknowns",
|
||||
"save and recover through the interface",
|
||||
"preview/confirm and recover in the same dossier",
|
||||
"revise a pending point and recover the new version without duplication"
|
||||
],
|
||||
"existing_guide_reused": true,
|
||||
"technical_proofs_reused": true,
|
||||
"new_technical_tests": 0,
|
||||
"new_installations": 0,
|
||||
"active_application_writes": 0,
|
||||
"private_pilots_modified": false,
|
||||
"team_messages_sent": 0,
|
||||
"team_execution_observed": false,
|
||||
"participants": [],
|
||||
"observed_results": [],
|
||||
"Codex_executes_team_exercise": false,
|
||||
"existing_authorised_accounts_only": true,
|
||||
"new_permissions_or_roles": false,
|
||||
"not_a_new_approval_gate": true,
|
||||
"results_to_return": [
|
||||
"date and operational profile",
|
||||
"internal analysis/dossier references in authorised channel",
|
||||
"steps achieved and guide/human help used",
|
||||
"step/field and concrete difficulty",
|
||||
"exact message or behaviour and expected result"
|
||||
],
|
||||
"resume_when": "Real team observations arrive;address only reported usability/access/save-recovery obstacles",
|
||||
"commercial_validation_separate": true,
|
||||
"Songo_and_Gurue": "private;external commercial evidence pending",
|
||||
"L8": "partial;PEC and own credits separate"
|
||||
}
|
||||
@@ -0,0 +1,759 @@
|
||||
{
|
||||
"date": "2026-10-27",
|
||||
"status": "installed_and_verified",
|
||||
"served_commit": "669cb8979b0c372659fc2ad3876aebc919d5df19",
|
||||
"previous_commit": "9fda6d6e12832a3cf25e7314f1f895dd430238c5",
|
||||
"remote_backup": "/var/backups/investmoz-sector-navigation-20261027/backup",
|
||||
"remote_receipt": "/var/backups/investmoz-sector-navigation-20261027/installed-receipt.json",
|
||||
"runtime_delta": [
|
||||
{
|
||||
"file": "app/Http/Controllers/PortalServicesController.php",
|
||||
"before_sha256": "512cc1e2a45a62f4164076a77c5b005a507c6f2150c4fbf7f834193b6357e6f8",
|
||||
"after_sha256": "9bf672f584e5012ec5d8b71828c83ea40d6c116619a41f985396d4531d733cdf"
|
||||
},
|
||||
{
|
||||
"file": "resources/js/pages/portal/services.tsx",
|
||||
"before_sha256": "434790ec829e8f6cb012f88da3871746593051eb7fd6034b6e7977eb2bef4e73",
|
||||
"after_sha256": "9621eaec71bbb87157dbe41841ba1c098e2b5603a72bbaa80adf3bceaa14b61c"
|
||||
}
|
||||
],
|
||||
"source_files_verified": 1000,
|
||||
"qa": {
|
||||
"tests": 3,
|
||||
"assertions": 319,
|
||||
"database": "proactive_investment_20261027_test",
|
||||
"port": 55441,
|
||||
"rollback": true,
|
||||
"routes_types_build": "passed",
|
||||
"browser": "10 unique cards;Agronegocio/Turismo isolated;sector filter,back,reload,guest dossier entry preserved",
|
||||
"screenshot": "unavailable (zero width);AX proof retained"
|
||||
},
|
||||
"installer": {
|
||||
"service": "ExecMainStatus=0\nActiveState=inactive\nSubState=dead\n",
|
||||
"installed": {
|
||||
"status": "installed",
|
||||
"commit": "669cb8979b0c372659fc2ad3876aebc919d5df19",
|
||||
"release": "/var/www/investmoz-homologacao/releases/669cb8979b0c372659fc2ad3876aebc919d5df19-6a2f00c6",
|
||||
"previous_release": "/var/www/investmoz-homologacao/releases/9fda6d6e12832a3cf25e7314f1f895dd430238c5-88fc859c",
|
||||
"database_migrations": 0,
|
||||
"content_imports": 0
|
||||
},
|
||||
"latest_report": {
|
||||
"status": "installed",
|
||||
"commit": "669cb8979b0c372659fc2ad3876aebc919d5df19",
|
||||
"release": "/var/www/investmoz-homologacao/releases/669cb8979b0c372659fc2ad3876aebc919d5df19-6a2f00c6",
|
||||
"previous_release": "/var/www/investmoz-homologacao/releases/9fda6d6e12832a3cf25e7314f1f895dd430238c5-88fc859c",
|
||||
"database_migrations": 0,
|
||||
"content_imports": 0,
|
||||
"requested_commits": [
|
||||
"669cb8979b0c372659fc2ad3876aebc919d5df19"
|
||||
],
|
||||
"finished_at": "2026-10-27T11:53:24.879567+00:00"
|
||||
},
|
||||
"log_file": "1793101792543556387.log",
|
||||
"summaries": [
|
||||
"OK (1 test, 3106 assertions)",
|
||||
"built in 31.42s",
|
||||
"OK (59 tests, 317 assertions)",
|
||||
"# tests 1080",
|
||||
"# pass 1080",
|
||||
"# fail 0"
|
||||
],
|
||||
"log_bytes": 306104
|
||||
},
|
||||
"backup": {
|
||||
"stage": "/var/backups/investmoz-sector-navigation-20261027/backup",
|
||||
"created_at": "2026-10-27T11:44:59Z",
|
||||
"target": {
|
||||
"database": "teste_investidor",
|
||||
"port": 5432
|
||||
},
|
||||
"tables": 174,
|
||||
"files_verified": 369,
|
||||
"dump_sha256": "9b2437017495fe449cfe0c869da62870bd0fc4bddde596660a37392b8dbdaaba",
|
||||
"files_sha256": "bf268753549312be793605de294105ac6836ff96a16ab740b1a2c929a15a523a",
|
||||
"restore_verified": false
|
||||
},
|
||||
"restore": {
|
||||
"restore_verified": true,
|
||||
"tables_verified": 174,
|
||||
"container_network": "none",
|
||||
"database": "sector_navigation_restore_20261027",
|
||||
"port": 55441,
|
||||
"source_dump_sha256": "9b2437017495fe449cfe0c869da62870bd0fc4bddde596660a37392b8dbdaaba",
|
||||
"active_database_writes": 0
|
||||
},
|
||||
"capacity_after_backup": {
|
||||
"at": "2026-10-27T11:48:33.493720+00:00",
|
||||
"baseline": "9fda6d6e12832a3cf25e7314f1f895dd430238c5",
|
||||
"capacity_after_backup_satisfied": true,
|
||||
"volumes": [
|
||||
{
|
||||
"path": "/var/www/investmoz-homologacao/releases",
|
||||
"free_bytes": 94615425024,
|
||||
"free_inodes": 23762622
|
||||
},
|
||||
{
|
||||
"path": "/var/lib/investmoz-deploy",
|
||||
"free_bytes": 94615425024,
|
||||
"free_inodes": 23762622
|
||||
},
|
||||
{
|
||||
"path": "/var/backups",
|
||||
"free_bytes": 94615425024,
|
||||
"free_inodes": 23762622
|
||||
},
|
||||
{
|
||||
"path": "/tmp",
|
||||
"free_bytes": 94615425024,
|
||||
"free_inodes": 23762622
|
||||
},
|
||||
{
|
||||
"path": "/var/lib/docker",
|
||||
"free_bytes": 94615425024,
|
||||
"free_inodes": 23762622
|
||||
}
|
||||
],
|
||||
"backup_age_seconds": 214.493685,
|
||||
"backup_dump_sha256": "9b2437017495fe449cfe0c869da62870bd0fc4bddde596660a37392b8dbdaaba",
|
||||
"restore_verified": true,
|
||||
"tables_verified": 174,
|
||||
"files_verified": 369,
|
||||
"backup_paths": {
|
||||
"dump": "/var/backups/investmoz-sector-navigation-20261027/backup/database.dump",
|
||||
"files": "/var/backups/investmoz-sector-navigation-20261027/backup/files-before.tar.gz"
|
||||
},
|
||||
"active_writes": 0
|
||||
},
|
||||
"schema_permissions": {
|
||||
"schema_sha256": "5045b3e51d2185377aa3d9ac7fa07a44fad3ec620cdfa83ffe31c8884ee1601d",
|
||||
"roles_grants_sha256": "1530c33cb016e67bfdc64891ddfce7f185b13b56b2cd87c012871a685e118109",
|
||||
"filesystem_permissions": {
|
||||
".env": {
|
||||
"mode": 416,
|
||||
"uid": 1001,
|
||||
"gid": 33
|
||||
},
|
||||
"storage": {
|
||||
"mode": 1533,
|
||||
"uid": 1001,
|
||||
"gid": 33
|
||||
},
|
||||
"storage/app": {
|
||||
"mode": 1533,
|
||||
"uid": 1001,
|
||||
"gid": 33
|
||||
},
|
||||
"bootstrap/cache": {
|
||||
"mode": 1528,
|
||||
"uid": 1001,
|
||||
"gid": 33
|
||||
}
|
||||
},
|
||||
"comparison_identity": "postgres;catalogue read only;no passwords collected",
|
||||
"unchanged": true
|
||||
},
|
||||
"preservation": {
|
||||
"checked_at": "2026-10-27T11:55:17.440349+00:00",
|
||||
"installed": "669cb8979b0c372659fc2ad3876aebc919d5df19",
|
||||
"business_tables_unchanged": 172,
|
||||
"total_tables": 174,
|
||||
"operational_tables_to_reconcile": {
|
||||
"cache": {
|
||||
"before": {
|
||||
"rows": 108,
|
||||
"checksum": "0d9184e9b1f30ace1b1da4990b284ed9"
|
||||
},
|
||||
"after": {
|
||||
"rows": 108,
|
||||
"checksum": "6cf70f4b3bfc3a57f18b68080f939a19"
|
||||
}
|
||||
},
|
||||
"sessions": {
|
||||
"before": {
|
||||
"rows": 21,
|
||||
"checksum": "a5144e0ab915427ad1a8dddb70d8beee"
|
||||
},
|
||||
"after": {
|
||||
"rows": 41,
|
||||
"checksum": "261778bcf00473e4d1792749cc5ec6d0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"persistent_files_unchanged": 369,
|
||||
"original_configuration_preserved": true,
|
||||
"synthetic_QA_storage_not_served": true,
|
||||
"QA_storage_saved_outside_active": "/var/lib/investmoz-deploy/qa-runtime/669cb8979b0c372659fc2ad3876aebc919d5df19-6a2f00c6",
|
||||
"fixture_or_content_imports": 0,
|
||||
"migrations": 0,
|
||||
"comparison_identity": "postgres;UTC;consistent before snapshot and after read-only transaction",
|
||||
"active_database_writes": 0
|
||||
},
|
||||
"operational_delta": {
|
||||
"status": "operational_delta_reconciled",
|
||||
"comparison_identity": "postgres before/after;isolated restored baseline;no RLS visibility inference",
|
||||
"cache": "queue:restart;requestthrottle;identical home-sector cached value with refreshed expiry(HomeController remember10min);no deletedcache",
|
||||
"sessions": "anonymous sessions from delivery user-agent or exactUA/time correlated with nginx public request;deleted sessions individually expired beyond configured lifetime,normal database session garbage collection(config/session lottery2/100)",
|
||||
"delta": {
|
||||
"cache": {
|
||||
"before_rows": 108,
|
||||
"after_rows": 108,
|
||||
"changes": [
|
||||
{
|
||||
"key_sha256": "7f7dd83f47f5bf404d55e6e9f1367948721579970b195680645e356031a070ff",
|
||||
"kind": "updated",
|
||||
"fields": [
|
||||
"value",
|
||||
"expiration"
|
||||
],
|
||||
"key_pattern": "investemoz-cache-illuminate:queue:restart",
|
||||
"is_queue_restart_key": true,
|
||||
"expiration_unchanged": false,
|
||||
"throttle_timer_pair": false,
|
||||
"same_value_home_cache_expiry_refresh": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"sessions": {
|
||||
"before_rows": 21,
|
||||
"after_rows": 41,
|
||||
"changes": [
|
||||
{
|
||||
"key_sha256": "2ad1cb241771733ecdcd4d01332669674850e8c69e092604b4deb80014596094",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "17b9f88494ab475782d0a1a069bd6c3c6154489201d1335fbae9af15530a7231",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "fba4cdd75dd31dbc9d9b38ae207cc10c91cf889ecdbe8e7996059f7d5c94a0ae",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "c2ac41f80cf479eb3c8f024b6140d7c65bb75407f8d92a89102b2424e9c78b81",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "438d48be29b8f94cadc4c0731034c86c65cbf457a422949e3ff8a3af82694313",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "64b01593f650d8ab56f143f5e7dd32647277e9be7ace1a77dba9578c6b5b7501",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": false,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "3bb40945f442a7f0f13c7471b2ede4fad92bb615c5716e5f8cbae29b8cb690f4",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "cad0a1de3861c0491bd01e7f133074335041313d64a0aa4692cf70fa607ba81c",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "a88c70aba8ba526065925b98c73e3a05cab4051d9b298e565bd562080eb19bf1",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "d0c392ca36450e9123f8d5472757d86954f41168448fb512ad1fdda6d7c6dd8b",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "96464d3abc3cbd84145b5d265e1d647b762c4e89afdc8cbccd5f3778940ca7d1",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "cfbc6ad7b813e14d9fb2b805daece5ab8c1ea2bfb42c548be709972b692eee3d",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "3caf5a87cdae5355a8066aa5a2773c3252608403c0b8dc66bbbc657c8029bae8",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "294f5cb374a33087114fbb2e71d8815b366ff4e52057ca8ec381d3998786d4fc",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "2f342bbe74c8109df6d30a2c1d293b36572c4953b80d9ccb18d7c8924642f4d7",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "1e430e81b8fc7238b6cc37d5ef16c8482f1475c15b490fc2a8512a7ec15d1136",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "8c7e4a6a4af2a4e0106a24dc4d6e434baefdb8d0bca551bc8377c34cee667ba5",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "9c23bed34e3ee88810369ce7daef74863f692d5ab0360a4f3ae46cd7f60fac2d",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "810ed909001d96275b1c7f2d3f5fd1843afdc2054c134a7d0e8474a9261d6f85",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "7e025b942486695c1749702b5ca355089121e35d744dcd03220aa7b1f5b4cb08",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"business_tables_to_confirm_separately": 172,
|
||||
"active_data_writes_by_comparison": 0,
|
||||
"restore_network": "none"
|
||||
},
|
||||
"public_delta": {
|
||||
"cards": 10,
|
||||
"index_details_before": true,
|
||||
"index_details_after": false,
|
||||
"sector_pages_unchanged": [
|
||||
"agronegocio",
|
||||
"turismo",
|
||||
"mercado-financeiro/fintech"
|
||||
],
|
||||
"guide_count_per_selected_sector": {
|
||||
"agronegocio": 1,
|
||||
"turismo": 1,
|
||||
"mercado-financeiro/fintech": 1
|
||||
},
|
||||
"filter": "agronegocio",
|
||||
"manifest_sha256": "e8f9e3d19077576db83e80adf3f34a93ca676553ac1f6c69c3f4e715a4bde76a",
|
||||
"manifest_verified": true,
|
||||
"private_writes": 0
|
||||
},
|
||||
"main_delivery": "normal fast-forward Git push;existing webhook/installer;old release preserved until ready",
|
||||
"private_pilots": "No Songo/Gurue publication;no QA data import",
|
||||
"pec": "7fdb6b42 remains separate QA branch, not installed in this delivery",
|
||||
"external_commit": "88b39b5 intent reproduced locally by explicit user instruction;no binary-equivalence claim",
|
||||
"artifacts": [
|
||||
{
|
||||
"file": "after.json",
|
||||
"bytes": 235088,
|
||||
"sha256": "2cbdd93fa921c8d2b1d05e5b7c9cdb896f7ebbda8839abe9d52f4f3728d1826e"
|
||||
},
|
||||
{
|
||||
"file": "backup.py",
|
||||
"bytes": 5452,
|
||||
"sha256": "3d4d549387b3b811f51c47e8da79f75e33d9814fd8ee93a116c7254c62d426ae"
|
||||
},
|
||||
{
|
||||
"file": "backup.txt",
|
||||
"bytes": 822,
|
||||
"sha256": "504f147468d9ffdd9cb773fa7eb7c0e1963f0b14047fbdd208659a78332f29d6"
|
||||
},
|
||||
{
|
||||
"file": "before.json",
|
||||
"bytes": 235089,
|
||||
"sha256": "ae5dd2e81c21bb71d01435e397ddf6d831a7527c5e8e7699ef86ef6f5200056a"
|
||||
},
|
||||
{
|
||||
"file": "capacity-after.py",
|
||||
"bytes": 1610,
|
||||
"sha256": "5fc773a1f74a3f6028c2d190c452c8d58f8b4a2f56afc508c61002d4eb892371"
|
||||
},
|
||||
{
|
||||
"file": "capacity-after.txt",
|
||||
"bytes": 1994,
|
||||
"sha256": "dffb2f7bc0f8a4d46dd76022d3952513b4f029545524b7e2d81e97cebebd9f09"
|
||||
},
|
||||
{
|
||||
"file": "index-served.txt",
|
||||
"bytes": 6663,
|
||||
"sha256": "bea4d0bbb39366a90a6b2c2fd16d14d0bd31bed93595145f1afe2740d59b99a0"
|
||||
},
|
||||
{
|
||||
"file": "inspect.txt",
|
||||
"bytes": 61498,
|
||||
"sha256": "6e66ce6aaeeb67723daa65d3659660aadb14760d365db21dc3a36a8b3cef2d3f"
|
||||
},
|
||||
{
|
||||
"file": "integrated.json",
|
||||
"bytes": 251,
|
||||
"sha256": "5f58ca5aad02394c5d0f8932ce9c3d09c915c16d2299ec76ddd06445f9c3b5e2"
|
||||
},
|
||||
{
|
||||
"file": "matrix-preservation.json",
|
||||
"bytes": 142,
|
||||
"sha256": "3f9eba3a66126091ade7bde897f6ed5be1428ddcb62b610a537ec638007ba2fe"
|
||||
},
|
||||
{
|
||||
"file": "operational-delta.txt",
|
||||
"bytes": 7899,
|
||||
"sha256": "56d9ac3eb9be3069164303d615fc484dedc3c1912a4e422bd340529419cb9c82"
|
||||
},
|
||||
{
|
||||
"file": "pipeline-current.py",
|
||||
"bytes": 21198,
|
||||
"sha256": "adec1c17654f3a8658b83b134f1e75f95391b235738552721906c3236e388604"
|
||||
},
|
||||
{
|
||||
"file": "preflight.py",
|
||||
"bytes": 2337,
|
||||
"sha256": "73484c8e8e811f68799200b87c7f2b58b91b09502afbd64b8f9b3116ab59bbf2"
|
||||
},
|
||||
{
|
||||
"file": "preflight.txt",
|
||||
"bytes": 4446,
|
||||
"sha256": "16d116ab7c673aa836fb5afae74369077b0103028d7972e1cb34bbb0bf9f8afd"
|
||||
},
|
||||
{
|
||||
"file": "preparation.json",
|
||||
"bytes": 245104,
|
||||
"sha256": "bbad8749a58c20e89f40064eee47b53969f85c4e478bcfd65eb73e0666684773"
|
||||
},
|
||||
{
|
||||
"file": "preservation.txt",
|
||||
"bytes": 945,
|
||||
"sha256": "95ed7c2338244ca751fb01e2a92ca5a4e408e23947bc394322970c2bf5eab860"
|
||||
},
|
||||
{
|
||||
"file": "preserve-data.py",
|
||||
"bytes": 2288,
|
||||
"sha256": "9f0c3097467fe171508e741fa1e360df7747aab15f2feb1465adaf38520e69c3"
|
||||
},
|
||||
{
|
||||
"file": "public-after.json",
|
||||
"bytes": 42854,
|
||||
"sha256": "cee7364e130234ca27fea14ad271c0b13d9de68fbecf0910ac4014fec07c498c"
|
||||
},
|
||||
{
|
||||
"file": "public-before.json",
|
||||
"bytes": 42784,
|
||||
"sha256": "cfe084efbf1bf43b3624e4a1b4834857c6673dc32f614fe6a8e57f47882c8b11"
|
||||
},
|
||||
{
|
||||
"file": "public-details-after-diagnostic.json",
|
||||
"bytes": 34496,
|
||||
"sha256": "173fc6ceb4429e4c53a481606efedb3c6738f224d761f43a75dc18cae4b6a1e8"
|
||||
},
|
||||
{
|
||||
"file": "reconcile-operational-delta.py",
|
||||
"bytes": 5338,
|
||||
"sha256": "295501048b897c8506ced3943013058f8bd2c904e8a48b083ecd173759c41cf3"
|
||||
},
|
||||
{
|
||||
"file": "restore.py",
|
||||
"bytes": 2819,
|
||||
"sha256": "4bd143dd278701e08d1878d4011b096d1fb22ecadd995b5b433349d9e4cd36cc"
|
||||
},
|
||||
{
|
||||
"file": "restore.txt",
|
||||
"bytes": 554,
|
||||
"sha256": "2b31f1844444604094dfbaed3e17a62a6e3c8dcac178052632b62780efd063c6"
|
||||
},
|
||||
{
|
||||
"file": "schema-after.txt",
|
||||
"bytes": 526,
|
||||
"sha256": "d5855a7a0a16c1762cce140750c3b8c984e9ea61817bdf02e684cc9ae1c01592"
|
||||
},
|
||||
{
|
||||
"file": "schema-before.txt",
|
||||
"bytes": 1016,
|
||||
"sha256": "0ab7c964792acdd8917bce256c472e55e034b103500a58b49f020b5084823ed7"
|
||||
},
|
||||
{
|
||||
"file": "schema-permissions-after.py",
|
||||
"bytes": 1864,
|
||||
"sha256": "52ddfc9d1a235518e95dedc653eb303866477d52a8b712da75d331cea7ab714f"
|
||||
},
|
||||
{
|
||||
"file": "schema-permissions-before.py",
|
||||
"bytes": 1707,
|
||||
"sha256": "2723b6a4e099f3af2efd333f3f9d8c6937e6cf2255b8448f5f27bc74455af319"
|
||||
},
|
||||
{
|
||||
"file": "source-after.txt",
|
||||
"bytes": 160,
|
||||
"sha256": "e88624cd7f7e72c2dad88fabac95c2dfe6ca9c140d40aecfed238fd46e2a0997"
|
||||
},
|
||||
{
|
||||
"file": "source-before.txt",
|
||||
"bytes": 324,
|
||||
"sha256": "d0211ba30920fd270b5bc3873455067b20cb0078f35991410f26356b70350e2b"
|
||||
},
|
||||
{
|
||||
"file": "source-check.py",
|
||||
"bytes": 669,
|
||||
"sha256": "2ea95b35eb8c2eb950216ab77e2a3127b2400372e5034179953edae058b0eb84"
|
||||
},
|
||||
{
|
||||
"file": "ssh-session.py",
|
||||
"bytes": 3050,
|
||||
"sha256": "51c402acdfb594d8146610a224f03b17369b6641511235a616c84c7e5d688f18"
|
||||
},
|
||||
{
|
||||
"file": "status-a1.txt",
|
||||
"bytes": 1994,
|
||||
"sha256": "1d78e2bf83b402b2108dff42b029fe95753399dfcd3e880263d0cdb2c77f4b85"
|
||||
},
|
||||
{
|
||||
"file": "status-a2.txt",
|
||||
"bytes": 2058,
|
||||
"sha256": "0307b19f4fd764350cac2c839968011516c7c7aa7bb5d80317816a70bc5cd16f"
|
||||
},
|
||||
{
|
||||
"file": "status-a3.txt",
|
||||
"bytes": 2162,
|
||||
"sha256": "ebdec6a635347bcec45d45b3ab182b7a10a22d816cef57709b156e4d2a1e4da7"
|
||||
},
|
||||
{
|
||||
"file": "status-a4.txt",
|
||||
"bytes": 2162,
|
||||
"sha256": "ebdec6a635347bcec45d45b3ab182b7a10a22d816cef57709b156e4d2a1e4da7"
|
||||
},
|
||||
{
|
||||
"file": "status-a5.txt",
|
||||
"bytes": 2244,
|
||||
"sha256": "f6f22631054fa0920a3c897db7bc6e3a00bce5420e4893f08125bd103dc5fb8f"
|
||||
},
|
||||
{
|
||||
"file": "verify-public.py",
|
||||
"bytes": 2470,
|
||||
"sha256": "733a94aa83698de4a5f1f1a79c9d3dbdce8d80c69787b549864f7916810777b4"
|
||||
}
|
||||
],
|
||||
"remote_receipt_sha256": "cd95142b7f85e40a0f86e0f7d3dca513e7bc208c89aa91b8e3ad213191d8465f"
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
{
|
||||
"date": "2026-10-27",
|
||||
"status": "QA passed; not yet installed",
|
||||
"source_intention": "88b39b527859bd420f2e223a7afea89bea38de54",
|
||||
"reproduced_locally": true,
|
||||
"byte_equivalence_to_external_commit": false,
|
||||
"baseline": "9fda6d6e12832a3cf25e7314f1f895dd430238c5",
|
||||
"branch": "codex/sector-navigation-fix-local-20261027",
|
||||
"runtime_delta": [
|
||||
"app/Http/Controllers/PortalServicesController.php",
|
||||
"resources/js/pages/portal/services.tsx"
|
||||
],
|
||||
"tests": {
|
||||
"feature_tests": 3,
|
||||
"assertions": 319,
|
||||
"database": "proactive_investment_20261027_test",
|
||||
"port": 55441,
|
||||
"rollback": true,
|
||||
"routes": "passed",
|
||||
"typescript": "passed",
|
||||
"build": "passed"
|
||||
},
|
||||
"browser": {
|
||||
"index_cards": 10,
|
||||
"separate_sectors": [
|
||||
"agronegocio",
|
||||
"turismo"
|
||||
],
|
||||
"opportunities_isolated": true,
|
||||
"filter": "agronegocio",
|
||||
"back": true,
|
||||
"reload": true,
|
||||
"fintech_url_preserved": true,
|
||||
"dossier_entry": "sector:3; existing guest authentication, no new dossier created",
|
||||
"screenshots": "capture unavailable: zero width; AX proof retained",
|
||||
"editorial_guides": "not loaded in QA sample; consumers unchanged, destination to check"
|
||||
},
|
||||
"protected_files": [
|
||||
{
|
||||
"file": "resources/js/components/sector-content.tsx",
|
||||
"sha256_LF": "4ee55554f8a88ba7db1ac0dc74259d4e66603979c3873f9ec897437830b8e0ae",
|
||||
"unchanged": true
|
||||
},
|
||||
{
|
||||
"file": "resources/js/components/sector-entry.tsx",
|
||||
"sha256_LF": "96672c709b70f840d105c664a92d49a17acb66cd852d43dadaa8f3fef8b3e845",
|
||||
"unchanged": true
|
||||
},
|
||||
{
|
||||
"file": "resources/js/components/public-sector-cards.tsx",
|
||||
"sha256_LF": "858ba6235ef8b46461d416b3264436e4df2c9555b8e6d8c3644dbd8c55463484",
|
||||
"unchanged": true
|
||||
},
|
||||
{
|
||||
"file": "app/Services/SectorInvestment.php",
|
||||
"sha256_LF": "0b8fbc6e3d03ede627e95332703e4749a53d86708bdc0f9579e958caf30e4d17",
|
||||
"unchanged": true
|
||||
},
|
||||
{
|
||||
"file": "app/Services/SectorOrganization.php",
|
||||
"sha256_LF": "f0e54fe6acfe746c6758387f43610232cb51371b0404bcefaf466d8b704f3ae3",
|
||||
"unchanged": true
|
||||
},
|
||||
{
|
||||
"file": "app/Http/Controllers/PublicPortalController.php",
|
||||
"sha256_LF": "cb6abd75533660a52e18148cc5f1ab706d2e08f9c94334510541e6b0e003b461",
|
||||
"unchanged": true
|
||||
},
|
||||
{
|
||||
"file": "resources/js/lib/project-dossier.ts",
|
||||
"sha256_LF": "d59f3fa4dcb3fade1f967564c972e30932aa095a4e5dd4ba0be8b4e07492ef0a",
|
||||
"unchanged": true
|
||||
},
|
||||
{
|
||||
"file": "resources/js/lib/irpc-annual-settlement.ts",
|
||||
"sha256_LF": "8cca5316259e3ebabfa75be9f64143a8664a447ac30b23b882318edfd70a68f1",
|
||||
"unchanged": true
|
||||
},
|
||||
{
|
||||
"file": "routes/web.php",
|
||||
"sha256_LF": "95930c76977d446b74a422acc4b60869ff6bae5d4567c21cc644484c5f09cbda",
|
||||
"unchanged": true
|
||||
}
|
||||
],
|
||||
"pec_branch_not_integrated": "7fdb6b42",
|
||||
"active_writes": 0,
|
||||
"private_pilots_published": false
|
||||
}
|
||||
@@ -0,0 +1,719 @@
|
||||
{
|
||||
"date": "2026-10-27T10:51:18.002627+00:00",
|
||||
"status": "both_deliveries_installed_in_requested_order",
|
||||
"order": [
|
||||
"l8",
|
||||
"proactive"
|
||||
],
|
||||
"stages": {
|
||||
"l8": {
|
||||
"candidate": "c85e772d2fd2be5182eb4d768ddd34d43962bf59",
|
||||
"previous": "f8d57bddf96b4610604a013fcc5bdd703c7dc6d5",
|
||||
"installed": {
|
||||
"status": "installed",
|
||||
"commit": "c85e772d2fd2be5182eb4d768ddd34d43962bf59",
|
||||
"release": "/var/www/investmoz-homologacao/releases/c85e772d2fd2be5182eb4d768ddd34d43962bf59-ae7401aa",
|
||||
"previous_release": "/var/www/investmoz-homologacao/releases/f8d57bddf96b4610604a013fcc5bdd703c7dc6d5-7a31e8ac",
|
||||
"database_migrations": 0,
|
||||
"content_imports": 0
|
||||
},
|
||||
"served": {
|
||||
"commit": "c85e772d2fd2be5182eb4d768ddd34d43962bf59",
|
||||
"branch": "main",
|
||||
"built_at": "2026-10-27T10:32:26.854949+00:00",
|
||||
"manifest_sha256": "f27852b26d53b46f6927ee46c13370db17a4bec1623655552fd8fdca3589005a"
|
||||
},
|
||||
"backup": {
|
||||
"stage": "/var/backups/investmoz-sequential-20261027/l8/backup",
|
||||
"created_at": "2026-10-27T10:25:59Z",
|
||||
"target": {
|
||||
"database": "teste_investidor",
|
||||
"port": 5432
|
||||
},
|
||||
"tables": 174,
|
||||
"files_verified": 369,
|
||||
"dump_sha256": "11bc7c8f25edc861b18785a9714845384cf29a5da92f58f55ff53f6913e69d42",
|
||||
"files_sha256": "52ffe40bcf943ce580047b55c34b60d3ba876d81dd4bc3ff4fbe1ec97a9c6f56",
|
||||
"restore_verified": false
|
||||
},
|
||||
"restore": {
|
||||
"restore_verified": true,
|
||||
"tables_verified": 174,
|
||||
"container_network": "none",
|
||||
"database": "irpc_l8_first_restore_20261027",
|
||||
"port": 55441,
|
||||
"source_dump_sha256": "11bc7c8f25edc861b18785a9714845384cf29a5da92f58f55ff53f6913e69d42",
|
||||
"active_database_writes": 0
|
||||
},
|
||||
"capacity_after_backup": {
|
||||
"at": "2026-10-27T10:28:35.403687+00:00",
|
||||
"baseline": "f8d57bddf96b4610604a013fcc5bdd703c7dc6d5",
|
||||
"capacity_after_backup_satisfied": true,
|
||||
"volumes": [
|
||||
{
|
||||
"path": "/var/www/investmoz-homologacao/releases",
|
||||
"free_bytes": 98153496576,
|
||||
"free_inodes": 23860325
|
||||
},
|
||||
{
|
||||
"path": "/var/lib/investmoz-deploy",
|
||||
"free_bytes": 98153496576,
|
||||
"free_inodes": 23860325
|
||||
},
|
||||
{
|
||||
"path": "/var/backups",
|
||||
"free_bytes": 98153496576,
|
||||
"free_inodes": 23860325
|
||||
},
|
||||
{
|
||||
"path": "/tmp",
|
||||
"free_bytes": 98153496576,
|
||||
"free_inodes": 23860325
|
||||
},
|
||||
{
|
||||
"path": "/var/lib/docker",
|
||||
"free_bytes": 98153496576,
|
||||
"free_inodes": 23860325
|
||||
}
|
||||
],
|
||||
"backup_age_seconds": 156.403651,
|
||||
"backup_dump_sha256": "11bc7c8f25edc861b18785a9714845384cf29a5da92f58f55ff53f6913e69d42",
|
||||
"restore_verified": true,
|
||||
"tables_verified": 174,
|
||||
"files_verified": 369,
|
||||
"backup_paths": {
|
||||
"dump": "/var/backups/investmoz-sequential-20261027/l8/backup/database.dump",
|
||||
"files": "/var/backups/investmoz-sequential-20261027/l8/backup/files-before.tar.gz"
|
||||
},
|
||||
"active_writes": 0
|
||||
},
|
||||
"source": {
|
||||
"phase": "after",
|
||||
"installed": "c85e772d2fd2be5182eb4d768ddd34d43962bf59",
|
||||
"files_verified": 996,
|
||||
"source_matches_Git": true,
|
||||
"active_writes": 0
|
||||
},
|
||||
"schema_permissions": {
|
||||
"schema_sha256": "5045b3e51d2185377aa3d9ac7fa07a44fad3ec620cdfa83ffe31c8884ee1601d",
|
||||
"roles_grants_sha256": "1530c33cb016e67bfdc64891ddfce7f185b13b56b2cd87c012871a685e118109",
|
||||
"filesystem_permissions": {
|
||||
".env": {
|
||||
"mode": 416,
|
||||
"uid": 1001,
|
||||
"gid": 33
|
||||
},
|
||||
"storage": {
|
||||
"mode": 1533,
|
||||
"uid": 1001,
|
||||
"gid": 33
|
||||
},
|
||||
"storage/app": {
|
||||
"mode": 1533,
|
||||
"uid": 1001,
|
||||
"gid": 33
|
||||
},
|
||||
"bootstrap/cache": {
|
||||
"mode": 1528,
|
||||
"uid": 1001,
|
||||
"gid": 33
|
||||
}
|
||||
},
|
||||
"comparison_identity": "postgres;catalogue read only;no passwords collected",
|
||||
"unchanged": true
|
||||
},
|
||||
"preservation": {
|
||||
"checked_at": "2026-10-27T10:34:53.148104+00:00",
|
||||
"installed": "c85e772d2fd2be5182eb4d768ddd34d43962bf59",
|
||||
"business_tables_unchanged": 172,
|
||||
"total_tables": 174,
|
||||
"operational_tables_to_reconcile": {
|
||||
"cache": {
|
||||
"before": {
|
||||
"rows": 106,
|
||||
"checksum": "6c05e12d73079effd52f3add83104de2"
|
||||
},
|
||||
"after": {
|
||||
"rows": 106,
|
||||
"checksum": "a9c2d7df6ecc8cabba35d5c6f1d019c5"
|
||||
}
|
||||
},
|
||||
"sessions": {
|
||||
"before": {
|
||||
"rows": 11,
|
||||
"checksum": "b83d84b3894b8fc6ef42b0b8fd937304"
|
||||
},
|
||||
"after": {
|
||||
"rows": 12,
|
||||
"checksum": "fea9aecf735aa394f40e9a4b6a55e27f"
|
||||
}
|
||||
}
|
||||
},
|
||||
"persistent_files_unchanged": 369,
|
||||
"original_configuration_preserved": true,
|
||||
"synthetic_QA_storage_not_served": true,
|
||||
"QA_storage_saved_outside_active": "/var/lib/investmoz-deploy/qa-runtime/c85e772d2fd2be5182eb4d768ddd34d43962bf59-ae7401aa",
|
||||
"fixture_or_content_imports": 0,
|
||||
"migrations": 0,
|
||||
"comparison_identity": "postgres;UTC;consistent before snapshot and after read-only transaction",
|
||||
"active_database_writes": 0
|
||||
},
|
||||
"operational_delta": {
|
||||
"status": "operational_delta_reconciled",
|
||||
"comparison_identity": "postgres before/after;isolated restored baseline;no RLS visibility inference",
|
||||
"cache": "queue:restart;requestthrottle;identical home-sector cached value with refreshed expiry(HomeController remember10min);no deletedcache",
|
||||
"sessions": "anonymous sessions from delivery user-agent or exactUA/time correlated with nginx public request;deleted sessions individually expired beyond configured lifetime,normal database session garbage collection(config/session lottery2/100)",
|
||||
"delta": {
|
||||
"cache": {
|
||||
"before_rows": 106,
|
||||
"after_rows": 106,
|
||||
"changes": [
|
||||
{
|
||||
"key_sha256": "7f7dd83f47f5bf404d55e6e9f1367948721579970b195680645e356031a070ff",
|
||||
"kind": "updated",
|
||||
"fields": [
|
||||
"value",
|
||||
"expiration"
|
||||
],
|
||||
"key_pattern": "investemoz-cache-illuminate:queue:restart",
|
||||
"is_queue_restart_key": true,
|
||||
"expiration_unchanged": false,
|
||||
"throttle_timer_pair": false,
|
||||
"same_value_home_cache_expiry_refresh": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"sessions": {
|
||||
"before_rows": 11,
|
||||
"after_rows": 15,
|
||||
"changes": [
|
||||
{
|
||||
"key_sha256": "7a0b0d7c40ef99d4c033c9df695a5cd69d6433f068463fdf9f8baff6835453f6",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "9f06bc9fb6ef8d45fe1c1093b4e54b59ee080463861b8051271597b0ec5e45a4",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "e66f917f9d71192fe17cf2c7c26046afa6bff0db292d0c232a5b59629be030a3",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "a84b009ac5f5358a0452f4a40968a4826e34ced257930b6ecf08a0289aa6684f",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"business_tables_to_confirm_separately": 172,
|
||||
"active_data_writes_by_comparison": 0,
|
||||
"restore_network": "none"
|
||||
},
|
||||
"public_delta": {
|
||||
"stage": "l8",
|
||||
"served_revision": {
|
||||
"commit": "c85e772d2fd2be5182eb4d768ddd34d43962bf59",
|
||||
"branch": "main",
|
||||
"built_at": "2026-10-27T10:32:26.854949+00:00",
|
||||
"manifest_sha256": "f27852b26d53b46f6927ee46c13370db17a4bec1623655552fd8fdca3589005a"
|
||||
},
|
||||
"manifest_hash_matches": true,
|
||||
"compiled_delta_markers": {
|
||||
"IRPC-annual-settlement/1": [
|
||||
"assets/project-dossier-BNt4O6Rz.js"
|
||||
],
|
||||
"Preparar saldo anual L8": [
|
||||
"assets/services-BvFvahdE.js"
|
||||
],
|
||||
"Calcular e guardar saldo anual L8": [
|
||||
"assets/services-BvFvahdE.js"
|
||||
],
|
||||
"Saldo anual simulado": [
|
||||
"assets/project-dossier-BNt4O6Rz.js"
|
||||
],
|
||||
"PEC e créditos separados": [
|
||||
"assets/project-dossier-BNt4O6Rz.js"
|
||||
]
|
||||
},
|
||||
"assets": [
|
||||
{
|
||||
"file": "assets/simulation-form-zFZ4YNrB.js",
|
||||
"status": 200,
|
||||
"sha256": "338068b86cce1c3215ee71b1328a6cb2f754b903ed78ff1886f8554df506d2d6"
|
||||
},
|
||||
{
|
||||
"file": "assets/services-BvFvahdE.js",
|
||||
"status": 200,
|
||||
"sha256": "97615f0d113746ee5ac1550a1e59c567dd152ef5ddac68f133bd2cee9e9c16d1"
|
||||
},
|
||||
{
|
||||
"file": "assets/project-dossier-BNt4O6Rz.js",
|
||||
"status": 200,
|
||||
"sha256": "8cf6a6678c5c9aed0d7b996147bc2fde35d20f86c73ec7bd894df2ae11aae75c"
|
||||
},
|
||||
{
|
||||
"file": "assets/project-dossier-document-Cj4QX7oF.js",
|
||||
"status": 200,
|
||||
"sha256": "302ec6831893f2ef4c09a90273b458c49a6ff281adb01c4594a166a90efe53e2"
|
||||
}
|
||||
],
|
||||
"simulator_status": 200,
|
||||
"served_component": "portal/services",
|
||||
"private_operations": 0,
|
||||
"QA_fixture_imports": 0,
|
||||
"accepted_QA_proofs_reused": true,
|
||||
"method": "Read-only served revision/manifest/compiled delta;no synthetic active records or publication"
|
||||
},
|
||||
"backup_restore_verified": true
|
||||
},
|
||||
"proactive": {
|
||||
"candidate": "9fda6d6e12832a3cf25e7314f1f895dd430238c5",
|
||||
"previous": "c85e772d2fd2be5182eb4d768ddd34d43962bf59",
|
||||
"installed": {
|
||||
"status": "installed",
|
||||
"commit": "9fda6d6e12832a3cf25e7314f1f895dd430238c5",
|
||||
"release": "/var/www/investmoz-homologacao/releases/9fda6d6e12832a3cf25e7314f1f895dd430238c5-88fc859c",
|
||||
"previous_release": "/var/www/investmoz-homologacao/releases/c85e772d2fd2be5182eb4d768ddd34d43962bf59-ae7401aa",
|
||||
"database_migrations": 0,
|
||||
"content_imports": 0
|
||||
},
|
||||
"served": {
|
||||
"commit": "9fda6d6e12832a3cf25e7314f1f895dd430238c5",
|
||||
"branch": "main",
|
||||
"built_at": "2026-10-27T10:50:00.471464+00:00",
|
||||
"manifest_sha256": "40de38cbf74b475f73de825bfd16af6fabe339faad4817e2857fd9ae36ec7cc0"
|
||||
},
|
||||
"backup": {
|
||||
"stage": "/var/backups/investmoz-sequential-20261027/proactive/backup",
|
||||
"created_at": "2026-10-27T10:36:28Z",
|
||||
"target": {
|
||||
"database": "teste_investidor",
|
||||
"port": 5432
|
||||
},
|
||||
"tables": 174,
|
||||
"files_verified": 369,
|
||||
"dump_sha256": "45107e48e18ab2b9a78af08374fe3bfa0af216f34433c10fc81f264400e331ce",
|
||||
"files_sha256": "f7196314f915e75975a9e580a69ddc4dffae64597ae05e9ae59cfbcbb0b47731",
|
||||
"restore_verified": false
|
||||
},
|
||||
"restore": {
|
||||
"restore_verified": true,
|
||||
"tables_verified": 174,
|
||||
"container_network": "none",
|
||||
"database": "irpc_l8_first_restore_20261027",
|
||||
"port": 55441,
|
||||
"source_dump_sha256": "45107e48e18ab2b9a78af08374fe3bfa0af216f34433c10fc81f264400e331ce",
|
||||
"active_database_writes": 0
|
||||
},
|
||||
"capacity_after_backup": {
|
||||
"at": "2026-10-27T10:46:06.120042+00:00",
|
||||
"baseline": "c85e772d2fd2be5182eb4d768ddd34d43962bf59",
|
||||
"capacity_after_backup_satisfied": true,
|
||||
"volumes": [
|
||||
{
|
||||
"path": "/var/www/investmoz-homologacao/releases",
|
||||
"free_bytes": 95851429888,
|
||||
"free_inodes": 23794207
|
||||
},
|
||||
{
|
||||
"path": "/var/lib/investmoz-deploy",
|
||||
"free_bytes": 95851429888,
|
||||
"free_inodes": 23794207
|
||||
},
|
||||
{
|
||||
"path": "/var/backups",
|
||||
"free_bytes": 95851429888,
|
||||
"free_inodes": 23794207
|
||||
},
|
||||
{
|
||||
"path": "/tmp",
|
||||
"free_bytes": 95851429888,
|
||||
"free_inodes": 23794207
|
||||
},
|
||||
{
|
||||
"path": "/var/lib/docker",
|
||||
"free_bytes": 95851429888,
|
||||
"free_inodes": 23794207
|
||||
}
|
||||
],
|
||||
"backup_age_seconds": 578.120001,
|
||||
"backup_dump_sha256": "45107e48e18ab2b9a78af08374fe3bfa0af216f34433c10fc81f264400e331ce",
|
||||
"restore_verified": true,
|
||||
"tables_verified": 174,
|
||||
"files_verified": 369,
|
||||
"backup_paths": {
|
||||
"dump": "/var/backups/investmoz-sequential-20261027/proactive/backup/database.dump",
|
||||
"files": "/var/backups/investmoz-sequential-20261027/proactive/backup/files-before.tar.gz"
|
||||
},
|
||||
"active_writes": 0
|
||||
},
|
||||
"source": {
|
||||
"phase": "after",
|
||||
"installed": "9fda6d6e12832a3cf25e7314f1f895dd430238c5",
|
||||
"files_verified": 1000,
|
||||
"source_matches_Git": true,
|
||||
"active_writes": 0
|
||||
},
|
||||
"schema_permissions": {
|
||||
"schema_sha256": "5045b3e51d2185377aa3d9ac7fa07a44fad3ec620cdfa83ffe31c8884ee1601d",
|
||||
"roles_grants_sha256": "1530c33cb016e67bfdc64891ddfce7f185b13b56b2cd87c012871a685e118109",
|
||||
"filesystem_permissions": {
|
||||
".env": {
|
||||
"mode": 416,
|
||||
"uid": 1001,
|
||||
"gid": 33
|
||||
},
|
||||
"storage": {
|
||||
"mode": 1533,
|
||||
"uid": 1001,
|
||||
"gid": 33
|
||||
},
|
||||
"storage/app": {
|
||||
"mode": 1533,
|
||||
"uid": 1001,
|
||||
"gid": 33
|
||||
},
|
||||
"bootstrap/cache": {
|
||||
"mode": 1528,
|
||||
"uid": 1001,
|
||||
"gid": 33
|
||||
}
|
||||
},
|
||||
"comparison_identity": "postgres;catalogue read only;no passwords collected",
|
||||
"unchanged": true
|
||||
},
|
||||
"preservation": {
|
||||
"checked_at": "2026-10-27T10:50:49.400700+00:00",
|
||||
"installed": "9fda6d6e12832a3cf25e7314f1f895dd430238c5",
|
||||
"business_tables_unchanged": 172,
|
||||
"total_tables": 174,
|
||||
"operational_tables_to_reconcile": {
|
||||
"cache": {
|
||||
"before": {
|
||||
"rows": 106,
|
||||
"checksum": "a9c2d7df6ecc8cabba35d5c6f1d019c5"
|
||||
},
|
||||
"after": {
|
||||
"rows": 106,
|
||||
"checksum": "db78f82003db53db664667b8e6f46088"
|
||||
}
|
||||
},
|
||||
"sessions": {
|
||||
"before": {
|
||||
"rows": 15,
|
||||
"checksum": "4df23659bf65acacc88e1d84d095a243"
|
||||
},
|
||||
"after": {
|
||||
"rows": 20,
|
||||
"checksum": "64d48443c5027a2fc08615f7528126b5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"persistent_files_unchanged": 369,
|
||||
"original_configuration_preserved": true,
|
||||
"synthetic_QA_storage_not_served": true,
|
||||
"QA_storage_saved_outside_active": "/var/lib/investmoz-deploy/qa-runtime/9fda6d6e12832a3cf25e7314f1f895dd430238c5-88fc859c",
|
||||
"fixture_or_content_imports": 0,
|
||||
"migrations": 0,
|
||||
"comparison_identity": "postgres;UTC;consistent before snapshot and after read-only transaction",
|
||||
"active_database_writes": 0
|
||||
},
|
||||
"operational_delta": {
|
||||
"status": "operational_delta_reconciled",
|
||||
"comparison_identity": "postgres before/after;isolated restored baseline;no RLS visibility inference",
|
||||
"cache": "queue:restart;requestthrottle;identical home-sector cached value with refreshed expiry(HomeController remember10min);no deletedcache",
|
||||
"sessions": "anonymous sessions from delivery user-agent or exactUA/time correlated with nginx public request;deleted sessions individually expired beyond configured lifetime,normal database session garbage collection(config/session lottery2/100)",
|
||||
"delta": {
|
||||
"cache": {
|
||||
"before_rows": 106,
|
||||
"after_rows": 106,
|
||||
"changes": [
|
||||
{
|
||||
"key_sha256": "7f7dd83f47f5bf404d55e6e9f1367948721579970b195680645e356031a070ff",
|
||||
"kind": "updated",
|
||||
"fields": [
|
||||
"value",
|
||||
"expiration"
|
||||
],
|
||||
"key_pattern": "investemoz-cache-illuminate:queue:restart",
|
||||
"is_queue_restart_key": true,
|
||||
"expiration_unchanged": false,
|
||||
"throttle_timer_pair": false,
|
||||
"same_value_home_cache_expiry_refresh": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"sessions": {
|
||||
"before_rows": 15,
|
||||
"after_rows": 20,
|
||||
"changes": [
|
||||
{
|
||||
"key_sha256": "f78ee9e6f589e1bbda4730d4e71a90c9ad082cc25776c415b6cc815ce4b4d491",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "7eb58435f5d60610be81945c0da4331a1dbfda680f7da3f107dbbeb6b60c39df",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "1d5026f13ace3894011db54d801f72cd76f9b138d54fe2f7b89f579743282db6",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "56820cc8560f13a2c0962a973bd05624712648c2beffd2058dfbae6c9103c7fa",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "a91337e37e71b520dd6d621b0ef7bbf68a9a20d099198ecd8edfdc365a9f52f1",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"business_tables_to_confirm_separately": 172,
|
||||
"active_data_writes_by_comparison": 0,
|
||||
"restore_network": "none"
|
||||
},
|
||||
"public_delta": {
|
||||
"stage": "proactive",
|
||||
"served_revision": {
|
||||
"commit": "9fda6d6e12832a3cf25e7314f1f895dd430238c5",
|
||||
"branch": "main",
|
||||
"built_at": "2026-10-27T10:50:00.471464+00:00",
|
||||
"manifest_sha256": "40de38cbf74b475f73de825bfd16af6fabe339faad4817e2857fd9ae36ec7cc0"
|
||||
},
|
||||
"manifest_hash_matches": true,
|
||||
"compiled_delta_markers": {
|
||||
"IRPC-annual-settlement/1": [
|
||||
"assets/project-dossier-DFH3NDYe.js"
|
||||
],
|
||||
"Preparar saldo anual L8": [
|
||||
"assets/services-CV6TWKfT.js"
|
||||
],
|
||||
"Calcular e guardar saldo anual L8": [
|
||||
"assets/services-CV6TWKfT.js"
|
||||
],
|
||||
"Saldo anual simulado": [
|
||||
"assets/project-dossier-DFH3NDYe.js"
|
||||
],
|
||||
"PEC e créditos separados": [
|
||||
"assets/project-dossier-DFH3NDYe.js"
|
||||
],
|
||||
"Território e sector": [
|
||||
"assets/proactive-investment-CPkgrUFY.js"
|
||||
],
|
||||
"Preparar análise e ideia": [
|
||||
"assets/proactive-investment-CPkgrUFY.js"
|
||||
],
|
||||
"Hipótese analítica": [
|
||||
"assets/project-dossier-DFH3NDYe.js",
|
||||
"assets/proactive-investment-CPkgrUFY.js"
|
||||
],
|
||||
"Documento existente associado": [
|
||||
"assets/proactive-investment-CPkgrUFY.js"
|
||||
],
|
||||
"Origem privada autorizada deste dossier": [
|
||||
"assets/proactive-investment-CPkgrUFY.js"
|
||||
]
|
||||
},
|
||||
"assets": [
|
||||
{
|
||||
"file": "assets/project-dossier-DFH3NDYe.js",
|
||||
"status": 200,
|
||||
"sha256": "62f4e47f6efbb7916663604450ee427401080fad6f9332be2cd0dc017866b85a"
|
||||
},
|
||||
{
|
||||
"file": "assets/project-dossier-document-CgBtS4qw.js",
|
||||
"status": 200,
|
||||
"sha256": "fb9aa7e305891719ae49cf8bbcee05b3114c05c8ee1df20238964fddb56721dd"
|
||||
},
|
||||
{
|
||||
"file": "assets/catalogue-DRXXB4Y7.js",
|
||||
"status": 200,
|
||||
"sha256": "66a2ae7c819f1ac36ffd37e8456c3cfd2537c820845b20eddb4dd7bfaf4a32a2"
|
||||
},
|
||||
{
|
||||
"file": "assets/services-CV6TWKfT.js",
|
||||
"status": 200,
|
||||
"sha256": "656da0adacd9141fcd9b1e82ab9959a62ea381a3815e183c5535801c95d92dff"
|
||||
},
|
||||
{
|
||||
"file": "assets/simulation-form-zFZ4YNrB.js",
|
||||
"status": 200,
|
||||
"sha256": "338068b86cce1c3215ee71b1328a6cb2f754b903ed78ff1886f8554df506d2d6"
|
||||
},
|
||||
{
|
||||
"file": "assets/proactive-investment-CPkgrUFY.js",
|
||||
"status": 200,
|
||||
"sha256": "288cd1005a05c82241a1ddd8c5bb446e297ead7b7605060cec80d268726522ac"
|
||||
},
|
||||
{
|
||||
"file": "assets/opportunity-draft-Da2FBHoN.js",
|
||||
"status": 200,
|
||||
"sha256": "ba50f61a39693ebebbe30ba1937d45c92b80f81ab9a98418ef75662f893cdfb2"
|
||||
},
|
||||
{
|
||||
"file": "assets/detail-BfSgTC5d.js",
|
||||
"status": 200,
|
||||
"sha256": "234657a2e5ebad7985d78ea49c5536c31e4aac9a143a02e7682d898e47cee530"
|
||||
},
|
||||
{
|
||||
"file": "assets/show-CJYhqIE4.js",
|
||||
"status": 200,
|
||||
"sha256": "6579df14a154c98235330f550e13bee20e74f19517cc1d166ae26322f56c74f9"
|
||||
}
|
||||
],
|
||||
"simulator_status": 200,
|
||||
"served_component": "portal/services",
|
||||
"public_catalogue_delta": {
|
||||
"ideas": {
|
||||
"status": 200,
|
||||
"component": "investment/catalogue"
|
||||
},
|
||||
"needs": {
|
||||
"status": 200,
|
||||
"component": "investment/catalogue"
|
||||
},
|
||||
"exists": {
|
||||
"status": 200,
|
||||
"component": "investment/catalogue"
|
||||
}
|
||||
},
|
||||
"private_operations": 0,
|
||||
"QA_fixture_imports": 0,
|
||||
"accepted_QA_proofs_reused": true,
|
||||
"method": "Read-only served revision/manifest/compiled delta;no synthetic active records or publication"
|
||||
},
|
||||
"backup_restore_verified": true
|
||||
}
|
||||
},
|
||||
"main": "9fda6d6e12832a3cf25e7314f1f895dd430238c5",
|
||||
"final_served_revision": "9fda6d6e12832a3cf25e7314f1f895dd430238c5",
|
||||
"private_pilots_preserved_no_publication": true,
|
||||
"fixtures_not_imported_into_active_database": true,
|
||||
"QA_runtime_storage_outside_active_release": true,
|
||||
"active_schema_migrations": 0,
|
||||
"new_grants": false,
|
||||
"SGF_TEMPO": "untouched",
|
||||
"accepted_business_proofs": "reused;post-install checks limited to deltas;existing installer automated checks retained",
|
||||
"prior_capacity_GiB": 91.60694122314453,
|
||||
"second_delivery_retry": {
|
||||
"initial_candidate": "56c0c6a7caa65a4cac54e17f9d455739f8eb6198",
|
||||
"initial_candidate_activated": false,
|
||||
"active_L8_preserved": true,
|
||||
"reason": "One frontend test depended on ignored local outputs/proactive-investment-20261027/recovered-context.json",
|
||||
"correction": "9fda6d6e12832a3cf25e7314f1f895dd430238c5 changes only the test and a minimal synthetic public API projection;no profile,pilot,credentials or live records",
|
||||
"application_runtime_identical_to_56c0c6a7": true,
|
||||
"directed_portability_test": "passed;one test;no skipped test",
|
||||
"existing_installer_guards_retained": true
|
||||
}
|
||||
}
|
||||
@@ -2524,7 +2524,7 @@ As estimativas são **horas de trabalho técnico**, não calendário nem mediç
|
||||
| L5 — retenções IRPC e guias | Documentos de compras/vendas e guia M/39 como exemplar | Rendimento pago/recebido, entidade que retém, residente/não residente/EP, evento temporal, excepções e casos digitais/e-money; guia e comprovativo ligados à operação | Contrapartes, facturas, pagamentos, período, moeda, oportunidade | Artigos aplicáveis por rendimento, taxa/convenção se invocada; corrigir remissões do exemplar. Sem submissão electrónica AT presumida | 32–56 h |
|
||||
| L6 — IRPC anual: regime normal | Disponível5aaf1a56:percurso normal demonstrado atécolecta,períodos/moedas e preparação parcialM20/M22 | Operacional no âmbito demonstrado por decisãoTempo26/10;limitações explícitas;5grupos código+5documentais preservados;sem conclusão integral | Plano financeiro,dados comuns,activos/livros e histórico;retenções/pagamentos/créditos anuaisL8 | Pendências bloqueiam só os casos dependentes;L7/L8 próprios | Estimativa inicial48–80h;saldo não reestimado |
|
||||
| L7 — IRPC: tratamentos específicos | f8d57bdd instalado;632dc069 e a7434cd2 preservados no percurso | Cumulações demonstradas disponíveis;7famílias de saldo e documentos por caso discriminados;L7parcial | Activos/períodos/saldos/benefícios reutilizados;ordem,limites e incompatibilidades explícitos | Outras combinações/específicos/mistos/transporte próprios;L8delimitado separadamente | Inicial48–80h;saldo não reestimado |
|
||||
| L8 — IRPC: liquidação e pagamentos | Primeiro incremento delimitado,não implementado:colecta válida→retenções recebidas por conta/PPC comprovados→saldo simulado | Residente geral,ano2026/MZN;ausências documentadas dePEC/créditos/autónomos fora do recorte;gravação/recuperação/relatório a demonstrar | Reutiliza colectaL6/L7,L5received/effective/on_account ecomprovativos do próprio sujeito/período | Não abater retenções por conta de terceiros;PEC/créditos/limites próprios;regra de mínimo/versionamento a aplicar;camposM20M22 só bloqueiam transporte afectado | Inicial32–56h;não é previsão actual do recorte |
|
||||
| L8 — IRPC: liquidação e pagamentos | PEC comprovado corrente de7fdb6b42 entregue eme182d1d7,sobre669cb897;saldo RF/PPC preservado | Residente geral2026/MZN;deduçãoPEC até à colecta,sem reembolso do excedente;L8parcial | ColectaL6/L7,L5RF,PPC/PEC comprovados,documentos e histórico anual | Créditos próprios,transporte/obrigaçãoPEC,autónomos,períodos/moedas eM20M22 próprios;primeiro crédito internacional delimitado,sem código novo | Inicial32–56h;não é previsão actual do saldo |
|
||||
| L9 — INSS: contribuição social | Massa salarial agregada e referência TCO3%+4% | Trabalhadores, remuneração/base contributiva, exclusões, empregador/trabalhador, TCO/TCP, declaração e encargos por período | Equipa/custos, entidade e calendário; cadastro de pessoa partilhado com L10 | Regulamento e alterações aplicáveis, natureza do vínculo; confirmar base antes da taxa. Contribuição social, distinta dos impostos | 24–40 h |
|
||||
| L10 — IRPS: retenção mensal e líquido | Entrada IRPS, custos salariais e escalões anuais documentais | Tabela mensal própria, remunerações/benefícios, situação pessoal relevante, retenção, salário líquido e contribuição ligada a L9 | Mesmo trabalhador/folha salarial, pagamentos e retenções | Tabela mensal do período, residência e condições familiares necessárias; não dividir escalões anuais por12 | 40–64 h |
|
||||
| L11 — IRPS: apuramento anual e outras categorias | Escalões anuais e exemplares M/19/M/20/anexos | Categorias aplicáveis aos rendimentos documentados, actividade independente, capitais/prediais/mais-valias, englobamento/tratamento autónomo, deduções, retenções e saldo | Dados pessoais autorizados, rendimentos/despesas, activos e L10 | Código IRPS/Lei11/2025, âmbito/categoria/período e deduções pessoais; diferenças de edição dos anexos. Não copiar regras IRPC | 48–80 h |
|
||||
@@ -6924,3 +6924,245 @@ O relatório comum tinha um conflito de imports. Foram conservados ambos e as ch
|
||||
Retomar na sessão local com SSH autenticado, usando a autorização já registada: medir espaço/inodes na partição efectiva, comparar com compilação e backup, conferir main e revisão activa, criar e ensaiar backup fresco, instalar L8 e conferir apenas o seu delta. Depois repetir as guardas necessárias para a candidata conjunta e conferir o delta proactivo preservando L8. Manter a versão activa até a nova estar pronta. Excluir fixtures QA dos inputs de instalação; não executar seeders/importações de amostras, não publicar pilotos privados nem usar o pacote QA como cópia da base activa. SGF TEMPO separado.
|
||||
|
||||
Recibo de preparação: `docs/investmoz/unification/sequential-delivery-preparation-20261027.json`. Provas e instrução de passagem: `outputs/sequential-delivery-20261027/`. Os recibos de execução e as duas revisões servidas serão registados apenas depois de execução real na sessão autenticada; este recibo não os substitui.
|
||||
|
||||
|
||||
## 27/10/2026 — correcção delimitada da navegação dos sectores
|
||||
|
||||
Reprodução local autorizada da intenção de `88b39b527859bd420f2e223a7afea89bea38de54`, indisponível no origin/outro ambiente. Base exacta `9fda6d6e12832a3cf25e7314f1f895dd430238c5`; novo ramo `codex/sector-navigation-fix-local-20261027`. Não se afirma identidade binária com o pacote externo. QA concluída; instalação ainda pendente nesta revisão documental.
|
||||
|
||||
**Causa e alteração:** o índice `/sectores` carregava `sectorDetails` para todos os sectores e apresentava as páginas completas abaixo dos cartões; os cartões passavam a âncoras locais. Removidos esse carregamento e a apresentação agregada. Os cartões usam `sector.url`, preservando as páginas individuais, a especialização Fintech e os caminhos históricos existentes. O índice mantém descrições curtas, contagens e acesso aos sectores, sem repetir guias e oportunidades de todos eles.
|
||||
|
||||
**Preservação:** página individual, `SectorContent`, `SectorEntry`, `SectorInvestment`, `SectorOrganization`, filtros, dados mestres, originais e continuidade para o mesmo dossier não foram alterados. PEC `7fdb6b42` continua no seu ramo QA; não é integrado nesta correcção. L8 instalado e percurso proactivo de `9fda6d6e` preservados. Songo/Gurué privados; sem fixtures QA no activo, publicação, importações, migrations ou alterações de permissões.
|
||||
|
||||
**Delta demonstrado:** actualizados os dois testes afectados de `SectorCatalogueContentTest` e acrescentado um caso de isolamento/regresso/recarregamento:3 testes/319 asserções, base isolada `proactive_investment_20261027_test`, loopback55441, transacções com rollback; sem `migrate:fresh`. Wayfinder `--with-form`, TypeScript e build QA aprovados. Navegador real:10 cartões, acesso separado a Agronegócio e Turismo, oportunidades próprias, filtro Agronegócio mantido no catálogo, recarregamento da página individual e regresso ao índice sem repetição. Entrada Turismo para o dossier conserva o contexto sectorial na ligação e encaminha o visitante para a autenticação existente; não se repetiu a prova completa de gravação do dossier já aceite. A amostra QA não tem guias editoriais carregados; a preservação dos guias assenta na comparação dos consumidores/fontes, e será conferida no destino sem republicar conteúdo. A captura de imagem do browser devolveu «0 width»; árvore de acessibilidade e testes guardados, sem alegação de captura visual inexistente.
|
||||
|
||||
Requisitos existentes actualizados apenas neste delta: `MASTER-471012ab-0007` e `MASTER-9dee08d0-0017`; não se encerra automaticamente o âmbito mais amplo dessas linhas. Recibo QA: `docs/investmoz/unification/sector-navigation-qa-20261027.json`. Provas locais: `outputs/sector-navigation-fix-20261027/`. Entrega autorizada seguirá o processo Git existente, com capacidade, backup/restauro fresco, comparação de fonte/dados/schema/configuração/permissões e revisão efectivamente servida.
|
||||
|
||||
|
||||
## 27/10/2026 — L8 e percurso proactivo instalados pela sequência autorizada
|
||||
|
||||
Entrega1: **L8 `c85e772d`**, anterior `f8d57bdd`. Entrega2, apenas após conferir a primeira: **percurso proactivo `9fda6d6e`**, incluindo `56c0c6a7` e preservando L8. As duas revisões foram conferidas no registo do instalador e no recurso público `/build/revision.json`, com hash do manifest e marcadores compilados dos deltas. `c85e772d` conserva o código aceite de `14360748`; o merge proactivo mantém ambos os fornecedores de linhas do relatório. A referência final servida passa a `9fda6d6e`.
|
||||
|
||||
A tentativa proactiva em `56c0c6a7` não foi activada: um teste dependia de um ficheiro de outputs existente apenas no QA local. L8 permaneceu em serviço. A correcção `9fda6d6e` torna esse teste portátil com um exemplar mínimo sintético da projecção pública da API, sem perfis, credenciais ou pilotos reais e sem escrita em outputs. O teste dirigido passou; o código da aplicação é byte a byte idêntico a `56c0c6a7`. Não se transportou a base QA nem se saltou a guarda do instalador. A nova tentativa usou o backup/restauro ainda fresco, com capacidade e fonte activa novamente conferidas.
|
||||
|
||||
A ligação SSH foi restabelecida pelo responsável na janela local, sem credenciais na conversa ou nos recibos. A medição inicial encontrou91,6GiB livres e inodes suficientes; a capacidade foi novamente conferida depois de cada backup. Instalador existente e suas guardas preservados; a versão anterior permaneceu activa até a nova release estar pronta. Os controlos automáticos do instalador foram mantidos, sem nova ronda manual das provas funcionais/económicas aceites.
|
||||
|
||||
**Backup/restauro fresco em cada etapa:** `/var/backups/investmoz-sequential-20261027/l8/backup` e `/var/backups/investmoz-sequential-20261027/proactive/backup`. Em cada uma, dump consistente e369ficheiros persistentes/configuração verificados; restauro das174tabelas num contentor sem rede, porta55441, com contagens/checksums iguais. Recibos `backup-receipt.json`, `restore-receipt.json`, `schema-permissions-before.json`, `schema-permissions-after.json`, `preservation-receipt.json` e `operational-delta-receipt.json` conservados nesses directórios. Hashes exactos no recibo consolidado.
|
||||
|
||||
**Preservação demonstrada:**172tabelas de negócio intactas em cada comparação; apenas cache/sessões operacionais reconciliados individualmente por identidade/timeouts e pedidos públicos de entrega. Schema, papéis, grants, permissões e originais persistentes conservados. Fontes instaladas comparadas aos commits:996ficheiros após L8 e1000após o proactivo. Sem migrations na base activa, importação de amostras QA, seeders ou publicação dos pilotos. O instalador manteve base e storage sintéticos isolados; o storage QA fica fora da árvore servida, e a release usa os mesmos `.env` e storage persistentes por ligação.
|
||||
|
||||
**Âmbito:** L8 continua parcial, com PEC e créditos de regra própria separados; os resultados continuam simulações. Songo permanece privado e pendente de respostas externas; Gurué continua hipótese agrícola privada. Disponibilização técnica não confirma clientes, procura, sustentabilidade ou publicação comercial. L3, ISPC, câmbio, L6/L7 e SGF TEMPO preservados.
|
||||
|
||||
Recibo consolidado: `docs/investmoz/unification/sequential-delivery-installed-20261027.json`. Provas locais: `outputs/l8-proactive-install-20261027/`. Os recibos históricos de preparação/suspensão mantêm-se como histórico e deixam de representar pendência de instalação destas duas candidatas.
|
||||
|
||||
Recibo remoto consolidado: `/var/backups/investmoz-sequential-20261027/installed-receipt.json`;SHA256 `62584b5abb50a05303a24379a0563e8dc147e25e682dc3566bb8edf015ba9d0b`. A fixture sintética portátil é apenas input de teste, sem importação na aplicação e sem dados dos pilotos.
|
||||
|
||||
|
||||
## 27/10/2026 — avaliação de utilização pela equipa, sem Codex
|
||||
|
||||
**Estado:** roteiro preparado; execução real pela equipa e resultados ainda por observar. O recibo da instalação mantém `9fda6d6e` como referência entregue, incluindo L8 e o percurso proactivo. As provas técnicas aceites mantêm-se válidas. Este exercício avalia utilização e clareza; não constitui outra instalação, ronda de testes técnicos ou nova aprovação obrigatória da entrega.
|
||||
|
||||
### Exercício para a equipa
|
||||
|
||||
Usar a própria conta e o perfil operacional já autorizado no [portal](https://teste-investidor.tempo.co.mz/), pela interface normal. Escolher uma análise privada de trabalho sobre um tema real, com uma fonte já disponível e autorizada. Preservar Songo e Gurué, que aguardam informação comercial; não é necessário obter novas respostas externas para fazer este exercício. Não usar contas ou amostras QA na aplicação activa. O guia de botões existente em «Guia curto da equipa» pode ser consultado; registar essa consulta para distinguir clareza da interface de apoio documental.
|
||||
|
||||
1. **Preparar a análise.** Escolher sector e território existentes, identificar o assunto e guardar como preparação privada, usando apenas a informação conhecida.
|
||||
2. **Registar evidências.** Identificar fonte/exemplar, trecho, período, abrangência e limitações. Separar facto documentado da interpretação. Associar um documento disponível quando aplicável; recuperar o original pelo gestor documental da ficha após o scan. A falta de resposta comercial permanece informação por confirmar.
|
||||
3. **Estruturar a ideia.** Descrever a solução complementar, quem poderá beneficiar, quem poderá contratar/pagar, condições, recursos e próxima acção. Manter hipótese onde a procura não esteja demonstrada; montante, frequência e preço desconhecidos não passam a zero.
|
||||
4. **Guardar e recuperar.** Sair da ficha e voltar a abri-la. Conferir que análise, evidências, documento associado e pendências são os que foram guardados, sem recorrer a scripts ou intervenção do Codex.
|
||||
5. **Levar ao dossier.** Usar a continuação do investimento pela ficha. Escolher o dossier existente quando for o mesmo investimento; criar outro apenas para um investimento novo. Rever a prévia, conservar o título deliberado e confirmar. Reabrir o dossier e localizar a ideia, a origem e as pendências; o original continua acessível pelos recursos da ficha de origem, sem cópia automática para o dossier.
|
||||
6. **Rever sem duplicar.** Actualizar uma pendência da própria análise com informação já conhecida e continuar o mesmo dossier. Conferir a nova versão após prévia/confirmação e a conservação da anterior. Guardar/confirmar não publica a análise, o anexo ou o dossier.
|
||||
|
||||
**O que observar:** se a pessoa encontra a próxima acção, compreende facto versus hipótese, consegue recuperar o que guardou e continuar o mesmo investimento sem ajuda do Codex. Anotar o que foi conseguido directamente, com o guia, com ajuda de outra pessoa, o que bloqueou e o que não foi tentado. Um impedimento concreto não obriga a repetir os passos já conseguidos; podem continuar os passos independentes.
|
||||
|
||||
### Devolução curta da avaliação
|
||||
|
||||
Registar por participante, sem enviar palavras-passe ou documentos privados nesta conversa: data e perfil utilizado; referências internas da análise e do dossier; passos conseguidos e tipo de apoio consultado; passo/campo onde surgiu dificuldade; mensagem exacta ou descrição do comportamento; resultado esperado. Distinguir acesso/perfil, linguagem/descoberta de acções, compreensão dos dados e perda ou erro de gravação/recuperação. Duração e sugestões são opcionais. Os identificadores só são partilhados no canal interno autorizado.
|
||||
|
||||
O coordenador devolve estes resultados pelo canal habitual da equipa. A avaliação só será marcada como observada depois dessa execução real; preparar o roteiro ou uma demonstração pelo Codex não a substitui. Nesta passagem não houve participante observado, análise criada no portal, alteração de conta/permissões, mensagem enviada à equipa ou publicação. As correcções seguintes serão delimitadas pelos obstáculos efectivamente relatados, sem reabrir provas aceites.
|
||||
|
||||
**Âmbitos preservados:** Songo e Gurué privados e pendentes de evidências comerciais. L8 permanece parcial; PEC e créditos com regras próprias continuam separados e não fazem parte deste exercício de utilização. O resultado técnico entregue não declara o IRPC completo nem valida comercialmente os pilotos.
|
||||
|
||||
Registo de preparação e resultados ainda em aberto: `docs/investmoz/unification/proactive-team-use-evaluation-20261027.json`. Não é um recibo de execução pela equipa.
|
||||
|
||||
|
||||
## 27/10/2026 — L8: PEC pago no próprio exercício, incremento em QA
|
||||
|
||||
**Estado:** implementado e demonstrado em QA, ainda sem instalação. O portal mantém a entrega `9fda6d6e`, segundo o recibo anterior; esta passagem não acedeu ao servidor nem alterou `main`. A avaliação real da equipa sobre o percurso proactivo continua pendente de retorno; Songo e Gurué permanecem privados.
|
||||
|
||||
**Recorte:** residente no regime geral, ano civil 2026, MZN; colecta corrente válida L6/L7, créditos anteriores ausentes ou conhecidos zero, sem imposto autónomo positivo. PEC é pagamento efectivamente comprovado do próprio sujeito/exercício, não estimativa de tesouraria. Reutilizados o livro anual, o vínculo à colecta, os PPC, as retenções L5 e o mecanismo de gravação/recuperação/relatório existentes.
|
||||
|
||||
**Regra implementada:** CIRPC, art. 66(1–2), exemplar oficial Lei34/2007, PDF18/p768-(72), conferido visualmente. A dedução PEC é a menor entre as parcelas disponíveis comprovadas e a colecta disponível antes de retenções/PPC. A colecta após PEC não fica negativa. O excedente PEC é apresentado como parcela seleccionada não deduzida: **não constitui reembolso nem transporte oficial criado pelo simulador**. Retenções/PPC ainda podem originar saldo simulado a recuperar. Créditos precedentes positivos ou desconhecidos mantêm o caso indeterminado, sem ordem ou limite presumidos.
|
||||
|
||||
O detalhe PEC é uma extensão opcional versionada do snapshot anual. A acção explícita «Preparar detalhe PEC» reutiliza estado/suporte anteriores, conservando o resumo e os resultados históricos. Cada prova identifica emitente/documento, pagamento efectivo, NUIT, exercício/data, moeda, montante comprovado, utilizações anteriores documentadas e parcela seleccionada. Zero utilizado exige valor e suporte; vazio não vira zero. Ausência declarada, pagamento conhecido e informação em falta permanecem distintos. Contradições não são apagadas. A identidade física do comprovativo ignora mudanças de data e confere PEC/PPC/retenções neste e noutros contextos do mesmo snapshot; parcelas mais utilização anterior não podem ultrapassar o comprovado. Não há consulta/consumo de conta fiscal oficial nem deduplicação global entre dossiers independentes.
|
||||
|
||||
| Caso sintético novo | Efeito demonstrado |
|
||||
| --- | --- |
|
||||
| Colecta1 408; PEC200; PPC500; ausência de retenções | Dedução PEC200; colecta após PEC1 208; saldo simulado a pagar708 MZN. |
|
||||
| Mesmo caso com retenção L5 efectivamente sofrida300 | Saldo simulado408 MZN; prova L5 reutilizada, sem alterar o cálculo L5. |
|
||||
| Colecta1 408; PEC2 000; sem PPC/retenções | Dedução1 408; saldo zero; PEC592 não deduzido, sem reembolso PEC. |
|
||||
| Mesmo PEC2 000 e PPC500 | Saldo simulado a recuperar500; PEC592 continua separado. |
|
||||
| Prova PEC300; já utilizado100; parcela200; sem PPC/retenções | Saldo simulado a pagar1 208; utilizações anteriores não deduzidas outra vez. |
|
||||
| Utilização anterior desconhecida, prova incompatível/duplicada ou crédito precedente positivo | Saldo indeterminado; subtotal conhecido não substitui os dados em falta. |
|
||||
|
||||
**Provas do delta:**12 testes novos PEC;19 testes do módulo anual directamente afectado preservados e aprovados. API privada:1 teste/102 asserções, cinco casos, base isolada `proactive_investment_20261027_test` em loopback55441 e transacção com rollback. Os cinco snapshots recuperados geraram HTML e dossier concordantes; histórico anterior conservado. Hashes dos registos existentes em sete tabelas conservaram55 oportunidades,64 documentos,1 vínculo documental,19 rascunhos,40 utilizadores,3 organizações e409 eventos anteriores. Não houve nova conta/grant, publicação ou importação de pilotos. Wayfinder pelo procedimento existente, TypeScript e build isolado aprovados. Interface PEC implementada; nesta passagem não se declara nova prova manual de utilização por browser/equipa. Não se repetiram suites independentes L3/ISPC/câmbio/L5–L7.30 PDFs do arquivo comparados byte a byte ao baseline `dc14f01e`, sem alterações; schema/configuração e módulos proactivos preservados.
|
||||
|
||||
### Saldo concreto do PEC e dependências de L8
|
||||
|
||||
| Código ainda por implementar | Exemplo / condição concreta de retoma |
|
||||
| --- | --- |
|
||||
| Cálculo da obrigação PEC e prestações | Distinguir volume anterior, PPC do ano anterior e PEC pago. Exemplo de domínio a desenvolver:volume10 000 000 ×0,5%=50 000, antes dos PPC anteriores. Reutilizar vendas/pagamentos só após reconciliação de entidade/exercício; aplicar limites/calendário da edição pertinente e representar dispensas/rectificações próprias. Nenhum vencimento/valor a pagar foi gerado neste incremento. |
|
||||
| Transporte, ordem de utilização e caducidade de PEC de exercícios anteriores | Exemplo:PEC2025 ainda não deduzido no fecho2026. Implementar proveniência por ano, utilizações, remanescente e prazo; não aceitar esse saldo como PEC2026 nem criar crédito oficial a partir dos592 do exemplo. |
|
||||
| Outros créditos, imposto autónomo, períodos especiais, outras moedas e transporte/submissão M20/M22 | Mantêm os grupos L8 anteriores, com condições e regras próprias; o PEC corrente independente não espera por esses casos. O mecanismo cambial existente será reutilizado quando corresponder a regra do pagamento. |
|
||||
|
||||
| Esclarecimento documental aguardado | Exemplo / condição concreta de retoma |
|
||||
| --- | --- |
|
||||
| Edição regulamentar após o anúncio governamental de alterações IRPC em2026 | Recuperar diploma publicado, artigos alterados e entrada em vigor para os calendários/obrigações afectados. O anúncio não substitui norma nem activa parâmetros novos. Não se reabrem as fontes aceites ou cálculos independentes do art66 já demonstrados. |
|
||||
| Aplicação do prazo do art66 aos saldos anteriores e eventuais regras transitórias | Fixar para um PEC2025 as datas/último exercício elegível e documentação das utilizações; manter a regra PEC distinta do transporte de cinco exercícios do crédito por dupla tributação internacional do art65. Não presumir reembolso, expiração ou saldo oficial. |
|
||||
| Crédito próprio específico positivo | Exemplo:imposto estrangeiro10 MZN exige rendimento/base/titular, imposto efectivamente pago, limite, convenção e período próprios antes de concorrer com PEC. Segue a dependência existente do crédito, não uma pesquisa geral de L5. |
|
||||
|
||||
Registo do incremento: `docs/investmoz/simulations/irpc-l8-pec-qa-20261027.json`; provas sintéticas locais em `outputs/irpc-l8-pec-20261027/`. **L8 permanece parcial.** Não há nova estimativa global de prazo nem declaração de IRPC integralmente concluído. Dificuldades da equipa serão tratadas como correcções concretas do percurso proactivo quando forem comunicadas.
|
||||
|
||||
|
||||
## 27/10/2026 — navegação dos sectores instalada e conferida
|
||||
|
||||
**Disponível no portal em `669cb8979b0c372659fc2ad3876aebc919d5df19`**, sobre `9fda6d6e`. Reproduzida localmente a correcção solicitada, com novo identificador; `88b39b5` não foi importado e deixa de ser condição de transferência/instalação. O índice mostra10cartões sem os detalhes completos de todos os sectores; cada cartão usa a URL própria. Páginas individuais, guias, especialização Fintech, filtro e continuidade do dossier preservados. Comparação pública antes/depois:os mesmos10cartões e as mesmas3páginas de Agronegócio/Turismo/Fintech, cada uma com1guia e respectivas oportunidades; só o índice perdeu `sectorDetails`, conforme pedido. Navegador no portal confirmou o índice servido sem repetição.
|
||||
|
||||
QA dirigida:3testes/319asserções, transacções com rollback em loopback55441;Wayfinder/tipos/build aprovados. Instalador existente preservado e controlos automáticos aprovados:PostgreSQL1/3106,Unit59/317,frontend1080pass/0fail,TypeScript ebuild31,42s. Integração Git normal,sem force;release anterior mantida até compilação e verificações prontas,seguida de troca atómica e conferência da revisão/manifest servidos. Delta de aplicação limitado a2ficheiros;1000ficheiros de fonte/recursos/documentos conferidos com o Git. Apenas2registos previstos da matriz actualizados,demais registos semanticamente intactos.
|
||||
|
||||
Capacidade antes:88,3GiB e23,76milhões de inodes livres;novamente conferida após backup. Backup fresco `/var/backups/investmoz-sector-navigation-20261027/backup`,dumpSHA256`9b2437017495fe449cfe0c869da62870bd0fc4bddde596660a37392b8dbdaaba`;restauro das174tabelas com contagens/checksums idênticos,em contentor sem rede,porta55441.369ficheiros persistentes/configuração verificados;172tabelas de negócio intactas depois da instalação;cache/sessões operacionais reconciliados por identidade/expiração/pedidos públicos. Schema,papéis,grants epermissões iguais. Sem migrations,importações ou publicação de pilotos;storageQA fica separado da release servida. Songo/Gurué privados;L8 e percurso proactivo anteriores preservados. PEC`7fdb6b42` continua emQA separado e não foi instalado nesta passagem. Avaliação real da equipa continua dependente do retorno dos participantes.
|
||||
|
||||
Recibo consolidado: `docs/investmoz/unification/sector-navigation-delivery-20261027.json`. Cópia remota: `/var/backups/investmoz-sector-navigation-20261027/installed-receipt.json`,SHA256`cd95142b7f85e40a0f86e0f7d3dca513e7bc208c89aa91b8e3ad213191d8465f`. Provas locais: `outputs/sector-navigation-delivery-20261027/`. Os registos anteriores de QA/transferência são históricos;esta correcção já não está pendente de instalação. Não se declara concluído o âmbito global dos requisitos sectoriais ou do simulador.
|
||||
|
||||
|
||||
## 27/10/2026 — PEC entregue e retoma dos créditos próprios de L8
|
||||
|
||||
**Instalado e conferido em `e182d1d79dc945f35d2079c6b8ca2ac93048cec3`**, incluindo o PEC aceite emQA`7fdb6b42` e preservando integralmente a correcção dos sectores`669cb897`. A reconciliação alterou apenas conflitos documentais, conservando os históricos das duas linhas e o recibo sectorial`4ee427ba`. Os4ficheiros de aplicação PEC ficaram idênticos à QA;os ficheiros sectoriais e seus testes idênticos ao instalado. Fonte/configuração/routes/schema fora do delta preservados. A matriz mantém as4actualizações documentais aceites da avaliação pela equipa,além da linhaPEC;sem nova revisão funcional do percurso proactivo.
|
||||
|
||||
**Âmbito entregue:** residente geral,ano civil2026,MZN;PEC efectivamente pago do próprio exercício,titular/data/prova/parcelas e uso anterior documentados. Dedução até à colecta disponível antes de retenções/PPC;créditos precedentes ausentes ou conhecidos zero. ExcedentePEC não gera reembolso nem saldo oficial transportado. Cálculo da obrigação,prestações,transporte/caducidade anteriores,créditos próprios positivos e imposto autónomo continuam separados. Informação em falta não vira zero;históricos não são recalculados. Reutilizadas as5recuperações e relatórios QA aceites:1 408−200−500=708 a pagar;PEC2 000 deduz1 408 e deixa592 não deduzidos;os500 a recuperar no caso comPPC provêm doPPC,sem reembolsar oPEC.
|
||||
|
||||
**Entrega:** capacidade87,16GiB e inodes suficientes antes,e novamente conferida após backup. Dump frescoSHA256`eff57769cda1e9d0f3d507348b57fc455c0bbc6e38beeb3688135109a6e219fd`,em`/var/backups/investmoz-irpc-l8-pec-20261027/backup`;restauro174tabelas com contagens/checksums iguais,contentor sem rede,porta55441.369ficheiros persistentes/configuração e172tabelas de negócio intactos;schema,papéis,grants e permissões iguais. Cache/sessões operacionais reconciliados:inclui1sessão anónima preexistente,sem mudança de identidade,com token efémero/URL anterior e IP/actividade actualizados,correlacionados individualmente comGET200de documento público pelo mesmoUA/IP/instante;nenhuma sessão sobrescrita pelo comparador. Sem migrations,importações,fixtures activas ou publicação de pilotos. Guardas do instalador mantidas;release anterior em serviço até troca atómica.1000fontes antes/1002depois conferidas comGit;delta de aplicação4ficheiros. Manifest e5marcadoresPEC servidos conferidos;10cartões dos sectores,3páginas/guias/oportunidades e filtroAgronegócio idênticos antes/depois.
|
||||
|
||||
**Provas reutilizadas:**12testesPEC+19do anual,API102asserções/5casos e relatórios aceites,sem outra ronda manual. Controlos obrigatórios do instalador aprovados:PostgreSQL1/3106,Unit59/317,frontend1092pass/0fail,TypeScript ebuild30,51s. Recibo consolidado:`docs/investmoz/simulations/irpc-l8-pec-delivery-20261027.json`;cópia remota:`/var/backups/investmoz-irpc-l8-pec-20261027/installed-receipt.json`,SHA256`a970266625807803ce73f1e63d0da1a540cff264ab88a19d3a8a387a85b26c34`. Provas locais:`outputs/irpc-l8-pec-delivery-20261027/`. Os recibosQA anteriores permanecem históricos;este PEC deixa de ser pendência de instalação.
|
||||
|
||||
### Retoma concreta — créditos próprios e saldo restante
|
||||
|
||||
O primeiro recorte para implementaçãoQA será o crédito por dupla tributação internacional do próprio exercício. Retoma desta passagem:fonte e contrato de dados/cálculo delimitados,**ainda sem novo código ou cálculo de crédito activado**. Reutilizado oCIRPC65,Lei34/2007PDF18/p768-(72),já no acervo aceite;ordem face aoPEC em66/RIRPC21. Não se exige revalidação genérica da fonte. Rendimento estrangeiro incluído na matéria colectável,imposto sobre o rendimento efectivamente pago,titular/país/exercício/prova e montante disponível devem corresponder. O crédito é limitado pela fracção nacional correspondente e,pela convenção quando aplicável;esse limite não pode ser presumido a partir do montante recebido. Exemplo sintético para provar o novo delta,**não implementado**:imposto estrangeiro10,limite nacional documentado8,ausência documentada deCDTaplicável→crédito8;colecta1 408→1 400;PEC200→1 200;PPC500→saldo700,sem retenções. Os2acima do limite não se tornam automaticamente crédito a transportar.
|
||||
|
||||
| Código ainda por implementar | Condição concreta de retoma |
|
||||
| --- | --- |
|
||||
| Qualificação,limites e dedução do crédito internacional corrente | Implementar o contrato acima,com estado conhecido/ausente/em falta,identidade da prova,ordem antes doPEC,limites e prevenção de uso duplicado comPEC/PPC/RF;demonstrar só novos efeitos,gravação/recuperação/relatório e preservação dos históricos. |
|
||||
| Crédito por dupla tributação económica e crédito de investimento/benefício | Grupos distintos;art64 e fundamentoCBFpróprio,qualificaçãoL7,titular/período,lucro/imposto/activos elegíveis,limite,cumulação e utilização documentados. Localização não concede benefício automaticamente. |
|
||||
| Transporte/utilização/caducidade dePEC e créditos anteriores | Proveniência por exercício e utilizações reais;PEC66 distinto do crédito65 e seu prazo próprio. Não confundir excesso sobre limite de elegibilidade com falta de colecta,nem criar saldos oficiais a partir de simulações. |
|
||||
| ObrigaçãoPPC/PEC,calendário,juros/multas/guias | Volume/PPC anteriores e demais factos pertinentes,edição normativa/vencimentos próprios;separado da dedução dePECjá pago agora entregue. |
|
||||
| Imposto autónomo,períodos especiais,outras moedas eM20/M22/submissão | Mantêm as dependências próprias já delimitadas;reutilizar periodização/câmbio quando a regra do caso estiver correspondida,sem compensação ou códigos oficiais presumidos. |
|
||||
|
||||
| Evidência/documento do caso em falta | Exemplo e condição concreta de retoma |
|
||||
| --- | --- |
|
||||
| Prova e inclusão do rendimento estrangeiro | Imposto10MZN sem rendimento identificado ou fracção nacional demonstrada:reutilizar dados/documentos existentes e identificar a correspondência;subtotal não resolve o saldo dependente. Não é falta de autenticação da fonte normativa. |
|
||||
| Convenção aplicável e limite | País/CDT conhecidos mas artigo,período ou imposto pago nos termos da convenção em falta:corresponder esses elementos;bloqueia apenas o caso dependente. |
|
||||
| Regulamentação2026para obrigação/calendário | Anúncio sem articulado publicado:retomar com diploma,artigos e vigência dos parâmetros afectados;sem bloquear oPECcorrente ou crédito independente cujo fundamento já está disponível. |
|
||||
|
||||
L8 permanece parcial. A avaliação real do percurso proactivo continua a aguardar retorno da equipa;dificuldades observadas serão correcções concretas. Songo eGurué permanecem privados;L3/ISPC/câmbio/L6/L7 eSGFTEMPO preservados. Sem nova estimativa global ou declaração de IRPC completo.
|
||||
|
||||
|
||||
## 27/10/2026 — Crédito internacional corrente: percurso implementado em QA
|
||||
|
||||
**Estado actual: implementado e demonstrado em QA, sem instalação.** Base `814fe0a9`, descendente do PEC instalado `e182d1d7` e da correcção dos sectores `669cb897`. Os registos anteriores de delimitação são históricos: o crédito corrente abaixo já tem código executável; os restantes grupos continuam separados. Ramo `codex/irpc-l8-international-credit-qa-20261027`. Recibo: `docs/investmoz/simulations/irpc-l8-international-credit-qa-20261027.json`.
|
||||
|
||||
**Âmbito e origem das restrições:** pedido expresso da Tempo para implementar o crédito internacional em QA, preservando o PEC e os históricos. Reutilizado CIRPC65(1–2), Lei34/2007, PDF18/p768-(72), e ordem antes do PEC em66/RIRPC21, do acervo aceite. Escolha incremental de implementação: residente geral, ano civil2026 e valores comprovados emMZN; inclusão do rendimento e fracção nacional documentadas, sem determinação automática dessa fracção, de vigência/aplicabilidade deCDT ou de conversão cambial. A selecção do país não concede crédito. A mudança de país/colecta exige reconfirmar a correspondência dos limites, conservando os valores.
|
||||
|
||||
**Percurso:** a interface permite preparar explicitamente o detalhe, reutilizando entidade/exercício/colecta e o resumo anterior quando este declara ausência ou zero; mantém esse resumo e os resultados anteriores. Uma posição por país/exercício agrega rendimentos e provas sob um só limite nacional. Regista natureza do imposto, pagamento efectivo, titular, suporte e utilização anterior real; data fora do exercício exige correspondência documental expressa, sem inferir transporte. O crédito elegível é o menor entre imposto seleccionado comprovado, fracção nacional e limiteCDT quando aplicável; desconta utilização anterior e limita a dedução à colecta. Seguem PEC, retenções ePPC existentes. Não transforma simulações históricas em utilização fiscal. Limites admitem fracção exacta; apresentação decimal não é regra de arredondamento fiscal.
|
||||
|
||||
| Caso sintético recuperado da API | Novo efeito demonstrado em MZN |
|
||||
| --- | --- |
|
||||
| Imposto estrangeiro10, limite nacional8, não aplicaçãoCDT documentada | Crédito8; 1 408−8=1 400; PEC200→1 200; PPC500→**700 a pagar, simulado**. Os2 acima do limite não são transporte. |
|
||||
| LimiteCDT documentado6 | Crédito6; saldo simulado702. Os dados de país/CDT são artificiais, sem afirmar uma convenção real. |
|
||||
| Utilização anterior comprovada3 | Elegível8, disponível5; saldo simulado703. Não somar utilizações de simulações. |
|
||||
| Crédito disponível2 000 e colecta1 408 | Deduz1 408; insuficiência592 identificada, sem transporte; PEC200 não deduzido. Os500 a recuperar provêm doPPC, sem reembolso do crédito/PEC. |
|
||||
| Ausência internacional expressa e documentada | Crédito0, saldo708; não confundir ausência com informação em falta. |
|
||||
| AplicabilidadeCDT em falta ou mesma prova usada comoPPC e imposto estrangeiro | Saldo e dedução dependentes indeterminados, com motivo; não preencher por presunção. |
|
||||
|
||||
**Provas do delta:**18testes novos e31anuais/PEC afectados,49aprovados; API privada1teste/171asserções com transacção revertida na baseQA guardada, porta55441. Sete casos porPOST/PUT/GET, recalculados após recuperação e exportados pelos relatóriosHTML e dossier existentes; histórico anterior congelado. Wayfinder `routes/actions`, TypeScript e compilação isolada aprovados. Esta passagem não inclui uma nova prova manual de utilização no browser nem uma avaliação pela equipa. Não se repetiram as suites independentes aceites. As7tabelas de registos anteriores ficaram intactas;30PDFs originais e11consumidores protegidos iguais ao instalado. Sem migrations, novas permissões ou configuração activa; nada enviado para a base do portal. Os testes usam fixtures sintéticas portáteis, sem dados comerciais ou pilotos reais.
|
||||
|
||||
**Prevenção de dupla dedução:** identidade física da prova por emitente/documento, independentemente da data; confronto entre imposto estrangeiro,RF,PPC ePEC e entre mapas do mesmo snapshot; país/entidade/exercício e montantes disponíveis coerentes. Posições repetidas do mesmo país/ano exigem consolidação, evitando repetir a quota nacional. Não existe consumo de saldos oficiais nem reconciliação global entre dossiers independentes. Valores conhecidos, ausência declarada e informação em falta permanecem distintos. Créditos económicos e benefícios positivos ou desconhecidos deixam apenas o saldo dependente indeterminado, preservando a qualificação internacional conhecida.
|
||||
|
||||
### Saldo concreto de L8 após este incremento
|
||||
|
||||
| Código por implementar | Condição de retoma |
|
||||
| --- | --- |
|
||||
| Determinação automática da fracção nacional e limites específicosCDT | Corresponder componentes da base/imposto e rendimento estrangeiro e regras específicas do caso; substituir entradas documentadas apenas quando essa correspondência estiver demonstrada. A regra corrente do menor/ordem já está implementada. |
|
||||
| Crédito económico e créditos de investimento/benefício | Aplicar art64/CBF próprio, qualificaçãoL7, base elegível, limite, cumulação e uso real; não os converter em crédito internacional. |
|
||||
| Transporte/caducidade e utilização interanual | Proveniência por exercício, utilização real e prazo próprio de65(3), distinto doPEC66; sem transformar imposto acima do limite em crédito transportável. PEC excedente/transporte continuam fora do âmbito entregue. |
|
||||
| Obrigação/calendárioPPC/PEC, autónomos, períodos especiais, outras moedas eM20/M22 | Retomar cada grupo com factos e regra de período/compensação/câmbio/campo correspondidos; reutilizar motores existentes sem alterar o cálculo independente aceite. |
|
||||
|
||||
| Informação documental externa em falta | Exemplo e condição de retoma |
|
||||
| --- | --- |
|
||||
| Inclusão e fracção nacional do caso | Imposto10 sem prova da inclusão/base: identificar o rendimento incluído e o suporte da quota; apenas o saldo desse caso fica indeterminado. Não exige nova autenticação da fonte oficial. |
|
||||
| Convenção, período e imposto elegível | País conhecido sem artigo/limite aplicável: corresponder artigo, vigência e imposto pago nos termos daCDT. O limite6 da QA não é uma correspondência oficial. |
|
||||
| Pagamento/titular/uso anterior e correspondência temporal | Pagamento em2027 afecto a2026 sem suporte: anexar/identificar correspondência expressa; sem presumir transporte ou utilização zero. |
|
||||
|
||||
**L8 permanece parcial.** Crédito internacional apenas emQA; PEC instalado em`e182d1d7` mantém o âmbito demonstrado. Songo eGurué privados; L3,ISPC,câmbio,L5–L7 eSGFTEMPO preservados. Avaliação de utilização do percurso proactivo continua a aguardar retorno da equipa. Sem instalação, publicação ou nova estimativa global nesta passagem.
|
||||
|
||||
|
||||
## 27/10/2026 — Crédito internacional corrente entregue pelo Git
|
||||
|
||||
**Instalado e conferido em `838ad941a24ae76ec4bf91ccc3f92012668fe8cf`**, sobre o PEC instalado`e182d1d7`, preservando a correcção dos sectores`669cb897`. A candidata é a própria revisãoQA aceite`838ad941`, sem alteração funcional na preparação; integração normal em`main`, sem sobrescrever outro trabalho. Os registosQA anteriores são históricos: este recorte deixa de estar pendente de instalação. Não encerra L8 ou o IRPC.
|
||||
|
||||
**Delta instalado:**5ficheiros de aplicação — detalhe internacional, projecção dos códigos do catálogo de países existente e integração no saldo anual. Todos idênticos à QA. Conferidas1002fontes antes/1005depois comGit; restantes fontes comparadas conservadas, incluindoPEC,L3,ISPC,câmbio,L5–L7,sectores,catálogoISO,rotas e configuração. Sem novas permissões,migrations ou importações. Fixtures sintéticas apenas no código de testes; não importadas para dados activos nem publicadas. Songo eGurué permanecem privados; SGFTEMPO separado.
|
||||
|
||||
**Recorte:** residente geral,ano civil2026,MZN comprovados. Inclusão do rendimento,fracção nacional,aplicabilidade/limiteCDT e utilizações anteriores documentados. Menor entre imposto pago e limites aplicáveis,menos uso anterior e até à colecta,antesPEC/RF/PPC;sem concessão pela escolha do país,conversão/arredondamento fiscal ou transporte presumidos. Exemplo condicionado aos dados e suportes sintéticos demonstrados:1 408−8de crédito−200PEC−500PPC=**700MZN de saldo simulado a pagar**. Os2acima do limite de elegibilidade não são transporte; insuficiência de colecta fica separada. Caso com500a recuperar deriva doPPC,sem reembolso do crédito ouPEC. Informação em falta e prova duplicada deixam o saldo dependente indeterminado.
|
||||
|
||||
**Recuperação e preservação:** capacidade antes superior a85GiB e23,69milhões de inodes; mínimo existente do instalador5GiB mais backup confrontado com espaço real e novamente conferido após backup. Backup fresco`/var/backups/investmoz-irpc-l8-international-20261027/backup`,dumpSHA256`b1e9740653a8664c23268e228efd3ed6a0514bf3e8786dd5adf43342cdb475e1`;restauro174tabelas com contagens/checksums iguais em contentor sem rede,porta55441.369ficheiros persistentes/configuração e172tabelas de negócio intactos depois da instalação;schema,papéis,grants e permissões iguais. Cache/sessões operacionais reconciliados individualmente com o backup e pedidos públicos;nenhum registo de negócio ou sessão sobrescrito pelo comparador. Release anterior mantida até compilação/verificações e troca atómica;runtimeQA fora da árvore servida.
|
||||
|
||||
**Provas:** reutilizadas18novas+31anuais/PEC,API171asserções e7recuperações comHTML/dossier e históricos conservados. Sem nova ronda manual das funcionalidades não afectadas. Controlos automáticos obrigatórios do instalador:OK (1 test, 3106 assertions); ✓ built in 31.55s; OK (59 tests, 317 assertions); # tests 1110; # pass 1110; # fail 0;TypeScript/Wayfinder/build e guardas aprovados. Revisão pública e hash do manifest conferidos,HTTP200do simulador e6marcadores compilados do novo delta/PEC identificados. Não foram criados casosQA no portal.
|
||||
|
||||
**SaldoL8 preservado:** derivação automática da fracção nacional/CDT específicas;créditos económicos/CBF;utilização interanual/transporte/caducidade próprios;obrigação/calendárioPPC/PEC;autónomos,períodos especiais,moedas eM20/M22 conforme dependências já discriminadas. Excedente/transportePEC fora do âmbito entregue. Informação de inclusão/CDT/prova/titular/uso bloqueia apenas o caso dependente. As condições concretas de retoma mantêm-se no saldo curtoQA anterior;sem repetir pesquisas ou exigir nova autenticação de fontes oficiais aceites.
|
||||
|
||||
Recibo:`docs/investmoz/simulations/irpc-l8-international-credit-delivery-20261027.json`. Cópia remota:`/var/backups/investmoz-irpc-l8-international-20261027/installed-receipt.json`,SHA256`d80324b898fd160ab3e7a2fd0b11ea2decd0b99a9324255850062ee4a13e0f0f`. Provas locais:`outputs/irpc-l8-international-delivery-20261027/`. Resolvida a colisão local`inspect.py`→`remote_delivery_inspection.py`;módulo padrão/Paramiko verificados no mesmoPython,guardaLinux/geteuid mantida,sem alteração de credenciais ou do servidor por essa correcção. A avaliação real do percurso proactivo continua dependente do retorno da equipa. **L8 permanece parcial.**
|
||||
|
||||
|
||||
## 28/10/2026 — retoma de L8 e preservação antes do novo delta
|
||||
|
||||
28/10/2026: inventário prévio à QA dos saldos anteriores: internacional corrente838ad941, PECe182d1d7 e sectores669cb897 preservados na basecf4e0638; todos os ficheiros do acervo iguais por bytes. Diferença da pasta original é de ramo/ambiente, não perda confirmada. Sem leitura ou escrita de dados activos; recibos aceites reutilizados. Continuação emQA de65(3)/66, sem declarar L8 concluído.
|
||||
|
||||
Escolha técnica: novo worktree `portal-investidor-irpc-carry-qa-20261028`, ramo `codex/irpc-l8-carry-qa-20261028`, a partir de `cf4e0638`. Sem migrations, novas permissões ou alterações no activo. Nenhuma perda funcional confirmada nesta comparação de fontes; não se declara nova comparação de dados/schema/permissões no servidor. Songo/Gurué e a avaliação pela equipa conservam os estados anteriores.
|
||||
|
||||
|
||||
## 28/10/2026 — saldos anteriores internacionais e PEC: delta QA concluído
|
||||
|
||||
Implementado e demonstrado em QA isolada, **sem instalação**. Base `cf4e0638`, com internacional `838ad941`, PEC `e182d1d7` e sectores `669cb897` preservados. A pasta original estava noutro ramo; isso não foi tratado como eliminação de funcionalidades. Recibo: `docs/investmoz/simulations/irpc-l8-carry-qa-20261028.json`.
|
||||
|
||||
Extensão opcional do livro anual existente: uma origem consolidada por natureza/país/exercício, apuramento e regra de origem documentados, elegível menos dedução inicial e utilizações efectivas subsequentes. Internacional: cinco exercícios seguintes; PEC: três. Origem2021 internacional e PEC2023 chegam a2026; origem2020 internacional/PEC2022 ficam fora do prazo ordinário, conservadas com explicação e saldo histórico. Não se converte imposto acima do limite em crédito transportável. Situações transitórias/especiais não são qualificadas pela regra ordinária.
|
||||
|
||||
Escolha expressa das parcelas correntes e anteriores, vinculada à colecta seleccionada, sem ordem FIFO imposta nem redução automática. A soma não ultrapassa a colecta e os créditos internacionais precedem o PEC. Valores não escolhidos ficam separados da insuficiência de colecta; simulações não criam nem consomem saldos oficiais. Exemplo sintético:1408−8corrente internacional−50anterior internacional−200PECcorrente−100PECanterior−500PPC=**550MZN a pagar, simulado**.
|
||||
|
||||
**Provas:**16novos+49afectados=65testes aprovados; API1teste/119asserções,6casos POST/PUT/GET na base guardada `proactive_investment_20261027_test`,loopback55441,com rollback. Seis recuperações recalculadas integralmente,HTML/dossier concordantes e resultados anteriores congelados.55oportunidades,64documentos,1vínculo,19rascunhos,40utilizadores,3organizações e409eventos anteriores preservados.31ficheiros do acervo byte a byte iguais,sem nova importação/publicação. TypeScript,build QA com exit0 ePint aprovados. Não foi feita nova prova manual de interface ou avaliação pela equipa. Sem migrations,grants,contas,alteração activa ou operações de servidor.
|
||||
|
||||
**Saldo concreto:**L8 continua parcial. Próximo incremento independente:crédito económico64,com gross-up53(1)a/exclusão40,qualificação dos lucros/pagador/beneficiário/imposto e limite proporcional correspondidos; fonte aceite já disponível. Permanecem fracção nacional/CDT automática,créditos/benefícios próprios,obrigação/calendárioPPC/PEC,autónomos,períodos especiais,outras moedas eM20/M22. Não declarar IRPC concluído nem repetir a validação das entregas independentes aceites.
|
||||
|
||||
|
||||
## 28/10/2026 — crédito económico: preservação e limites antes do novo delta
|
||||
|
||||
28/10/2026, antes de novos ensaios: inventário preservado do internacional838ad941/PECe182d1d7/sectores669cb897 instalados e saldos anterioresd134a92d/6ededb59 emQA sem instalação. Novo ramo económico parte de6ededb59, agregável com o delta compatível, sem reapresentar a aprovação dos anteriores.31ficheiros do acervo iguais por bytes;nenhuma perda de fontes confirmada. Sem acesso ao activo nesta preparação;comparação de dados/schema/permissões do destino só na entrega autorizada.
|
||||
|
||||
CIRPC64(1–2)/53(1)a e40(1) na redacçãoLei20/2009 são correspondidos no próprio acervo aceite. Regra económica distinta da internacional65;na liquidação precede65/PEC (RIRPC21). Novo detalhe opcional,sem alterar resultados congelados ou sobrescrever deliberadamente o resumo económico anterior. Sem instalação/publicação.
|
||||
|
||||
|
||||
## 28/10/2026 — crédito económico ordinário demonstrado em QA; agrupamento compatível
|
||||
|
||||
**Implementado e demonstrado em QA isolada, sem instalação.** Ramo `codex/irpc-l8-economic-qa-20261028`, base `6ededb59`, incluindo os saldos anteriores `d134a92d`. A última referência instalada documentada continua `838ad941`; não foi feita nova leitura do servidor. Não se reabre a aceitação dos históricos ou das provas anteriores.
|
||||
|
||||
**Cobertura:**CIRPC64(1–2),53(1)a,40(1)na redacção20/2009 eRIRPC21(2/5). Lucros ordinários distribuídos e IRPC correspondente documentados,beneficiário/distribuidor com sede/direcção efectiva emMoçambique,distribuidor sujeito/não isento,exclusão40(1)não aplicável com suporte. A selecção explícita da colecta corrente reutiliza NUIT/exercício/residência válidos e conserva edições divergentes. Novo detalhe opcional; resumo económico deliberado anterior e resultados congelados conservados.
|
||||
|
||||
**Cálculo:**60%doIRPCdocumentadamente correspondente aos lucros;confere o acréscimo53 já incluído emL6 e limita pela quota proporcional documentada,menos utilização efectiva anterior,até à colecta. O portal não deriva IRPC do dividendo líquido nem altera a base/colecta silenciosamente;inclusão/acréscimo e quota exigem correspondência do caso. A existência de perdas que reduzam a base abaixo dos lucros não é tratada como impedimento genérico. Crédito económico precede o internacional ePEC,sem reembolso ou transporte económico presumido. Exemplo sintético condicionado aos suportes:IRPC20→nominal12;1408−12−8internacional−200PEC−500PPC=**688MZN a pagar,simulado**.
|
||||
|
||||
**Mesma parcela:**identidade por emitente/documento/parcela com suporte;duplicação dos lucros também conferida independentemente de trocar o comprovativo de imposto,a data ou oID. O mesmo documento pode suportar parcelas de imposto distintas quando a separação estiver documentada em ambos os registos. Um comprovativo anterior sem parcela continua a abranger o documento inteiro,não se torna livre por receber novo nome noutro mapa. Atribuições anteriores e capacidade do comprovativo conferidas neste e noutros contextos do snapshot;divergências não são apagadas. ProtecçõesL5 conservadas. Não é reconciliação global de conta fiscal entre dossiers independentes.
|
||||
|
||||
**Agrupamento:**composição demonstrada com os saldos anteriores:1408−12económico−8internacionalcorrente−50internacionalanterior−200PECcorrente−100PECanterior−500PPC=**538MZN a pagar,simulado**. A prioridade económica não reduz automaticamente a afectação entre exercícios já escolhida;se exceder a colecta remanescente,o caso pede reconciliação conservando as escolhas. Os dois incrementos podem integrar uma entrega Git comum;nenhum foi instalado nesta passagem.
|
||||
|
||||
**Provas do delta:**20testes económicos novos+65afectados aceites=85aprovados;API1teste/210asserções,11casos POST/PUT/GET na base guardada `proactive_investment_20261027_test`,loopback55441,transacção com rollback. Onze recuperações recalculadas integralmente,HTML/dossier concordantes e históricos anteriores conservados.55oportunidades,64documentos,1vínculo,19rascunhos,40utilizadores,3organizações e409eventos anteriores intactos. TypeScript,build QA ePint aprovados.31ficheiros do acervo preservados por bytes,sem novas importações,publicações,scan declarado,migrations,contas ougrants. Não foi feita nova prova manual da interface nem avaliação pela equipa.
|
||||
|
||||
**Pendências concretas:**inclusão/correcção automática do dividendo/acréscimo emL6 e derivação da quota proporcional;exclusão40(1)na base com todas as suas condições;liquidação64(3)/associações64(4);créditos/benefícios específicos;fracção internacional/CDT automática;obrigação/calendárioPPC/PEC;autónomos,períodos especiais,outras moedas eM20/M22. Correspondências e parâmetros em falta afectam o caso dependente,sem revalidação genérica de fontes oficiais. Saldos anteriores mantêm o recorte ordinário já demonstrado. **L8 permanece parcial.** Recibo:`docs/investmoz/simulations/irpc-l8-economic-qa-20261028.json`.
|
||||
|
||||
Preservação delimitada:558ficheiros protegidos com conteúdo Git idêntico à baseQA;uma diferençaLF/CRLF em `docs/investmoz/simulations/irpc-l8-carry-qa-20261028.json` entre checkouts,conferida como normalização de ambiente,sem perda de conteúdo. Os31originais do acervo são iguais por bytes.
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,10 @@
|
||||
import type {FiscalProofParcel} from '@/lib/fiscal-proof-parcel';
|
||||
|
||||
export function FiscalProofParcelFields({value, change, label}: {value: FiscalProofParcel; change: (value: FiscalProofParcel) => void; label: string}) {
|
||||
return <details className="rounded border p-2" open={!!value.parcel || !!value.parcelReference}>
|
||||
<summary>Identificar parcela quando o documento suporta várias parcelas — {label}</summary>
|
||||
<p>Sem identificação específica, o comprovativo existente abrange o documento inteiro. Para distinguir impostos ou parcelas, indique a linha/identificador exacto e o suporte dessa separação. Os montantes comprovado e seleccionado devem corresponder à parcela identificada. A mudança do identificador não comprova uma parcela diferente.</p>
|
||||
<label className="block">Identificador da parcela<input aria-label={label + ' — Parcela documental'} value={value.parcel ?? ''} onChange={e => change({parcel: e.target.value})}/></label>
|
||||
<label className="block">Suporte da separação das parcelas<input aria-label={label + ' — Suporte da parcela'} value={value.parcelReference ?? ''} onChange={e => change({parcelReference: e.target.value})}/></label>
|
||||
</details>;
|
||||
}
|
||||
@@ -1,24 +1,29 @@
|
||||
import {SimulationIrpcEconomic} from './simulation-irpc-economic';
|
||||
import {FiscalProofParcelFields} from './fiscal-proof-parcel';
|
||||
import {SimulationIrpcCarry} from './simulation-irpc-carry';
|
||||
import {SimulationIrpcInternational} from './simulation-irpc-international';
|
||||
import {SimulationIrpcPec} from './simulation-irpc-pec';
|
||||
import {useState} from 'react';
|
||||
import {annualBook,newAnnualBook,saveAnnualBook,reuseAnnualAssessment,annualAssessment,annualWithholdingOptions,reuseAnnualWithholding,newAnnualPpc,recordAnnual,annualReportRows,annualHtml,annualSettlementSource,type AnnualBook,type AnnualPpc} from '@/lib/irpc-annual-settlement';
|
||||
import {annualBook,newAnnualBook,saveAnnualBook,reuseAnnualAssessment,annualAssessment,annualWithholdingOptions,reuseAnnualWithholding,newAnnualPpc,recordAnnual,annualReportRows,annualHtml,annualSource,type AnnualBook,type AnnualPpc} from '@/lib/irpc-annual-settlement';
|
||||
|
||||
type Props={draft:Record<string,unknown>;onChange:(field:string,value:string)=>void};
|
||||
export function SimulationIrpcAnnual({draft,onChange}:Props){
|
||||
const[error,setError]=useState('');if(draft.fiscal_component!=='irpc')return null;
|
||||
try{
|
||||
const b=annualBook(draft),save=(next:AnnualBook)=>{try{onChange('modelo_a_snapshot',saveAnnualBook(draft,next));setError('');}catch(e){setError(String(e));}};
|
||||
if(!b)return<section aria-label="IRPC — L8 fecho anual" className="rounded border p-4"><h4>L8 — da colecta ao saldo anual simulado</h4><p>Reutiliza a colecta válida, retenções efectivamente sofridas por conta e PPC comprovados do próprio sujeito/exercício. PEC e créditos com regras próprias permanecem separados.</p><button type="button" onClick={()=>{try{save(reuseAnnualAssessment(draft,newAnnualBook()));}catch(e){setError(String(e));}}}>Preparar saldo anual L8</button>{error&&<p role="alert">{error}</p>}</section>;
|
||||
if(!b)return<section aria-label="IRPC — L8 fecho anual" className="rounded border p-4"><h4>L8 — da colecta ao saldo anual simulado</h4><p>Reutiliza a colecta válida, retenções efectivamente sofridas por conta e PPC comprovados do próprio sujeito/exercício. PEC comprovado pode ser detalhado no próprio exercício; créditos com regras próprias permanecem separados.</p><button type="button" onClick={()=>{try{save(reuseAnnualAssessment(draft,newAnnualBook()));}catch(e){setError(String(e));}}}>Preparar saldo anual L8</button>{error&&<p role="alert">{error}</p>}</section>;
|
||||
const patch=(p:Partial<AnnualBook>)=>save({...b,...p}),a=annualAssessment(draft),options=annualWithholdingOptions(draft);
|
||||
const field=(key:'subjectNuit'|'year'|'reference'|'assessmentReference',label:string)=><label className="block">{label}<input aria-label={label} value={b[key]} onChange={e=>patch({[key]:e.target.value})}/></label>;
|
||||
const group=(key:'withholdings'|'ppc',label:string)=><><label className="block">Estado — {label}<select aria-label={'Estado L8 — '+label} value={b[key].state} onChange={e=>patch({[key]:{...b[key],state:e.target.value as ''|'known'|'absent'}})}><option value="">Informação em falta</option><option value="known">Valores conhecidos — seleccionar comprovativos</option><option value="absent">Ausência declarada com suporte</option></select></label><label className="block">Suporte — {label}<input aria-label={'Suporte L8 — '+label} value={b[key].reference} onChange={e=>patch({[key]:{...b[key],reference:e.target.value}})}/></label></>;
|
||||
return<section aria-label="IRPC — L8 fecho anual" className="space-y-3 rounded border p-4"><h4>L8 — colecta, abatimentos e saldo anual simulado</h4><p>A colecta acima é anterior aos abatimentos. Este saldo tem cálculo e histórico próprios; retenção entregue pela empresa por pagamento a terceiros não é retenção sofrida. O resultado continua simulado, sem submissão, pagamento ou reembolso automático.</p><p>{annualSettlementSource}</p>{error&&<p role="alert">{error}</p>}
|
||||
return<section aria-label="IRPC — L8 fecho anual" className="space-y-3 rounded border p-4"><h4>L8 — colecta, abatimentos e saldo anual simulado</h4><p>A colecta acima é anterior aos abatimentos. Este saldo tem cálculo e histórico próprios; retenção entregue pela empresa por pagamento a terceiros não é retenção sofrida. O resultado continua simulado, sem submissão, pagamento ou reembolso automático.</p><p>{annualSource(b)}</p>{error&&<p role="alert">{error}</p>}
|
||||
{field('subjectNuit','NUIT do sujeito do fecho L8')}{field('year','Exercício do fecho L8')}{field('reference','Suporte do fecho da entidade e exercício L8')}
|
||||
<p>Colecta disponível:{a.colecta?a.colecta.numerator+'/'+a.colecta.denominator+' MZN':'Indeterminada'};origem:{a.kind==='special'?'L7 qualificado':'L6 normal'};NUIT:{a.subjectNuit||'Por identificar'};exercício:{a.year||'Por identificar'}.</p><button type="button" onClick={()=>{try{save(reuseAnnualAssessment(draft,b));}catch(e){setError(String(e));}}}>Reutilizar colecta corrente L6/L7</button>{field('assessmentReference','Suporte da colecta reutilizada L8')}
|
||||
<fieldset className="space-y-2 rounded border p-3"><legend>Retenções efectivamente sofridas por conta</legend>{group('withholdings','Retenções sofridas')}
|
||||
{options.length===0?<p>Não há afectações de comprovativosL5 neste contexto. Reutilize e reconcilie a retenção recebida emL5 ou declare ausência com suporte.</p>:options.map(o=><div key={o.rowId+'|'+o.paymentId} className="rounded border p-2"><p>{o.document||'Prova por identificar'};emitente:{o.issuer};data:{o.date};parcelaL5:{o.amount}MZN;{o.reason||'Retenção recebida/efectiva por conta reconciliada emL5.'}</p><button type="button" disabled={!o.ready||b.withholdings.rows.some(r=>r.rowId===o.rowId&&r.paymentId===o.paymentId)} onClick={()=>{try{save(reuseAnnualWithholding(draft,b,o.rowId,o.paymentId,crypto.randomUUID()));}catch(e){setError(String(e));}}}>Seleccionar prova L5 {o.document||o.paymentId}</button></div>)}
|
||||
{b.withholdings.rows.map((r,index)=>{const change=(p:Partial<typeof r>)=>patch({withholdings:{...b.withholdings,rows:b.withholdings.rows.map(x=>x.id===r.id?{...x,...p}:x)}});return<fieldset key={r.id} className="rounded border p-2"><legend>Retenção seleccionada {index+1}</legend><p>Rendimento:{r.rowId};prova:{r.paymentId};NUIT:{r.subjectNuit};exercício:{r.year}. Fonte conservada. Se mudou, retire esta selecção e seleccione a prova actual. O histórico mantém-se.</p><label className="block">Parcela a abater<input aria-label={'Parcela da retenção L8 '+(index+1)} value={r.amount} onChange={e=>change({amount:e.target.value})}/></label><label className="block">O próprio sujeito é beneficiário e o rendimento está incluído na base deste exercício<select aria-label={'Rendimento incluído L8 '+(index+1)} value={r.incomeIncluded} onChange={e=>change({incomeIncluded:e.target.value as ''|'yes'|'no'})}><option value="">Informação em falta</option><option value="yes">Sim — documentado</option><option value="no">Não — crédito não aplicável neste fecho</option></select></label><label className="block">Suporte da titularidade e inclusão<input aria-label={'Suporte da retenção L8 '+(index+1)} value={r.reference} onChange={e=>change({reference:e.target.value})}/></label><button type="button" onClick={()=>patch({withholdings:{...b.withholdings,rows:b.withholdings.rows.filter(x=>x.id!==r.id)}})}>Retirar selecção L8 da retenção {index+1}</button></fieldset>;})}</fieldset>
|
||||
{b.withholdings.rows.map((r,index)=>{const change=(p:Partial<typeof r>)=>patch({withholdings:{...b.withholdings,rows:b.withholdings.rows.map(x=>x.id===r.id?{...x,...p}:x)}});return<fieldset key={r.id} className="rounded border p-2"><legend>Retenção seleccionada {index+1}</legend><p>Rendimento:{r.rowId};prova:{r.paymentId};NUIT:{r.subjectNuit};exercício:{r.year}. Fonte conservada. Se mudou, retire esta selecção e seleccione a prova actual. O histórico mantém-se.</p><label className="block">Parcela a abater<input aria-label={'Parcela da retenção L8 '+(index+1)} value={r.amount} onChange={e=>change({amount:e.target.value})}/></label><label className="block">O próprio sujeito é beneficiário e o rendimento está incluído na base deste exercício<select aria-label={'Rendimento incluído L8 '+(index+1)} value={r.incomeIncluded} onChange={e=>change({incomeIncluded:e.target.value as ''|'yes'|'no'})}><option value="">Informação em falta</option><option value="yes">Sim — documentado</option><option value="no">Não — crédito não aplicável neste fecho</option></select></label><label className="block">Suporte da titularidade e inclusão<input aria-label={'Suporte da retenção L8 '+(index+1)} value={r.reference} onChange={e=>change({reference:e.target.value})}/></label><FiscalProofParcelFields value={r} change={change} label={"Retenção L8 "+(index+1)}/><button type="button" onClick={()=>patch({withholdings:{...b.withholdings,rows:b.withholdings.rows.filter(x=>x.id!==r.id)}})}>Retirar selecção L8 da retenção {index+1}</button></fieldset>;})}</fieldset>
|
||||
<fieldset className="space-y-2 rounded border p-3"><legend>PPC efectivamente pagos</legend>{group('ppc','PPC efectuados')}<p>Regista comprovativos de pagamentos por conta; não calcula automaticamente a obrigação PPC nem usa pagamentos previstos no plano.</p><button type="button" onClick={()=>patch({ppc:{...b.ppc,rows:[...b.ppc.rows,{...newAnnualPpc(crypto.randomUUID()),subjectNuit:b.subjectNuit,year:b.year}]}})}>Registar comprovativo PPC</button>
|
||||
{b.ppc.rows.map((r,index)=>{const change=(p:Partial<AnnualPpc>)=>patch({ppc:{...b.ppc,rows:b.ppc.rows.map(x=>x.id===r.id?{...x,...p}:x)}});return<fieldset key={r.id} className="rounded border p-2"><legend>PPC {index+1}</legend>{([['document','Comprovativo'],['issuer','Emitente'],['date','Data (AAAA-MM-DD)'],['subjectNuit','NUIT beneficiário'],['year','Exercício do comprovativo'],['currency','Moeda do comprovativo'],['proofAmount','Montante comprovado'],['amount','Parcela a abater'],['reference','Suporte do pagamento e afectação']]as const).map(([key,label])=><label key={key} className="block">{label}<input aria-label={'PPC L8 '+(index+1)+' — '+label} value={r[key]} onChange={e=>change({[key]:e.target.value})}/></label>)}<label className="block">Pagamento efectivamente comprovado<select aria-label={'PPC L8 '+(index+1)+' — Efectivo'} value={r.effective} onChange={e=>change({effective:e.target.value as ''|'yes'|'no'})}><option value="">Informação em falta</option><option value="yes">Sim — comprovativo reconciliado</option><option value="no">Não — apenas previsão/outro estado</option></select></label><button type="button" onClick={()=>patch({ppc:{...b.ppc,rows:b.ppc.rows.filter(x=>x.id!==r.id)}})}>Retirar PPC {index+1}</button></fieldset>;})}</fieldset>
|
||||
{([['pec','PEC'],['credits','Créditos com regras próprias']]as const).map(([key,label])=><fieldset key={key} className="rounded border p-3"><legend>{label} — tratamento separado</legend><label className="block">Estado — {label}<select aria-label={'Estado L8 — '+label} value={b[key].state} onChange={e=>patch({[key]:{...b[key],state:e.target.value as ''|'known'|'absent'}})}><option value="">Informação em falta</option><option value="absent">Ausência declarada com suporte</option><option value="known">Valor conhecido — positivo exige regra própria</option></select></label>{(b[key].state==='known'||b[key].amount)&&<label className="block">Montante declarado<input aria-label={'Montante L8 — '+label} value={b[key].amount} onChange={e=>patch({[key]:{...b[key],amount:e.target.value}})}/></label>}<label className="block">Suporte<input aria-label={'Suporte L8 — '+label} value={b[key].reference} onChange={e=>patch({[key]:{...b[key],reference:e.target.value}})}/></label></fieldset>)}
|
||||
{b.ppc.rows.map((r,index)=>{const change=(p:Partial<AnnualPpc>)=>patch({ppc:{...b.ppc,rows:b.ppc.rows.map(x=>x.id===r.id?{...x,...p}:x)}});return<fieldset key={r.id} className="rounded border p-2"><legend>PPC {index+1}</legend>{([['document','Comprovativo'],['issuer','Emitente'],['date','Data (AAAA-MM-DD)'],['subjectNuit','NUIT beneficiário'],['year','Exercício do comprovativo'],['currency','Moeda do comprovativo'],['proofAmount','Montante comprovado'],['amount','Parcela a abater'],['reference','Suporte do pagamento e afectação']]as const).map(([key,label])=><label key={key} className="block">{label}<input aria-label={'PPC L8 '+(index+1)+' — '+label} value={r[key]} onChange={e=>change({[key]:e.target.value})}/></label>)}<label className="block">Pagamento efectivamente comprovado<select aria-label={'PPC L8 '+(index+1)+' — Efectivo'} value={r.effective} onChange={e=>change({effective:e.target.value as ''|'yes'|'no'})}><option value="">Informação em falta</option><option value="yes">Sim — comprovativo reconciliado</option><option value="no">Não — apenas previsão/outro estado</option></select></label><FiscalProofParcelFields value={r} change={change} label={"PPC L8 "+(index+1)}/><button type="button" onClick={()=>patch({ppc:{...b.ppc,rows:b.ppc.rows.filter(x=>x.id!==r.id)}})}>Retirar PPC {index+1}</button></fieldset>;})}</fieldset>
|
||||
<SimulationIrpcEconomic book={b} save={save} residentConfirmed={a.current&&a.book.scope==='resident_general'&&b.subjectNuit===a.subjectNuit&&b.year===a.year&&b.assessmentKey===a.key}/><SimulationIrpcInternational book={b} save={save}/><SimulationIrpcPec book={b} save={save}/><SimulationIrpcCarry book={b} save={save}/>{([...(b.pecPayments?[]:[['pec','PEC']as const]),...(b.internationalCredits?[]:[['credits','Créditos com regras próprias']as const])]).map(([key,label])=><fieldset key={key} className="rounded border p-3"><legend>{label} — tratamento separado</legend><label className="block">Estado — {label}<select aria-label={'Estado L8 — '+label} value={b[key].state} onChange={e=>patch({[key]:{...b[key],state:e.target.value as ''|'known'|'absent'}})}><option value="">Informação em falta</option><option value="absent">Ausência declarada com suporte</option><option value="known">Valor conhecido — positivo exige regra própria</option></select></label>{(b[key].state==='known'||b[key].amount)&&<label className="block">Montante declarado<input aria-label={'Montante L8 — '+label} value={b[key].amount} onChange={e=>patch({[key]:{...b[key],amount:e.target.value}})}/></label>}<label className="block">Suporte<input aria-label={'Suporte L8 — '+label} value={b[key].reference} onChange={e=>patch({[key]:{...b[key],reference:e.target.value}})}/></label></fieldset>)}
|
||||
<button type="button" onClick={()=>{try{save(recordAnnual(draft,b));}catch(e){setError(String(e));}}}>Calcular e guardar saldo anual L8</button><button type="button" onClick={()=>{try{const html=annualHtml(draft),url=URL.createObjectURL(new Blob([html],{type:'text/html;charset=utf-8'})),link=document.createElement('a');link.href=url;link.download='IRPC-L8-saldo-simulado.html';link.click();URL.revokeObjectURL(url);setError('');}catch(e){setError(String(e));}}}>Exportar saldo anual L8</button>
|
||||
{annualReportRows(draft).map((r,n)=><p key={n}>{r[1]} — {r[2]} — {r[3]}</p>)}</section>;
|
||||
}catch(e){return<p role="alert">PreparaçãoL8 não recuperável:{String(e)}. Originais e históricos conservados.</p>;}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import {prepareCreditCarry, type AnnualBook} from '@/lib/irpc-annual-settlement';
|
||||
import {carrySource, newCarryLot, newCarryUse, type CarryLot, type CreditCarry} from '@/lib/irpc-credit-carry';
|
||||
import CountrySelect, {type Country} from '@/components/country-select';
|
||||
import {countryIsoCodes} from '@/lib/country-iso-codes';
|
||||
|
||||
const pt = new Intl.DisplayNames(['pt'], {type: 'region'});
|
||||
const en = new Intl.DisplayNames(['en'], {type: 'region'});
|
||||
const countries: Country[] = countryIsoCodes.filter(code => code !== 'MZ').map(code => ({code, name: pt.of(code) ?? code, name_en: en.of(code) ?? code})).sort((a, b) => a.name.localeCompare(b.name, 'pt'));
|
||||
|
||||
export function SimulationIrpcCarry({book, save}: {book: AnnualBook; save: (book: AnnualBook) => void}) {
|
||||
const c = book.creditCarry;
|
||||
if (!c) return <fieldset className="rounded border p-3"><legend>Saldos de exercícios anteriores</legend><p>Se dispõe de remanescentes documentados de crédito internacional ou PEC, prepare a origem e as utilizações efectivas. Os resultados anteriores ficam conservados.</p><button type="button" onClick={() => save(prepareCreditCarry(book))}>Preparar saldos anteriores</button></fieldset>;
|
||||
const update = (value: CreditCarry) => save({...book, creditCarry: value});
|
||||
const lotChange = (r: CarryLot) => update({...c, lots: c.lots.map(x => x.id === r.id ? r : x)});
|
||||
return <fieldset className="space-y-3 rounded border p-3"><legend>Crédito internacional e PEC — exercícios anteriores</legend>
|
||||
<p>{carrySource}</p>
|
||||
<label className="block">Inventário de saldos anteriores<select aria-label="Estado dos saldos anteriores L8" value={c.state} onChange={e => update({...c, state: e.target.value as CreditCarry['state']})}><option value="">Informação em falta</option><option value="known">Saldos conhecidos</option><option value="absent">Ausência documentada</option></select></label>
|
||||
<label className="block">Suporte do inventário<input aria-label="Suporte dos saldos anteriores L8" value={c.reference} onChange={e => update({...c, reference: e.target.value})}/></label>
|
||||
{(c.state === 'known' || c.lots.length > 0) && <>
|
||||
<p>Consolide uma origem por natureza, país e exercício. Transcreva o montante elegível apurado e o deduzido na origem, com o suporte da regra desse exercício. Imposto estrangeiro acima dos limites de elegibilidade não é remanescente por insuficiência de colecta. Só se aplica aqui o prazo ordinário a exercícios anuais; situações transitórias ou especiais exigem correspondência própria.</p>
|
||||
<button type="button" onClick={() => update({...c, lots: [...c.lots, {...newCarryLot(crypto.randomUUID()), subjectNuit: book.subjectNuit}]})}>Registar origem de saldo anterior</button>
|
||||
</>}
|
||||
{c.lots.map((r, i) => {
|
||||
const patch = (v: Partial<CarryLot>) => lotChange({...r, ...v});
|
||||
const prefix = 'Saldo anterior ' + (i + 1) + ' — ';
|
||||
return <fieldset key={r.id} className="space-y-2 rounded border p-3"><legend>Origem {i + 1}</legend>
|
||||
<label className="block">Natureza<select aria-label={prefix + 'Natureza'} value={r.kind} onChange={e => patch({kind: e.target.value as CarryLot['kind']})}><option value="">Por identificar</option><option value="international">Crédito por dupla tributação internacional</option><option value="pec">PEC efectivamente pago</option></select></label>
|
||||
{(r.kind === 'international' || r.country) && <CountrySelect countries={countries} label={prefix + 'País estrangeiro'} value={r.country} onChange={country => patch({country})} allowLegacyValue/>}
|
||||
{r.kind === 'pec' && <p>O PEC não tem país estrangeiro. A escolha da natureza conserva os valores introduzidos; reconcilie qualquer país anterior.</p>}
|
||||
{([['subjectNuit', 'NUIT titular'], ['year', 'Exercício de origem'], ['currency', 'Moeda comprovada'], ['originDocument', 'Declaração/apuramento de origem'], ['originReference', 'Suporte da origem e insuficiência de colecta'], ['ruleReference', 'Regra e período aplicáveis na origem'], ['eligible', 'Montante elegível na origem — MZN'], ['originDeducted', 'Deduzido na origem — MZN'], ['selected', 'Parcela escolhida para este fecho — MZN']] as const).map(([key, label]) => <label key={key} className="block">{label}<input aria-label={prefix + label} value={r[key]} onChange={e => patch({[key]: e.target.value})}/></label>)}
|
||||
<label className="block">Exercício de origem anual ordinário<select aria-label={prefix + 'Período ordinário'} value={r.ordinary} onChange={e => patch({ordinary: e.target.value as CarryLot['ordinary']})}><option value="">Informação em falta</option><option value="yes">Sim — documentado</option><option value="no">Não — regra específica por corresponder</option></select></label>
|
||||
<label className="block">Remanescente por insuficiência de colecta<select aria-label={prefix + 'Insuficiência de colecta'} value={r.insufficient} onChange={e => patch({insufficient: e.target.value as CarryLot['insufficient']})}><option value="">Informação em falta</option><option value="yes">Sim — documentado</option><option value="no">Não — não é saldo elegível neste percurso</option></select></label>
|
||||
<label className="block">Utilizações efectivas posteriores à origem<select aria-label={prefix + 'Estado dos usos'} value={r.usesState} onChange={e => patch({usesState: e.target.value as CarryLot['usesState']})}><option value="">Informação em falta</option><option value="known">Utilizações conhecidas</option><option value="absent">Ausência documentada</option></select></label>
|
||||
<label className="block">Suporte do inventário de utilizações<input aria-label={prefix + 'Suporte dos usos'} value={r.usesReference} onChange={e => patch({usesReference: e.target.value})}/></label>
|
||||
<p>Inclua utilizações efectivas já ocorridas neste exercício antes desta parcela. Não registe simulações históricas como utilizações fiscais. Crédito internacional admite fracção exacta n/d; não é aplicado arredondamento fiscal.</p>
|
||||
{r.usesState === 'known' && <button type="button" onClick={() => patch({uses: [...r.uses, newCarryUse(crypto.randomUUID())]})}>Registar utilização efectiva da origem {i + 1}</button>}
|
||||
{r.uses.map((u, j) => <fieldset key={u.id} className="rounded border p-2"><legend>Utilização {i + 1}.{j + 1}</legend>
|
||||
{([['year', 'Exercício'], ['amount', 'Montante MZN'], ['document', 'Documento de utilização'], ['reference', 'Suporte']] as const).map(([key, label]) => <label key={key} className="block">{label}<input aria-label={prefix + 'Uso ' + (j + 1) + ' — ' + label} value={u[key]} onChange={e => patch({uses: r.uses.map(x => x.id === u.id ? {...x, [key]: e.target.value} : x)})}/></label>)}
|
||||
<button type="button" onClick={() => patch({uses: r.uses.filter(x => x.id !== u.id)})}>Retirar utilização {i + 1}.{j + 1}</button>
|
||||
</fieldset>)}
|
||||
<button type="button" onClick={() => update({...c, lots: c.lots.filter(x => x.id !== r.id)})}>Retirar origem {i + 1}</button>
|
||||
</fieldset>;
|
||||
})}
|
||||
{(c.state === 'known' || c.currentInternational || c.currentPec || c.allocationReference) && <fieldset className="space-y-2 rounded border p-3"><legend>Escolha das parcelas entre exercícios</legend>
|
||||
<p>Escolha quanto deduzir dos créditos correntes e de cada origem anterior. Os créditos internacionais precedem o PEC; a soma fica limitada à colecta. Não se presume prioridade entre exercícios e nenhuma parcela é reduzida automaticamente. Valores não escolhidos ficam identificados, sem criação de transporte oficial.</p>
|
||||
<label className="block">Crédito internacional corrente a deduzir — MZN<input aria-label="Parcela corrente internacional L8" value={c.currentInternational} onChange={e => update({...c, currentInternational: e.target.value})}/></label>
|
||||
<label className="block">PEC corrente a deduzir — MZN<input aria-label="Parcela corrente PEC L8" value={c.currentPec} onChange={e => update({...c, currentPec: e.target.value})}/></label>
|
||||
<label className="block">Suporte da escolha e seus impactos<input aria-label="Suporte da afectação entre exercícios L8" value={c.allocationReference} onChange={e => update({...c, allocationReference: e.target.value})}/></label>
|
||||
<button type="button" onClick={() => update({...c, assessmentKey: book.assessmentKey})}>Confirmar afectação à colecta seleccionada</button>
|
||||
<p>{c.assessmentKey && c.assessmentKey === book.assessmentKey ? 'Escolha vinculada à colecta seleccionada; o cálculo verifica disponibilidade e limites.' : 'Falta confirmar a escolha na colecta seleccionada. Valores conservados.'}</p>
|
||||
</fieldset>}
|
||||
</fieldset>;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import {prepareEconomicBook, type AnnualBook} from '@/lib/irpc-annual-settlement';
|
||||
import {economicSource, newEconomicPosition, type EconomicPosition} from '@/lib/irpc-economic-credit';
|
||||
|
||||
export function SimulationIrpcEconomic({book, save, residentConfirmed}: {book: AnnualBook; save: (book: AnnualBook) => void; residentConfirmed: boolean}) {
|
||||
const c = book.economicCredits;
|
||||
if (!c) return <fieldset className="rounded border p-3"><legend>Crédito por dupla tributação económica</legend><p>Para lucros distribuídos por entidade moçambicana sujeita e não isenta de IRPC, distinga o imposto correspondente aos lucros da retenção sofrida e do imposto estrangeiro. Reutiliza a colecta e conserva o resumo de créditos anterior.</p><button type="button" onClick={() => save(prepareEconomicBook(book))}>Preparar crédito económico</button></fieldset>;
|
||||
const update = (next: typeof c) => save({...book, economicCredits: next});
|
||||
const rowChange = (r: EconomicPosition) => update({...c, rows: c.rows.map(x => x.id === r.id ? r : x)});
|
||||
return <fieldset className="space-y-3 rounded border p-3"><legend>Crédito económico — lucros distribuídos</legend>
|
||||
<p>{economicSource}</p>
|
||||
<p>O detalhe substitui nesta execução o resumo económico anterior, que permanece conservado: {book.internationalCredits?.economic.state || 'informação em falta'}; montante {book.internationalCredits?.economic.amount || 'não preenchido'}. Os benefícios e o crédito internacional mantêm os seus estados próprios.</p>
|
||||
<label className="block">Estado do crédito económico<select aria-label="Estado do crédito económico L8" value={c.state} onChange={e => update({...c, state: e.target.value as typeof c.state})}><option value="">Informação em falta</option><option value="known">Lucros e IRPC conhecidos</option><option value="absent">Ausência documentada</option></select></label>
|
||||
<label className="block">Suporte do inventário económico<input aria-label="Suporte económico L8" value={c.reference} onChange={e => update({...c, reference: e.target.value})}/></label>
|
||||
{(c.state === 'known' || c.rows.length > 0) && <button type="button" onClick={() => update({...c, state: 'known', rows: [...c.rows, {...newEconomicPosition(crypto.randomUUID()), subjectNuit: book.subjectNuit, year: book.year, recipientResident: residentConfirmed ? 'yes' : ''}]})}>Registar lucros distribuídos</button>}
|
||||
{c.rows.map((r, i) => {
|
||||
const patch = (v: Partial<EconomicPosition>) => rowChange({...r, ...v});
|
||||
const prefix = 'Crédito económico ' + (i + 1) + ' — ';
|
||||
const field = (key: Exclude<keyof EconomicPosition, 'previousUse'>, label: string) => <label key={key} className="block">{label}<input aria-label={prefix + label} value={r[key]} onChange={e => patch({[key]: e.target.value})}/></label>;
|
||||
const choice = (key: 'recipientResident' | 'payerResident' | 'payerIrpc' | 'payerExempt' | 'article40' | 'incomeIncluded', label: string) => <label key={key} className="block">{label}<select aria-label={prefix + label} value={r[key]} onChange={e => patch({[key]: e.target.value})}><option value="">Informação em falta</option><option value="yes">Sim — documentado</option><option value="no">Não — documentado</option></select></label>;
|
||||
const outside = !!r.kind && r.kind !== 'ordinary_profit' || r.article40 === 'yes';
|
||||
const detail = <>
|
||||
{([['distributionDocument', 'Documento da distribuição'], ['distributionParcel', 'Parcela dos lucros no documento'], ['distributionDate', 'Data da distribuição (AAAA-MM-DD)'], ['income', 'Lucros incluídos antes do crédito — MZN, ilíquidos de retenção'], ['incomeReference', 'Suporte da inclusão e do valor ilíquido na base corrente'], ['taxIssuer', 'Emitente do comprovativo de IRPC correspondente'], ['taxDocument', 'Documento do IRPC correspondente aos lucros'], ['taxParcel', 'Parcela desse IRPC no documento'], ['parcelReference', 'Suporte da identificação da parcela de imposto'], ['currency', 'Moeda comprovada'], ['proofAmount', 'IRPC comprovado nesta parcela — MZN'], ['taxAmount', 'IRPC correspondente aos lucros seleccionados — MZN'], ['attributionReference', 'Suporte da correspondência entre imposto e lucros'], ['previouslyAttributed', 'IRPC desta parcela já atribuído a outros lucros — MZN'], ['priorAttributionReference', 'Suporte das atribuições anteriores, incluindo zero conhecido'], ['grossUpIncluded', 'Crédito nominal já acrescido à base (art.53) — MZN'], ['grossUpReference', 'Suporte do acréscimo art.53 já incluído em L6'], ['nationalLimit', 'Quota proporcional da colecta após acréscimo — MZN'], ['nationalLimitReference', 'Suporte da quota proporcional da colecta']] as const).map(([key, label]) => field(key, label))}
|
||||
{choice('incomeIncluded', 'Lucros ilíquidos e acréscimo incluídos na base corrente')}
|
||||
<p>O cálculo confere 60% do IRPC correspondente com o acréscimo já incluído em L6 e a quota proporcional documentada. Se a base estiver incompleta, reconcilie-a no percurso existente. Esta acção conserva os valores e não altera a colecta automaticamente.</p>
|
||||
<button type="button" onClick={() => patch({assessmentKey: book.assessmentKey})}>Confirmar inclusão e quota na colecta seleccionada</button>
|
||||
<label className="block">Utilização anterior efectiva do crédito<select aria-label={prefix + 'Estado do uso anterior'} value={r.previousUse.state} onChange={e => patch({previousUse: {...r.previousUse, state: e.target.value as EconomicPosition['previousUse']['state']}})}><option value="">Informação em falta</option><option value="known">Montante conhecido</option><option value="absent">Ausência documentada</option></select></label>
|
||||
{(r.previousUse.state === 'known' || r.previousUse.amount) && <label className="block">Crédito já utilizado — MZN<input aria-label={prefix + 'Uso anterior MZN'} value={r.previousUse.amount} onChange={e => patch({previousUse: {...r.previousUse, amount: e.target.value}})}/></label>}
|
||||
<label className="block">Suporte do uso anterior ou da sua ausência<input aria-label={prefix + 'Suporte do uso anterior'} value={r.previousUse.reference} onChange={e => patch({previousUse: {...r.previousUse, reference: e.target.value}})}/></label>
|
||||
</>;
|
||||
return <fieldset key={r.id} className="space-y-2 rounded border p-3"><legend>Lucros {i + 1}</legend>
|
||||
{field('subjectNuit', 'NUIT beneficiário')}{field('payerNuit', 'NUIT distribuidor')}{field('year', 'Exercício')}
|
||||
<label className="block">Natureza<select aria-label={prefix + 'Natureza'} value={r.kind} onChange={e => patch({kind: e.target.value as EconomicPosition['kind']})}><option value="">Por identificar</option><option value="ordinary_profit">Lucros ordinários — art.64(1–2)</option><option value="liquidation">Partilha em liquidação — art.64(3)</option><option value="association">Associação — art.64(4)</option></select></label>
|
||||
{residentConfirmed && r.recipientResident === 'yes' ? <p>Residência do beneficiário reutilizada da colecta corrente no regime geral. NUIT e exercício conservados.</p> : choice('recipientResident', 'Beneficiário com sede/direcção efectiva em Moçambique')}{choice('payerResident', 'Distribuidor com sede/direcção efectiva em Moçambique')}{choice('payerIrpc', 'Distribuidor sujeito a IRPC')}{choice('payerExempt', 'Distribuidor isento de IRPC')}{field('qualificationReference', 'Suporte da qualificação das entidades e dos lucros')}
|
||||
{choice('article40', 'Esta parcela está abrangida pela exclusão do art.40(1)')}{field('article40Reference', 'Suporte da correspondência do art.40(1) e seu período')}
|
||||
<p>O art.40(1), na redacção de 2009, contempla as entidades e condições próprias, incluindo participação directa de pelo menos20% e prazo de dois anos, nos termos da fonte. Uma parcela excluída da base não recebe também o crédito64. A simples percentagem de participação não decide todas as condições.</p>
|
||||
{outside ? <><p>Esta parcela exige o tratamento específico identificado. O crédito ordinário não é aplicado; os valores introduzidos ficam conservados.</p><details><summary>Consultar ou reconciliar dados conservados</summary>{detail}</details></> : detail}
|
||||
<button type="button" onClick={() => update({...c, rows: c.rows.filter(x => x.id !== r.id)})}>Retirar lucros {i + 1}</button>
|
||||
</fieldset>;
|
||||
})}
|
||||
</fieldset>;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import {FiscalProofParcelFields} from './fiscal-proof-parcel';
|
||||
import CountrySelect, {type Country} from '@/components/country-select';
|
||||
import {countryIsoCodes} from '@/lib/country-iso-codes';
|
||||
import {newInternationalProof, prepareInternationalBook, type AnnualBook} from '@/lib/irpc-annual-settlement';
|
||||
import {newInternationalPosition, internationalCreditSource, type CreditEvidence, type InternationalCredits, type InternationalPosition, type InternationalTaxProof} from '@/lib/irpc-international-credit';
|
||||
|
||||
const pt = new Intl.DisplayNames(['pt'], {type: 'region'}), en = new Intl.DisplayNames(['en'], {type: 'region'});
|
||||
const countries: Country[] = countryIsoCodes.filter(code => code !== 'MZ').map(code => ({code, name: pt.of(code) ?? code, name_en: en.of(code) ?? code})).sort((a, b) => a.name.localeCompare(b.name, 'pt'));
|
||||
function Evidence({value, label, change}: {value: CreditEvidence; label: string; change: (value: CreditEvidence) => void}) {
|
||||
return <fieldset className="rounded border p-2"><legend>{label}</legend>
|
||||
<label className="block">Estado<select aria-label={label + ' — Estado'} value={value.state} onChange={e => change({...value, state: e.target.value as CreditEvidence['state']})}><option value="">Informação em falta</option><option value="known">Valor conhecido</option><option value="absent">Ausência declarada</option></select></label>
|
||||
{(value.state === 'known' || value.amount) && <label className="block">Montante MZN<input aria-label={label + ' — Montante'} value={value.amount} onChange={e => change({...value, amount: e.target.value})}/></label>}
|
||||
<label className="block">Suporte<input aria-label={label + ' — Suporte'} value={value.reference} onChange={e => change({...value, reference: e.target.value})}/></label>
|
||||
</fieldset>;
|
||||
}
|
||||
export function SimulationIrpcInternational({book, save}: {book: AnnualBook; save: (book: AnnualBook) => void}) {
|
||||
const c = book.internationalCredits;
|
||||
if (!c) return <fieldset className="rounded border p-3"><legend>Crédito internacional — próprio exercício</legend><p>Prepare a inclusão do imposto estrangeiro comprovado e dos limites documentados, antes do PEC. O resumo anterior de créditos e os históricos ficam conservados. A escolha do país não confirma uma convenção ou benefício.</p><button type="button" onClick={() => save(prepareInternationalBook(book))}>Preparar crédito internacional</button></fieldset>;
|
||||
const update = (next: InternationalCredits) => save({...book, internationalCredits: next});
|
||||
const positionChange = (p: InternationalPosition) => update({...c, positions: c.positions.map(x => x.id === p.id ? p : x)});
|
||||
return <fieldset className="space-y-3 rounded border p-3"><legend>Crédito por dupla tributação internacional</legend>
|
||||
<p>{internationalCreditSource}</p>
|
||||
<p>Reúna os comprovativos de cada país numa posição por exercício. Indique a fracção nacional e a convenção conforme o suporte do caso. Limites admitem decimal ou fracção exacta n/d; a apresentação não constitui arredondamento fiscal. Valores MZN exigem correspondência documental; uma previsão de câmbio ou de pagamento não comprova o crédito.</p>
|
||||
<label className="block">Estado internacional<select aria-label="Estado do crédito internacional L8" value={c.state} onChange={e => update({...c, state: e.target.value as InternationalCredits['state']})}><option value="">Informação em falta</option><option value="known">Posições conhecidas</option><option value="absent">Ausência declarada</option></select></label>
|
||||
<label className="block">Suporte do inventário<input aria-label="Suporte do crédito internacional L8" value={c.reference} onChange={e => update({...c, reference: e.target.value})}/></label>
|
||||
{book.economicCredits ? <p>Resumo económico anterior conservado: {c.economic.state || "Informação em falta"}; {c.economic.amount || "Montante não preenchido"}. Nesta execução é usado o detalhe económico acima.</p> : <Evidence label="Crédito económico — regra própria" value={c.economic} change={economic => update({...c, economic})}/>}
|
||||
<Evidence label="Benefícios/créditos próprios — regra própria" value={c.benefits} change={benefits => update({...c, benefits})}/>
|
||||
<p>{book.economicCredits ? "Benefícios positivos exigem regras próprias; o crédito económico tem o seu detalhe separado. Informação em falta mantém o saldo dependente indeterminado." : "Créditos económicos e benefícios positivos exigem regras próprias; informação em falta mantém o saldo dependente indeterminado."}</p>
|
||||
<button type="button" onClick={() => update({...c, state: 'known', positions: [...c.positions, {...newInternationalPosition(crypto.randomUUID()), subjectNuit: book.subjectNuit, year: book.year, assessmentKey: book.assessmentKey}]})}>Registar posição internacional</button>
|
||||
{c.positions.map((p, index) => {
|
||||
const patch = (v: Partial<InternationalPosition>) => positionChange({...p, ...v});
|
||||
const paymentChange = (r: InternationalTaxProof) => patch({payments: p.payments.map(x => x.id === r.id ? r : x)});
|
||||
return <fieldset key={p.id} className="space-y-2 rounded border p-3"><legend>Posição internacional {index + 1}</legend>
|
||||
<CountrySelect countries={countries} label={'País do imposto estrangeiro ' + (index + 1)} value={p.country} onChange={country => patch({country})} allowLegacyValue/>
|
||||
{([['subjectNuit', 'NUIT titular'], ['year', 'Exercício'], ['incomeAmount', 'Rendimento incluído — MZN'], ['incomeReference', 'Suporte da inclusão na matéria colectável'], ['nationalLimit', 'Fracção nacional correspondente — MZN'], ['nationalLimitReference', 'Suporte da fracção nacional e exercício']] as const).map(([key, label]) => <label key={key} className="block">{label}<input aria-label={'Internacional ' + (index + 1) + ' — ' + label} value={p[key]} onChange={e => patch({[key]: e.target.value})}/></label>)}
|
||||
<label className="block">Rendimento incluído na matéria colectável<select aria-label={'Internacional ' + (index + 1) + ' — Inclusão'} value={p.incomeIncluded} onChange={e => patch({incomeIncluded: e.target.value as InternationalPosition['incomeIncluded']})}><option value="">Informação em falta</option><option value="yes">Sim — documentado</option><option value="no">Não — crédito não aplicável nesta base</option></select></label>
|
||||
<button type="button" onClick={() => patch({assessmentKey: book.assessmentKey, qualificationCountry: p.country})}>Vincular inclusão e limites ao país e à colecta seleccionados</button>
|
||||
<label className="block">Convenção aplicável<select aria-label={'Internacional ' + (index + 1) + ' — Convenção'} value={p.treaty} onChange={e => patch({treaty: e.target.value as InternationalPosition['treaty']})}><option value="">Informação em falta</option><option value="yes">Sim — artigo, período e limite documentados</option><option value="no">Não — ausência de aplicação documentada</option></select></label>
|
||||
{(p.treaty === 'yes' || p.treatyLimit) && <label className="block">Imposto pago elegível nos termos da convenção — limite MZN<input aria-label={'Internacional ' + (index + 1) + ' — Limite CDT'} value={p.treatyLimit} onChange={e => patch({treatyLimit: e.target.value})}/></label>}
|
||||
<label className="block">Suporte da convenção ou da não aplicação<input aria-label={'Internacional ' + (index + 1) + ' — Suporte CDT'} value={p.treatyReference} onChange={e => patch({treatyReference: e.target.value})}/></label>
|
||||
<Evidence label={'Utilização anterior do crédito — país ' + (index + 1)} value={p.previousUse} change={previousUse => patch({previousUse})}/>
|
||||
<p>Utilização anterior é crédito já deduzido e documentado. Não é a soma de simulações históricas nem o imposto pago acima dos limites.</p>
|
||||
<button type="button" onClick={() => patch({payments: [...p.payments, {...newInternationalProof(crypto.randomUUID()), subjectNuit: p.subjectNuit, year: p.year}]})}>Registar imposto estrangeiro na posição {index + 1}</button>
|
||||
{p.payments.map((r, ri) => {
|
||||
const change = (v: Partial<InternationalTaxProof>) => paymentChange({...r, ...v});
|
||||
return <fieldset key={r.id} className="rounded border p-2"><legend>Imposto estrangeiro {index + 1}.{ri + 1}</legend>
|
||||
{([['document', 'Comprovativo'], ['issuer', 'Emitente'], ['date', 'Data (AAAA-MM-DD)'], ['subjectNuit', 'NUIT titular'], ['year', 'Exercício do pagamento'], ['currency', 'Moeda'], ['proofAmount', 'Total comprovado — MZN'], ['amount', 'Parcela de imposto seleccionado — MZN'], ['reference', 'Suporte do pagamento e afectação'], ['taxReference', 'Suporte da natureza de imposto sobre o rendimento']] as const).map(([key, label]) => <label key={key} className="block">{label}<input aria-label={'Imposto estrangeiro ' + (index + 1) + '.' + (ri + 1) + ' — ' + label} value={r[key]} onChange={e => change({[key]: e.target.value})}/></label>)}
|
||||
{(r.exerciseReference || r.date && !r.date.startsWith(p.year + '-')) && <label className="block">Suporte da correspondência da data ao exercício<input aria-label={'Imposto estrangeiro ' + (index + 1) + '.' + (ri + 1) + ' — Correspondência temporal'} value={r.exerciseReference} onChange={e => change({exerciseReference: e.target.value})}/></label>}
|
||||
<label className="block">Pagamento efectivo<select aria-label={'Imposto estrangeiro ' + (index + 1) + '.' + (ri + 1) + ' — Efectivo'} value={r.effective} onChange={e => change({effective: e.target.value as InternationalTaxProof['effective']})}><option value="">Informação em falta</option><option value="yes">Sim — comprovado</option><option value="no">Não — previsão/outra situação</option></select></label>
|
||||
<label className="block">Imposto sobre o rendimento elegível<select aria-label={'Imposto estrangeiro ' + (index + 1) + '.' + (ri + 1) + ' — Natureza'} value={r.incomeTax} onChange={e => change({incomeTax: e.target.value as InternationalTaxProof['incomeTax']})}><option value="">Informação em falta</option><option value="yes">Sim — natureza documentada</option><option value="no">Não — outra natureza, sem crédito presumido</option></select></label>
|
||||
<FiscalProofParcelFields value={r} change={change} label={"Imposto estrangeiro " + (index + 1) + "." + (ri + 1)}/><button type="button" onClick={() => patch({payments: p.payments.filter(x => x.id !== r.id)})}>Retirar imposto estrangeiro {index + 1}.{ri + 1}</button>
|
||||
</fieldset>;
|
||||
})}
|
||||
<button type="button" onClick={() => update({...c, positions: c.positions.filter(x => x.id !== p.id)})}>Retirar posição internacional {index + 1}</button>
|
||||
</fieldset>;
|
||||
})}
|
||||
</fieldset>;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import {FiscalProofParcelFields} from './fiscal-proof-parcel';
|
||||
import {newPecPayment, pecSource, type PecPayment} from '@/lib/irpc-pec';
|
||||
import type {AnnualBook} from '@/lib/irpc-annual-settlement';
|
||||
|
||||
export function SimulationIrpcPec({book, save}: {book: AnnualBook; save: (book: AnnualBook) => void}) {
|
||||
const detail = book.pecPayments;
|
||||
if (!detail) return <fieldset className="rounded border p-3"><legend>PEC — comprovativos do próprio exercício</legend><p>O resumo anterior fica conservado. Preparar o detalhe permite reconciliar os pagamentos efectivos e deduzi-los até à colecta disponível, sem reembolso do excedente PEC.</p><button type="button" onClick={() => save({...book, pecPayments: {version: 1, state: book.pec.state, reference: book.pec.reference, rows: []}})}>Preparar detalhe PEC</button></fieldset>;
|
||||
const update = (next: typeof detail) => save({...book, pecPayments: next});
|
||||
return <fieldset className="space-y-2 rounded border p-3"><legend>PEC — pagamentos efectivamente comprovados</legend>
|
||||
<p>{pecSource}</p><p>Identifique a natureza PEC no comprovativo. As utilizações anteriores exigem valor e suporte; zero conhecido é diferente de informação em falta. A parcela seleccionada não pode exceder o comprovado menos o já utilizado. Saldos de anos anteriores, cálculo da obrigação e créditos próprios permanecem separados.</p>
|
||||
<label className="block">Estado dos PEC<select aria-label="Estado dos PEC comprovados L8" value={detail.state} onChange={e => update({...detail, state: e.target.value as typeof detail.state})}><option value="">Informação em falta</option><option value="known">Pagamentos conhecidos</option><option value="absent">Ausência declarada com suporte</option></select></label>
|
||||
<label className="block">Suporte do inventário PEC<input aria-label="Suporte do inventário PEC L8" value={detail.reference} onChange={e => update({...detail, reference: e.target.value})}/></label>
|
||||
<button type="button" onClick={() => update({...detail, state: 'known', rows: [...detail.rows, {...newPecPayment(crypto.randomUUID()), subjectNuit: book.subjectNuit, year: book.year}]})}>Registar comprovativo PEC</button>
|
||||
{detail.rows.map((r, index) => {
|
||||
const change = (p: Partial<PecPayment>) => update({...detail, rows: detail.rows.map(x => x.id === r.id ? {...x, ...p} : x)});
|
||||
return <fieldset key={r.id} className="rounded border p-2"><legend>PEC {index + 1}</legend>
|
||||
{([['document', 'Comprovativo PEC'], ['issuer', 'Emitente'], ['date', 'Data (AAAA-MM-DD)'], ['subjectNuit', 'NUIT beneficiário'], ['year', 'Exercício de origem'], ['currency', 'Moeda'], ['proofAmount', 'Montante comprovado'], ['previouslyUsed', 'Já utilizado fora destas parcelas'], ['priorUseReference', 'Suporte das utilizações anteriores ou do zero conhecido'], ['amount', 'Parcela disponível seleccionada'], ['reference', 'Suporte do pagamento PEC e afectação']] as const).map(([key, label]) => <label key={key} className="block">{label}<input aria-label={'PEC L8 ' + (index + 1) + ' — ' + label} value={r[key]} onChange={e => change({[key]: e.target.value})}/></label>)}
|
||||
<label className="block">Pagamento PEC efectivamente comprovado<select aria-label={'PEC L8 ' + (index + 1) + ' — Efectivo'} value={r.effective} onChange={e => change({effective: e.target.value as PecPayment['effective']})}><option value="">Informação em falta</option><option value="yes">Sim — natureza PEC e pagamento reconciliados</option><option value="no">Não — previsão ou outra natureza</option></select></label>
|
||||
<FiscalProofParcelFields value={r} change={change} label={"PEC L8 " + (index + 1)}/><button type="button" onClick={() => update({...detail, rows: detail.rows.filter(x => x.id !== r.id)})}>Retirar selecção PEC {index + 1}</button>
|
||||
</fieldset>;
|
||||
})}
|
||||
<p>O detalhe substitui o resumo PEC apenas nesta execução. Os comprovativos e resultados de execuções anteriores permanecem no histórico; não há consumo automático de saldos oficiais.</p>
|
||||
</fieldset>;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
// Derived code projection of resources/data/iso3166.tab; keep the existing PHP catalogue authoritative.
|
||||
// Source SHA256 (LF): 837c80785080c8433fd9d4ea87e78f161ac7a40389301c5153d4f90198baeb2a; regenerate with node tools/country-iso-projection.mjs
|
||||
export const countryIsoCodes = 'AD AE AF AG AI AL AM AO AQ AR AS AT AU AW AX AZ BA BB BD BE BF BG BH BI BJ BL BM BN BO BQ BR BS BT BV BW BY BZ CA CC CD CF CG CH CI CK CL CM CN CO CR CU CV CW CX CY CZ DE DJ DK DM DO DZ EC EE EG EH ER ES ET FI FJ FK FM FO FR GA GB GD GE GF GG GH GI GL GM GN GP GQ GR GS GT GU GW GY HK HM HN HR HT HU ID IE IL IM IN IO IQ IR IS IT JE JM JO JP KE KG KH KI KM KN KP KR KW KY KZ LA LB LC LI LK LR LS LT LU LV LY MA MC MD ME MF MG MH MK ML MM MN MO MP MQ MR MS MT MU MV MW MX MY MZ NA NC NE NF NG NI NL NO NP NR NU NZ OM PA PE PF PG PH PK PL PM PN PR PS PT PW PY QA RE RO RS RU RW SA SB SC SD SE SG SH SI SJ SK SL SM SN SO SR SS ST SV SX SY SZ TC TD TF TG TH TJ TK TL TM TN TO TR TT TV TW TZ UA UG UM US UY UZ VA VC VE VG VI VN VU WF WS YE YT ZA ZM ZW'.split(' ');
|
||||
@@ -0,0 +1,12 @@
|
||||
export type FiscalProofParcel = {parcel?: string; parcelReference?: string};
|
||||
export const canonicalParcel = (s: string) => s.normalize('NFKC').trim().toLocaleLowerCase('pt').replace(/\s+/g, ' ');
|
||||
export function validateProofParcel(r: FiscalProofParcel) {
|
||||
if (r.parcel !== undefined && typeof r.parcel !== 'string' || r.parcelReference !== undefined && typeof r.parcelReference !== 'string') throw Error('Identificação da parcela documental não recuperável; original conservado.');
|
||||
}
|
||||
export function requireProofParcel(r: FiscalProofParcel) {
|
||||
validateProofParcel(r);
|
||||
if (!!r.parcel?.trim() !== !!r.parcelReference?.trim()) throw Error('Parcela documental e suporte devem corresponder; não se presume que linhas distintas representam impostos diferentes.');
|
||||
}
|
||||
export const parcelProjection = (r: FiscalProofParcel): FiscalProofParcel => ({...(r.parcel === undefined ? {} : {parcel: r.parcel}), ...(r.parcelReference === undefined ? {} : {parcelReference: r.parcelReference})});
|
||||
// Old proofs without a parcel cover the physical document. They overlap any newly identified parcel until reconciled.
|
||||
export const overlappingProofParcels = (a: FiscalProofParcel & {identity: string}, b: FiscalProofParcel & {identity: string}) => a.identity === b.identity && (!a.parcel?.trim() || !b.parcel?.trim() || canonicalParcel(a.parcel) === canonicalParcel(b.parcel));
|
||||
@@ -1,3 +1,8 @@
|
||||
import {carryPlan,carryIdentity,carrySource,newCreditCarry,validateCreditCarry,type CreditCarry} from './irpc-credit-carry.ts';
|
||||
import {economicTaxProof,economicGroupPending,economicDistributionIdentity,economicPosition,economicDeduction,economicSource,newEconomicCredits,validateEconomic,type EconomicCredits} from './irpc-economic-credit.ts';
|
||||
import {validateProofParcel,requireProofParcel,parcelProjection,overlappingProofParcels,type FiscalProofParcel} from './fiscal-proof-parcel.ts';
|
||||
import {validateInternational,internationalProofValue,internationalGroupPending,internationalPosition,internationalDeduction,internationalCreditSource,newInternationalCredits,type InternationalCredits,type InternationalTaxProof} from './irpc-international-credit.ts';
|
||||
import {validatePec,pecPaymentValue,pecDeduction,pecSource,type PecPayments} from './irpc-pec.ts';
|
||||
import {normalBook,normalCurrent} from './irpc-normal.ts';
|
||||
import {irpcBook,irpcCurrent} from './irpc-withholdings.ts';
|
||||
import {acquisitionScope} from './iva-acquisitions.ts';
|
||||
@@ -9,13 +14,35 @@ export const annualSettlementSource='CIRPC, arts. 63 e 67 (Lei 34/2007 e naturez
|
||||
type State=''|'known'|'absent';
|
||||
type Evidence={state:State;amount:string;reference:string};
|
||||
type Group={state:State;reference:string};
|
||||
export type AnnualWithholding={id:string;rowId:string;paymentId:string;sourceKey:string;subjectNuit:string;year:string;amount:string;incomeIncluded:''|'yes'|'no';reference:string};
|
||||
export type AnnualPpc={id:string;document:string;issuer:string;date:string;subjectNuit:string;year:string;currency:string;proofAmount:string;amount:string;effective:''|'yes'|'no';reference:string};
|
||||
export type AnnualWithholding=FiscalProofParcel&{id:string;rowId:string;paymentId:string;sourceKey:string;subjectNuit:string;year:string;amount:string;incomeIncluded:''|'yes'|'no';reference:string};
|
||||
export type AnnualPpc=FiscalProofParcel&{id:string;document:string;issuer:string;date:string;subjectNuit:string;year:string;currency:string;proofAmount:string;amount:string;effective:''|'yes'|'no';reference:string};
|
||||
type AnnualRecord={version:typeof annualSettlementVersion;key:string;createdAt:string;model:AnnualModel};
|
||||
export type AnnualBook={version:1;subjectNuit:string;year:string;reference:string;assessment:''|'normal'|'special';assessmentKey:string;assessmentReference:string;withholdings:Group&{rows:AnnualWithholding[]};ppc:Group&{rows:AnnualPpc[]};pec:Evidence;credits:Evidence;result?:AnnualRecord;history:AnnualRecord[]};
|
||||
export type AnnualBook={version:1;subjectNuit:string;year:string;reference:string;assessment:''|'normal'|'special';assessmentKey:string;assessmentReference:string;withholdings:Group&{rows:AnnualWithholding[]};ppc:Group&{rows:AnnualPpc[]};pec:Evidence;pecPayments?:PecPayments;credits:Evidence;internationalCredits?:InternationalCredits;creditCarry?:CreditCarry;economicCredits?:EconomicCredits;result?:AnnualRecord;history:AnnualRecord[]};
|
||||
export const annualSource=(b:AnnualBook)=>{
|
||||
const excluded=b.internationalCredits?(b.pecPayments?'Créditos económicos/benefícios positivos':'PEC/créditos económicos/benefícios positivos'):(b.pecPayments?'Créditos próprios positivos':'PEC/créditos positivos');
|
||||
return annualSettlementSource.replace('PEC/créditos positivos',b.economicCredits?(b.pecPayments?'Benefícios positivos':'PEC/benefícios positivos'):excluded)+(b.pecPayments?' '+pecSource:'')+(b.internationalCredits?' '+internationalCreditSource:'')+(b.creditCarry?' '+carrySource:'')+(b.economicCredits?' '+economicSource:'');
|
||||
};
|
||||
export function prepareEconomicBook(b:AnnualBook):AnnualBook{
|
||||
if(b.economicCredits)return b;
|
||||
const next=prepareInternationalBook(b),e=newEconomicCredits(),old=next.internationalCredits!.economic;
|
||||
if(old.state==='absent'){e.state='absent';e.reference=old.reference;}
|
||||
return{...next,economicCredits:e};
|
||||
}
|
||||
export function prepareCreditCarry(b:AnnualBook):AnnualBook{return b.creditCarry?b:{...b,creditCarry:newCreditCarry()};}
|
||||
export function prepareInternationalBook(b:AnnualBook):AnnualBook{
|
||||
if(b.internationalCredits)return b;
|
||||
const c=newInternationalCredits(),old=b.credits;
|
||||
if(old.state==='absent'||old.state==='known'&&old.amount==='0'){
|
||||
c.economic=structuredClone(old);c.benefits=structuredClone(old);
|
||||
if(old.state==='absent'){c.state='absent';c.reference=old.reference;}
|
||||
}
|
||||
return{...b,internationalCredits:c};
|
||||
}
|
||||
|
||||
const evidence=():Evidence=>({state:'',amount:'',reference:''});
|
||||
export const newAnnualBook=():AnnualBook=>({version:1,subjectNuit:'',year:'',reference:'',assessment:'',assessmentKey:'',assessmentReference:'',withholdings:{state:'',reference:'',rows:[]},ppc:{state:'',reference:'',rows:[]},pec:evidence(),credits:evidence(),history:[]});
|
||||
export const newAnnualPpc=(id:string):AnnualPpc=>({id,document:'',issuer:'',date:'',subjectNuit:'',year:'',currency:'MZN',proofAmount:'',amount:'',effective:'',reference:''});
|
||||
export const newInternationalProof=(id:string):InternationalTaxProof=>({...newAnnualPpc(id),incomeTax:'',taxReference:'',exerciseReference:''});
|
||||
const zero=()=>fraction(0n),cmp=(a:FiscalFraction,b:FiscalFraction)=>BigInt(a.numerator)*BigInt(b.denominator)-BigInt(b.numerator)*BigInt(a.denominator);
|
||||
const iso=(s:string)=>/^20\d{2}-\d{2}-\d{2}$/.test(s)&&Number.isFinite(Date.parse(s))&&new Date(s).toISOString().slice(0,10)===s;
|
||||
const moneyInput=(s:string)=>{if(!/^\d{1,18}(\.\d{1,2})?$/.test(s))throw Error('MontanteMZN em falta/inválido;máximo dois decimais,sem zero presumido.');return decimalFraction(s);};
|
||||
@@ -24,11 +51,16 @@ const proofIdentity=(issuer:string,document:string,_date:string)=>JSON.stringify
|
||||
function store(d:Record<string,unknown>){const s=JSON.parse(String(d.modelo_a_snapshot||'{"version":1,"books":{}}'));if(s.version!==1||!s.books||typeof s.books!=='object'||Array.isArray(s.books))throw Error('Livros fiscais não recuperáveis;originais conservados.');return s;}
|
||||
function books(d:Record<string,unknown>):Record<string,AnnualBook>{const m=store(d).irpcAnnual;if(m===undefined)return{};if(!m||typeof m!=='object'||Array.isArray(m))throw Error('MapaL8 não recuperável;originais conservados.');return m;}
|
||||
export function validateAnnual(b:AnnualBook){
|
||||
if(b?.economicCredits!==undefined)validateEconomic(b.economicCredits);
|
||||
if(b?.creditCarry!==undefined)validateCreditCarry(b.creditCarry);
|
||||
if(b?.pecPayments!==undefined)validatePec(b.pecPayments);
|
||||
if(b?.internationalCredits!==undefined)validateInternational(b.internationalCredits);
|
||||
if(!b||b.version!==1||['subjectNuit','year','reference','assessmentKey','assessmentReference'].some(k=>typeof b[k as 'year']!=='string')||!['','normal','special'].includes(b.assessment)||!Array.isArray(b.history))throw Error('PreparaçãoL8 não recuperável.');
|
||||
for(const g of[b.withholdings,b.ppc,b.pec,b.credits])if(!g||!['','known','absent'].includes(g.state)||typeof g.reference!=='string')throw Error('EstadoL8 não recuperável.');
|
||||
for(const g of[b.pec,b.credits])if(typeof g.amount!=='string')throw Error('CréditoL8 não recuperável.');
|
||||
const w=['id','rowId','paymentId','sourceKey','subjectNuit','year','amount','incomeIncluded','reference'],p=Object.keys(newAnnualPpc(''));
|
||||
for(const[rows,keys]of[[b.withholdings.rows,w],[b.ppc.rows,p]]as const)if(!Array.isArray(rows)||new Set(rows.map(r=>r?.id)).size!==rows.length||rows.some(r=>!r||keys.some(k=>typeof(r as unknown as Record<string,unknown>)[k]!=='string')))throw Error('ComprovativosL8 não recuperáveis ou identificador repetido.');
|
||||
for(const r of[...b.withholdings.rows,...b.ppc.rows])validateProofParcel(r);
|
||||
if(b.withholdings.rows.some(r=>!['','yes','no'].includes(r.incomeIncluded))||b.ppc.rows.some(r=>!['','yes','no'].includes(r.effective)))throw Error('QualificaçãoL8 não recuperável.');
|
||||
for(const r of[b.result,...b.history])if(r&&(r.version!==annualSettlementVersion||typeof r.key!=='string'||typeof r.createdAt!=='string'||!r.model||!Array.isArray(r.model.pending)))throw Error('HistóricoL8 não recuperável.');
|
||||
}
|
||||
@@ -53,7 +85,7 @@ export function reuseAnnualWithholding(d:Record<string,unknown>,b:AnnualBook,row
|
||||
if(b.withholdings.rows.some(r=>r.rowId===rowId&&r.paymentId===paymentId))throw Error('Retenção/prova já seleccionada;altere a parcela existente sem duplicar.');
|
||||
return{...b,withholdings:{...b.withholdings,rows:[...b.withholdings.rows,{id,rowId,paymentId,sourceKey:o.key,subjectNuit:b.subjectNuit,year:o.year,amount:o.amount,incomeIncluded:'',reference:''}]}};
|
||||
}
|
||||
function dependencies(d:Record<string,unknown>){let normal:unknown,L5:unknown;try{const a=annualAssessment(d);normal=[a.record,a.current];}catch(e){normal=String(e);}try{const b=irpcBook(d);L5=[b.rows,b.payments,b.result,irpcCurrent(d,b)];}catch(e){L5=String(e);}return[normal,L5,Object.entries(books(d)).filter(([k])=>k!==acquisitionScope(d)).map(([k,b])=>[k,b.subjectNuit,b.year,b.withholdings,b.ppc])];}
|
||||
function dependencies(d:Record<string,unknown>){let normal:unknown,L5:unknown;try{const a=annualAssessment(d);normal=[a.record,a.current];}catch(e){normal=String(e);}try{const b=irpcBook(d);L5=[b.rows,b.payments,b.result,irpcCurrent(d,b)];}catch(e){L5=String(e);}return[normal,L5,Object.entries(books(d)).filter(([k])=>k!==acquisitionScope(d)).map(([k,b])=>[k,b.subjectNuit,b.year,b.withholdings,b.ppc,...(b.pecPayments?[b.pecPayments]:[]),...(b.internationalCredits?[b.internationalCredits]:[]),...(b.creditCarry?[b.creditCarry]:[]),...(b.economicCredits?[b.economicCredits]:[])])];}
|
||||
function key(d:Record<string,unknown>,b:AnnualBook){const{result:_,history:__,...input}=b;return closureFingerprint([annualSettlementVersion,acquisitionScope(d),input,dependencies(d)]);}
|
||||
export function annualCurrent(d:Record<string,unknown>,b:AnnualBook){return !!b.result&&b.result.key===key(d,b);}
|
||||
export function calculateAnnual(d:Record<string,unknown>,b:AnnualBook){
|
||||
@@ -69,31 +101,75 @@ export function calculateAnnual(d:Record<string,unknown>,b:AnnualBook){
|
||||
const group=(g:Group,rows:unknown[],label:string)=>{if(!g.state||!g.reference.trim())pending.push(label+':identifique valor conhecido,ausência declarada ou informação em falta com suporte.');if(g.state==='absent'&&rows.length)pending.push(label+':ausência declarada contradiz parcelas presentes;conserve e reconcilie.');if(g.state==='known'&&!rows.length)pending.push(label+':não há comprovativos seleccionados;ausência exige declaração própria.');};
|
||||
group(b.withholdings,b.withholdings.rows,'Retenções sofridas por conta');group(b.ppc,b.ppc.rows,'PPC efectuados');
|
||||
try{if(b.withholdings.state==='absent'&&options.some(o=>o.ready&&o.year===b.year&&cmp(moneyInput(o.amount),zero())>0n))pending.push('Ausência de retenções sofridas contradiz provaL5 efectiva por conta no exercício;reconcilie sem a ignorar.');}catch(e){pending.push('Reconciliação da ausência de retenções: '+String(e));}
|
||||
for(const[label,e]of[['PEC',b.pec],['Créditos com regras próprias',b.credits]]as const){try{if(!e.state||!e.reference.trim())throw Error('presença/ausência e suporte em falta');if(e.state==='absent'){if(e.amount.trim())throw Error('ausência contradiz montante preenchido');}else if(cmp(moneyInput(e.amount),zero())!==0n)throw Error('montante positivo fora deste recorte:corresponder ordem,limite e transporte próprios;sem abatimento ou reembolso automático');}catch(err){pending.push(label+': '+String(err));}}
|
||||
const claims:Array<{id:string;kind:'withholding'|'ppc';identity:string;proofAmount:FiscalFraction|null;value:FiscalFraction|null;sourceKey:string;allocationAmount:FiscalFraction|null;document:string;issuer:string;date:string;subjectNuit:string;year:string;reason:string}>=[];
|
||||
for(const[label,e]of[...(b.pecPayments?[]:[['PEC',b.pec]as const]),...(b.internationalCredits?[]:[['Créditos com regras próprias',b.credits]as const])]){try{if(!e.state||!e.reference.trim())throw Error('presença/ausência e suporte em falta');if(e.state==='absent'){if(e.amount.trim())throw Error('ausência contradiz montante preenchido');}else if(cmp(moneyInput(e.amount),zero())!==0n)throw Error('montante positivo fora deste recorte:corresponder ordem,limite e transporte próprios;sem abatimento ou reembolso automático');}catch(err){pending.push(label+': '+String(err));}}
|
||||
const claims:Array<FiscalProofParcel&{id:string;kind:'withholding'|'ppc'|'pec'|'foreign_credit'|'economic_credit';country?:string;positionId?:string;previouslyUsed?:FiscalFraction;identity:string;proofAmount:FiscalFraction|null;value:FiscalFraction|null;sourceKey:string;allocationAmount:FiscalFraction|null;document:string;issuer:string;date:string;subjectNuit:string;year:string;reason:string}>=[];
|
||||
for(const r of b.withholdings.rows){const o=options.find(o=>o.rowId===r.rowId&&o.paymentId===r.paymentId),issues:string[]=[];let value:FiscalFraction|null=null,proofAmount:FiscalFraction|null=null;
|
||||
try{if(!o||!o.ready||r.sourceKey!==o.key)throw Error(o?.reason||'provaL5 ausente ou alterada;reutilize sem duplicar');if(r.subjectNuit!==b.subjectNuit||r.year!==b.year||o.year!==b.year||!o.date.startsWith(b.year+'-'))throw Error('entidade/exercício da retenção não correspondem ao fecho');if(r.incomeIncluded!=='yes'||!r.reference.trim())throw Error('confirme o próprio beneficiário/NUIT e inclusão do rendimento na base do exercício com suporte');value=moneyInput(r.amount);proofAmount=moneyInput(o.proofAmount);if(cmp(value,moneyInput(o.amount))>0n)throw Error('parcela ultrapassa a retenção efectivamente comprovada e afectada emL5');if(b.withholdings.rows.filter(x=>x.rowId===r.rowId&&x.paymentId===r.paymentId).length!==1)throw Error('retenção/prova repetida;uma afectação por linhaL5');if(assessment?.record?.model.special?.cooperative.status==='calculated'&&cmp(value,zero())>0n)throw Error('cooperativa integralmente elegível declara ausência de rendimentos retidos;segregue o casoL7 antes do crédito anual');}catch(e){issues.push(String(e));value=null;}
|
||||
claims.push({id:r.id,kind:'withholding',identity:o?.identity??'',proofAmount,value,sourceKey:r.sourceKey,allocationAmount:o&&value?moneyInput(o.amount):null,document:o?.document??r.paymentId,issuer:o?.issuer??'',date:o?.date??'',subjectNuit:r.subjectNuit,year:r.year,reason:issues.join(' ')});
|
||||
try{requireProofParcel(r);if(!o||!o.ready||r.sourceKey!==o.key)throw Error(o?.reason||'provaL5 ausente ou alterada;reutilize sem duplicar');if(r.subjectNuit!==b.subjectNuit||r.year!==b.year||o.year!==b.year||!o.date.startsWith(b.year+'-'))throw Error('entidade/exercício da retenção não correspondem ao fecho');if(r.incomeIncluded!=='yes'||!r.reference.trim())throw Error('confirme o próprio beneficiário/NUIT e inclusão do rendimento na base do exercício com suporte');value=moneyInput(r.amount);proofAmount=moneyInput(o.proofAmount);if(cmp(value,moneyInput(o.amount))>0n)throw Error('parcela ultrapassa a retenção efectivamente comprovada e afectada emL5');if(b.withholdings.rows.filter(x=>x.rowId===r.rowId&&x.paymentId===r.paymentId).length!==1)throw Error('retenção/prova repetida;uma afectação por linhaL5');if(assessment?.record?.model.special?.cooperative.status==='calculated'&&cmp(value,zero())>0n)throw Error('cooperativa integralmente elegível declara ausência de rendimentos retidos;segregue o casoL7 antes do crédito anual');}catch(e){issues.push(String(e));value=null;}
|
||||
claims.push({...parcelProjection(r),id:r.id,kind:'withholding',identity:o?.identity??'',proofAmount,value,sourceKey:r.sourceKey,allocationAmount:o&&value?moneyInput(o.amount):null,document:o?.document??r.paymentId,issuer:o?.issuer??'',date:o?.date??'',subjectNuit:r.subjectNuit,year:r.year,reason:issues.join(' ')});
|
||||
}
|
||||
for(const r of b.ppc.rows){const issues:string[]=[];let value:FiscalFraction|null=null,proofAmount:FiscalFraction|null=null;
|
||||
try{if(!r.document.trim()||!r.issuer.trim()||!r.reference.trim()||r.effective!=='yes'||r.currency!=='MZN'||!iso(r.date)||!r.date.startsWith(b.year+'-')||r.subjectNuit!==b.subjectNuit||r.year!==b.year)throw Error('PPC exige comprovativo efectivo,MZN,data,entidade e exercício correspondentes;projecção/tesouraria não é pagamento');value=moneyInput(r.amount);proofAmount=moneyInput(r.proofAmount);if(cmp(value,proofAmount)>0n)throw Error('parcelaPPC ultrapassa o montante comprovado');}catch(e){issues.push(String(e));value=null;}
|
||||
claims.push({id:r.id,kind:'ppc',identity:proofIdentity(r.issuer,r.document,r.date),proofAmount,value,sourceKey:'',allocationAmount:null,document:r.document,issuer:r.issuer,date:r.date,subjectNuit:r.subjectNuit,year:r.year,reason:issues.join(' ')});
|
||||
try{requireProofParcel(r);if(!r.document.trim()||!r.issuer.trim()||!r.reference.trim()||r.effective!=='yes'||r.currency!=='MZN'||!iso(r.date)||!r.date.startsWith(b.year+'-')||r.subjectNuit!==b.subjectNuit||r.year!==b.year)throw Error('PPC exige comprovativo efectivo,MZN,data,entidade e exercício correspondentes;projecção/tesouraria não é pagamento');value=moneyInput(r.amount);proofAmount=moneyInput(r.proofAmount);if(cmp(value,proofAmount)>0n)throw Error('parcelaPPC ultrapassa o montante comprovado');}catch(e){issues.push(String(e));value=null;}
|
||||
claims.push({...parcelProjection(r),id:r.id,kind:'ppc',identity:proofIdentity(r.issuer,r.document,r.date),proofAmount,value,sourceKey:'',allocationAmount:null,document:r.document,issuer:r.issuer,date:r.date,subjectNuit:r.subjectNuit,year:r.year,reason:issues.join(' ')});
|
||||
}
|
||||
const elsewhere:Array<{identity:string;subjectNuit:string;year:string;value:FiscalFraction;proofAmount:FiscalFraction;sourceKey:string}>=[];
|
||||
for(const[scope,other]of Object.entries(books(d))){if(scope===acquisitionScope(d))continue;try{validateAnnual(other);for(const r of other.ppc.rows)elsewhere.push({identity:proofIdentity(r.issuer,r.document,r.date),subjectNuit:r.subjectNuit,year:r.year,value:moneyInput(r.amount),proofAmount:moneyInput(r.proofAmount),sourceKey:''});for(const r of other.withholdings.rows){const source=(store(d).irpcWithholdings??{})[scope];const row=source?.rows?.find((x:{id:string})=>x.id===r.rowId),p=source?.payments?.find((x:{id:string})=>x.id===r.paymentId);if(row&&p)elsewhere.push({identity:proofIdentity(row.counterparty,p.reference,p.date),subjectNuit:r.subjectNuit,year:r.year,value:moneyInput(r.amount),proofAmount:moneyInput(p.amount),sourceKey:r.sourceKey});}}catch(e){if(other?.subjectNuit===b.subjectNuit)pending.push('Outro mapa da mesma entidade não recuperável para conferir duplicações: '+scope+';'+String(e));}}
|
||||
for(const c of claims){if(!c.value||!c.proofAmount)continue;const same=claims.filter(x=>x.identity===c.identity&&x.value),other=elsewhere.filter(x=>x.identity===c.identity);let issue='';
|
||||
if(same.filter(x=>x.kind==='ppc').length>1||new Set(same.map(x=>x.kind)).size>1)issue='Mesmo comprovativo repetido ou usado como retenção ePPC;reconcilie sem dedução dupla.';
|
||||
if(b.pecPayments){group(b.pecPayments,b.pecPayments.rows,'PEC pagos no próprio exercício');for(const r of b.pecPayments.rows){let value:FiscalFraction|null=null,proofAmount:FiscalFraction|null=null,previouslyUsed:FiscalFraction|undefined;let reason='';try{({value,proofAmount,previouslyUsed}=pecPaymentValue(r,b.subjectNuit,b.year));}catch(e){reason=String(e);}claims.push({...parcelProjection(r),id:r.id,kind:'pec',identity:proofIdentity(r.issuer,r.document,r.date),value,proofAmount,previouslyUsed,sourceKey:'',allocationAmount:null,document:r.document,issuer:r.issuer,date:r.date,subjectNuit:r.subjectNuit,year:r.year,reason});}}
|
||||
if(b.internationalCredits){
|
||||
pending.push(...internationalGroupPending(b.internationalCredits,!!b.economicCredits));
|
||||
const countries=b.internationalCredits.positions.map(p=>p.country);
|
||||
if(new Set(countries).size!==countries.length)pending.push('Um país/exercício tem várias posições;consolide rendimentos e comprovativos sob uma única fracção nacional,sem repetir o limite.');
|
||||
for(const p of b.internationalCredits.positions)for(const r of p.payments){let value:FiscalFraction|null=null,proofAmount:FiscalFraction|null=null,reason='';try{({value,proofAmount}=internationalProofValue(r,b.subjectNuit,b.year));}catch(e){reason=String(e);}claims.push({...parcelProjection(r),id:r.id,kind:'foreign_credit',country:p.country,positionId:p.id,identity:proofIdentity(r.issuer,r.document,r.date),proofAmount,value,sourceKey:'',allocationAmount:null,document:r.document,issuer:r.issuer,date:r.date,subjectNuit:r.subjectNuit,year:r.year,reason});}
|
||||
}
|
||||
if(b.economicCredits){
|
||||
pending.push(...economicGroupPending(b.economicCredits));
|
||||
if(!b.internationalCredits)pending.push('Distinguir o detalhe económico do inventário internacional e dos benefícios; use a preparação existente sem apagar o resumo anterior.');
|
||||
for(const r of b.economicCredits.rows){let value:FiscalFraction|null=null,proofAmount:FiscalFraction|null=null,previouslyUsed:FiscalFraction|undefined,reason='';try{({value,proofAmount,previouslyUsed}=economicTaxProof(r));}catch(e){reason=String(e);}claims.push({id:r.id,kind:'economic_credit',positionId:r.id,identity:proofIdentity(r.taxIssuer,r.taxDocument,r.distributionDate),parcel:r.taxParcel,parcelReference:r.parcelReference,proofAmount,value,previouslyUsed,sourceKey:'',allocationAmount:null,document:r.taxDocument,issuer:r.taxIssuer,date:r.distributionDate,subjectNuit:r.subjectNuit,year:r.year,reason});}
|
||||
}
|
||||
const elsewhere:Array<FiscalProofParcel&{identity:string;subjectNuit:string;year:string;value:FiscalFraction;proofAmount:FiscalFraction;sourceKey:string;kind:'ppc'|'pec'|'withholding'|'foreign_credit'|'economic_credit';country?:string;previouslyUsed?:FiscalFraction}>=[];
|
||||
for(const[scope,other]of Object.entries(books(d))){if(scope===acquisitionScope(d))continue;try{validateAnnual(other);for(const r of other.economicCredits?.rows??[]){
|
||||
if(b.economicCredits?.rows.some(x=>economicDistributionIdentity(x)===economicDistributionIdentity(r)))pending.push('Parcela de lucros económicos já preparada noutro mapa;consolide sem duplicar o crédito, mesmo com documentos de imposto diferentes.');
|
||||
const v=economicTaxProof(r);elsewhere.push({identity:proofIdentity(r.taxIssuer,r.taxDocument,r.distributionDate),parcel:r.taxParcel,parcelReference:r.parcelReference,subjectNuit:r.subjectNuit,year:r.year,...v,sourceKey:'',kind:'economic_credit'});
|
||||
}for(const p of other.internationalCredits?.positions??[]){if(other.subjectNuit===b.subjectNuit&&other.year===b.year&&b.internationalCredits?.positions.some(x=>x.country===p.country))pending.push('País '+p.country+' já preparado noutro mapa da entidade/exercício;consolide a fracção nacional e usos sem duplicar quotas.');for(const r of p.payments){const v=internationalProofValue(r,other.subjectNuit,other.year);elsewhere.push({...parcelProjection(r),identity:proofIdentity(r.issuer,r.document,r.date),subjectNuit:r.subjectNuit,year:r.year,...v,sourceKey:'',kind:'foreign_credit',country:p.country});}}for(const r of other.pecPayments?.rows??[]){const p=pecPaymentValue(r,other.subjectNuit,other.year);elsewhere.push({...parcelProjection(r),identity:proofIdentity(r.issuer,r.document,r.date),subjectNuit:r.subjectNuit,year:r.year,...p,sourceKey:'',kind:'pec'});}for(const r of other.ppc.rows)elsewhere.push({...parcelProjection(r),identity:proofIdentity(r.issuer,r.document,r.date),subjectNuit:r.subjectNuit,year:r.year,value:moneyInput(r.amount),proofAmount:moneyInput(r.proofAmount),sourceKey:'',kind:'ppc'});for(const r of other.withholdings.rows){const source=(store(d).irpcWithholdings??{})[scope];const row=source?.rows?.find((x:{id:string})=>x.id===r.rowId),p=source?.payments?.find((x:{id:string})=>x.id===r.paymentId);if(row&&p)elsewhere.push({...parcelProjection(r),identity:proofIdentity(row.counterparty,p.reference,p.date),subjectNuit:r.subjectNuit,year:r.year,value:moneyInput(r.amount),proofAmount:moneyInput(p.amount),sourceKey:r.sourceKey,kind:'withholding'});}}catch(e){if(other?.subjectNuit===b.subjectNuit)pending.push('Outro mapa da mesma entidade não recuperável para conferir duplicações: '+scope+';'+String(e));}}
|
||||
for(const c of claims){if(!c.value||!c.proofAmount)continue;const same=claims.filter(x=>overlappingProofParcels(x,c)&&x.value),other=elsewhere.filter(x=>overlappingProofParcels(x,c));let issue='';
|
||||
if(same.filter(x=>x.kind!=='withholding').length>1&&!same.every(x=>x.kind==='economic_credit')||new Set(same.map(x=>x.kind)).size>1)issue='Mesmo comprovativo repetido ou usado com naturezas diferentes (retenção/PPC/PEC/crédito internacional);reconcilie sem dedução dupla.';
|
||||
if(other.some(x=>x.subjectNuit!==b.subjectNuit||x.year!==b.year))issue='Mesmo comprovativo já associado a outra entidade/exercício;reconcilie a titularidade e a afectação.';
|
||||
if([...same,...other].some(x=>x.proofAmount&&cmp(x.proofAmount,c.proofAmount!)!==0n)||cmp(addFractions([...same.flatMap(x=>x.value?[x.value]:[]),...other.map(x=>x.value)]),c.proofAmount)>0n)issue='Comprovativo utilizado além do montante disponível ou com montantes divergentes neste/outro mapa.';
|
||||
if(c.kind==='withholding'&&c.sourceKey&&c.allocationAmount&&cmp(addFractions([...same.filter(x=>x.sourceKey===c.sourceKey).flatMap(x=>x.value?[x.value]:[]),...other.filter(x=>x.sourceKey===c.sourceKey).map(x=>x.value)]),c.allocationAmount)>0n)issue='Mesma afectação L5 reutilizada além da parcela disponível, ainda que o comprovativo global seja maior.';
|
||||
if((c.kind==='pec'||c.kind==='economic_credit')&&c.previouslyUsed){if(other.some(x=>x.kind!==c.kind)||[...same,...other].some(x=>!x.previouslyUsed||cmp(x.previouslyUsed,c.previouslyUsed!)!==0n)||cmp(addFractions([c.previouslyUsed,...same.flatMap(x=>x.value?[x.value]:[]),...other.map(x=>x.value)]),c.proofAmount)>0n)issue='PEC/IRPC económico com utilização/atribuição anterior divergente, natureza incompatível ou parcelas acima do saldo disponível.';}
|
||||
if(other.some(x=>x.kind!==c.kind))issue='Comprovativo já afectado a outra natureza (retenção/PPC/PEC/crédito internacional);sem dupla dedução.';
|
||||
if(c.kind==='foreign_credit'&&other.some(x=>x.country!==c.country))issue='Comprovativo estrangeiro associado a outro país/natureza;reconcilie sem duplicação.';
|
||||
if(c.kind==='withholding'&&c.sourceKey&&c.allocationAmount&&cmp(addFractions([...claims.filter(x=>x.kind==='withholding'&&x.sourceKey===c.sourceKey).flatMap(x=>x.value?[x.value]:[]),...elsewhere.filter(x=>x.kind==='withholding'&&x.sourceKey===c.sourceKey).map(x=>x.value)]),c.allocationAmount)>0n)issue='Mesma afectação L5 reutilizada além da parcela disponível, ainda que o comprovativo global seja maior.';
|
||||
if(issue)c.reason=[c.reason,issue].filter(Boolean).join(' ');
|
||||
}
|
||||
for(const c of claims)if(c.reason)pending.push((c.document||c.id)+': '+c.reason);
|
||||
const knownWithholdings=addFractions(claims.filter(c=>c.kind==='withholding'&&!c.reason&&c.value).map(c=>c.value!)),knownPpc=addFractions(claims.filter(c=>c.kind==='ppc'&&!c.reason&&c.value).map(c=>c.value!));
|
||||
const ready=pending.length===0&&colecta!==null,arithmeticBalance=ready?subtractFractions(subtractFractions(colecta!,knownWithholdings),knownPpc):null;
|
||||
const positions=b.internationalCredits?.positions.map(p=>{const own=claims.filter(c=>c.kind==='foreign_credit'&&c.positionId===p.id);const m=internationalPosition(p,addFractions(own.filter(c=>!c.reason&&c.value).map(c=>c.value!)),own.filter(c=>c.reason).map(c=>c.reason),b.subjectNuit,b.year,b.assessmentKey);pending.push(...m.pending.map(e=>'País '+p.country+': '+e));return m;})??[];
|
||||
const economicRows=b.economicCredits?.rows.map(r=>{
|
||||
const issues=claims.filter(c=>c.kind==='economic_credit'&&c.positionId===r.id&&c.reason).map(c=>c.reason);
|
||||
const m=economicPosition(r,b.subjectNuit,b.year,b.assessmentKey,colecta,issues);
|
||||
pending.push(...m.pending.map(e=>'Lucros '+r.distributionDocument+': '+e));return m;
|
||||
})??[];
|
||||
if(b.economicCredits){
|
||||
if(b.economicCredits.rows.length&&assessment?.kind!=='normal')pending.push('Crédito económico delimitado à colecta normal corrente;efeitos L7 exigem correspondência própria.');
|
||||
}
|
||||
const economicPreview=b.economicCredits?economicDeduction(economicRows,colecta,true):null;
|
||||
const remainingForCarry=economicPreview?.afterCredit??colecta;
|
||||
const pecAvailable=addFractions(claims.filter(c=>c.kind==='pec'&&!c.reason&&c.value).map(c=>c.value!));
|
||||
const carryDuplicates:string[]=[];
|
||||
if(b.creditCarry)for(const[scope,other]of Object.entries(books(d))){
|
||||
if(scope===acquisitionScope(d)||other.subjectNuit!==b.subjectNuit)continue;
|
||||
try{validateAnnual(other);if(other.creditCarry?.lots.some(r=>b.creditCarry!.lots.some(x=>carryIdentity(x)===carryIdentity(r))))carryDuplicates.push('Saldo anterior já preparado noutro mapa da entidade;consolide a origem e usos sem duplicar: '+scope);}
|
||||
catch(e){carryDuplicates.push('Outro mapa da entidade não recuperável para conferir saldos anteriores: '+scope+';'+String(e));}
|
||||
}
|
||||
const carry=b.creditCarry?carryPlan(b.creditCarry,b.subjectNuit,b.year,b.assessmentKey,addFractions(positions.flatMap(p=>p.available&&!p.pending.length?[p.available]:[])),pecAvailable,remainingForCarry,carryDuplicates):null;
|
||||
if(carry)pending.push(...carry.pending);
|
||||
const ready=pending.length===0&&colecta!==null,economic=b.economicCredits?economicDeduction(economicRows,colecta,ready):null,afterEconomic=economic?.afterCredit??colecta;
|
||||
const international=b.internationalCredits?internationalDeduction(positions,afterEconomic,ready,carry?.international??undefined):null;
|
||||
const creditCollecta=ready&&carry?subtractFractions(international?.afterCredit??afterEconomic!,carry.priorInternational):international?.afterCredit??afterEconomic;
|
||||
const pec=b.pecPayments?pecDeduction(pecAvailable,creditCollecta,ready,carry?.pec??undefined):null;
|
||||
const afterPec=ready&&carry?subtractFractions(pec?.afterPec??creditCollecta!,carry.priorPec):pec?.afterPec??creditCollecta;
|
||||
const arithmeticBalance=ready?subtractFractions(subtractFractions(afterPec!,knownWithholdings),knownPpc):null;
|
||||
if(international&&international.insufficientCollecta&&cmp(international.insufficientCollecta,zero())>0n)warn.push('Crédito elegível sem colecta suficiente:sem reembolso,transporte ou escolha automática da afectação por país.');
|
||||
let payable:FiscalFraction|null=null,recoverable:FiscalFraction|null=null,belowMinimum:FiscalFraction|null=null;let status:'payable'|'recoverable'|'balanced'|'below_minimum'|'indeterminate'='indeterminate';
|
||||
if(arithmeticBalance){const n=BigInt(arithmeticBalance.numerator),abs=fraction(n<0n?-n:n,BigInt(arithmeticBalance.denominator));payable=zero();recoverable=zero();belowMinimum=zero();if(!n)status='balanced';else if(cmp(abs,fraction(100n))<0n){status='below_minimum';belowMinimum=abs;warn.push('Art27(6):saldo inferior a100MZN,sem cobrança/reembolso neste recorte;aritmética conservada,não é transporte de crédito.');}else if(n>0n){status='payable';payable=abs;}else{status='recoverable';recoverable=abs;}}
|
||||
return{version:annualSettlementVersion,input:structuredClone(input),subjectNuit:b.subjectNuit,year:b.year,assessment:assessment?.record?{key:b.assessmentKey,kind:b.assessment,version:assessment.record.model.version,createdAt:assessment.record.createdAt,colecta,period:assessment.period,origin:assessment.record.model.origin}:null,claims,knownWithholdings,knownPpc,arithmeticBalance,payable,recoverable,belowMinimum,status,pending:[...new Set(pending)],warnings:warn,source:annualSettlementSource,officialBalancesConsumed:false,submitted:false,refundReceived:false,limitations:['L8 parcial:PEC e créditos positivos,tributação autónoma,períodos especiais e pagamentos noutra moeda exigem regras próprias.','PPC aqui é pagamento comprovado;não cálculo automático da obrigação ou calendário.','Retenção sofrida não é retenção entregue pela empresa por conta de terceiros.','Valores racionais exactos;camposM20/M22 e representação não presumidos.','Saldo simulado por colecta projectada;sem pagamento,submissão ou reembolso automático.']};
|
||||
return{version:annualSettlementVersion,input:structuredClone(input),subjectNuit:b.subjectNuit,year:b.year,assessment:assessment?.record?{key:b.assessmentKey,kind:b.assessment,version:assessment.record.model.version,createdAt:assessment.record.createdAt,colecta,period:assessment.period,origin:assessment.record.model.origin}:null,claims,...(economic?{economicCredit:economic}:{}),internationalCredit:international,pecDeduction:pec,...(carry?{creditCarry:{...carry,afterInternational:ready?creditCollecta:null,afterPec:ready?afterPec:null}}:{}),knownWithholdings,knownPpc,arithmeticBalance,payable,recoverable,belowMinimum,status,pending:[...new Set(pending)],warnings:warn,source:annualSource(b),officialBalancesConsumed:false,submitted:false,refundReceived:false,limitations:[...(b.economicCredits?['Crédito económico ordinário:IRPC correspondente aos lucros,acréscimo53 já incluído e quota proporcional documentados. Sem cálculo automático da inclusão ou da exclusão40;liquidação64(3),associações64(4),benefícios e regras especiais separados. Sem reembolso ou transporte económico presumidos.']:[]),...(b.creditCarry?['Saldos anteriores documentados: escolha explícita das parcelas correntes e anteriores, sem prioridade presumida entre exercícios, reembolso ou criação/consumo de saldos oficiais.']:[]),...(b.internationalCredits?['Crédito internacional do próprio exercício:fracção nacional,convenção e montantes MZN são documentados;sem cálculo presumido,transporte ou consumo oficial. '+(b.economicCredits?'Benefícios positivos ficam separados;crédito económico no detalhe próprio.':'Créditos económicos/benefícios positivos ficam separados.')]:[]),b.economicCredits?'L8 parcial:benefícios próprios,derivação automática de quotas,inclusão/correcção económica na base,liquidação/associações,obrigações,autónomos,períodos especiais,moedas eM20/M22 separados. Saldos anteriores só quando detalhados e documentados.':b.creditCarry?'L8 parcial:créditos económicos/benefícios,obrigações,tributação autónoma,períodos especiais,outras moedas e correspondência M20/M22 separados. Prazos dos saldos anteriores demonstrados apenas para origens anuais ordinárias documentadas;sem criação automática de transporte.':b.internationalCredits?'L8 parcial:créditos económicos/benefícios positivos,transporte/caducidade,obrigações,tributação autónoma,períodos especiais e outras moedas separados. PEC sem reembolso ou transporte automático.':(b.pecPayments?'L8 parcial:transporte/caducidade de PEC, cálculo da obrigação, créditos próprios positivos, tributação autónoma, períodos especiais e outras moedas separados. Excedente PEC não é reembolso nem saldo transportado oficialmente.':'L8 parcial:PEC e créditos positivos,tributação autónoma,períodos especiais e pagamentos noutra moeda exigem regras próprias.'),'PPC aqui é pagamento comprovado;não cálculo automático da obrigação ou calendário.','Retenção sofrida não é retenção entregue pela empresa por conta de terceiros.','Valores racionais exactos;camposM20/M22 e representação não presumidos.','Saldo simulado por colecta projectada;sem pagamento,submissão ou reembolso automático.']};
|
||||
}
|
||||
export type AnnualModel=ReturnType<typeof calculateAnnual>;
|
||||
export function recordAnnual(d:Record<string,unknown>,b:AnnualBook,now=new Date().toISOString()):AnnualBook{return{...b,history:b.result?[...b.history,structuredClone(b.result)]:b.history,result:{version:annualSettlementVersion,key:key(d,b),createdAt:now,model:calculateAnnual(d,b)}};}
|
||||
@@ -108,16 +184,39 @@ export function annualReportRows(d:Record<string,unknown>):Array<[string,string,
|
||||
'NUIT: '+m.subjectNuit+'; exercício: '+m.year+'; colecta: '+show(m.assessment?.colecta??null)+'; origem: '+(m.assessment?.kind==='special'?'L7 qualificado':'L6 normal')+'. '+(annualCurrent(d,b)?'Entradas correspondentes.':'Histórico; origem/entradas alteradas: recalcule o saldo corrente.')+' Saldo simulado, não liquidação submetida.',
|
||||
m.input.reference+'; '+m.input.assessmentReference+'; '+m.source],
|
||||
...m.claims.map(c=>[
|
||||
'IRPC — L8',(c.kind==='withholding'?'Retenção sofrida':'PPC')+' — '+(c.document||c.id),
|
||||
'Entidade: '+c.subjectNuit+'; exercício: '+c.year+'; emitente: '+c.issuer+'; data: '+c.date+'; comprovado: '+show(c.proofAmount)+'; parcela: '+(c.reason?'Indeterminado':show(c.value))+'. '+(c.reason||'Reconciliada sem utilização duplicada.'),
|
||||
c.kind==='withholding'?m.input.withholdings.reference+'; '+(m.input.withholdings.rows.find(r=>r.id===c.id)?.reference??''):m.input.ppc.reference+'; '+(m.input.ppc.rows.find(r=>r.id===c.id)?.reference??'')
|
||||
'IRPC — L8',(c.kind==='withholding'?'Retenção sofrida':c.kind==='pec'?'PEC':c.kind==='economic_credit'?'IRPC correspondente aos lucros':c.kind==='foreign_credit'?'Imposto estrangeiro':'PPC')+' — '+(c.document||c.id),
|
||||
'Entidade: '+c.subjectNuit+'; exercício: '+c.year+'; emitente: '+c.issuer+'; data: '+c.date+'; comprovado: '+show(c.proofAmount)+'; parcela: '+(c.reason?'Indeterminado':show(c.value))+(c.parcel?'; parcela documental: '+c.parcel+'; '+(c.parcelReference??''):'')+'. '+(c.reason||'Reconciliada sem utilização duplicada.'),
|
||||
c.kind==='economic_credit'?(m.input.economicCredits?.reference??'')+'; '+(m.input.economicCredits?.rows.find(r=>r.id===c.id)?.attributionReference??''):c.kind==='withholding'?m.input.withholdings.reference+'; '+(m.input.withholdings.rows.find(r=>r.id===c.id)?.reference??''):c.kind==='foreign_credit'?(m.input.internationalCredits?.reference??'')+'; '+(m.input.internationalCredits?.positions.flatMap(p=>p.payments).find(r=>r.id===c.id)?.reference??'')+'; '+(m.input.internationalCredits?.positions.flatMap(p=>p.payments).find(r=>r.id===c.id)?.exerciseReference??''):c.kind==='pec'?(m.input.pecPayments?.reference??'')+'; '+(m.input.pecPayments?.rows.find(r=>r.id===c.id)?.reference??'')+'; utilizações anteriores: '+(m.input.pecPayments?.rows.find(r=>r.id===c.id)?.previouslyUsed??'')+' MZN; '+(m.input.pecPayments?.rows.find(r=>r.id===c.id)?.priorUseReference??''):m.input.ppc.reference+'; '+(m.input.ppc.rows.find(r=>r.id===c.id)?.reference??'')
|
||||
]as[string,string,string,string]),
|
||||
...(m.economicCredit?[
|
||||
...m.economicCredit.rows.map(r=>['IRPC — L8','Crédito económico — '+r.input.distributionDocument,
|
||||
'Lucros incluídos: '+r.input.income+' MZN;IRPC correspondente: '+r.input.taxAmount+' MZN;crédito nominal60%: '+show(r.nominal)+';acréscimo53 incluído: '+r.input.grossUpIncluded+' MZN;quota proporcional: '+show(r.nationalLimit)+';elegível: '+show(r.eligible)+';uso efectivo anterior: '+show(r.previousUse)+';disponível: '+show(r.available)+';estado: '+r.status+'. '+r.pending.join(' | '),
|
||||
r.input.qualificationReference+';40(1): '+r.input.article40Reference+';inclusão: '+r.input.incomeReference+';53: '+r.input.grossUpReference+';quota: '+r.input.nationalLimitReference+';uso: '+r.input.previousUse.reference]as[string,string,string,string]),
|
||||
['IRPC — L8','Crédito económico — antes do internacional','Disponível: '+show(m.economicCredit.available)+';dedução: '+show(m.economicCredit.deducted)+';colecta após crédito económico: '+show(m.economicCredit.afterCredit)+'. Sem reembolso ou criação/consumo de saldo oficial.',m.economicCredit.source+'; '+(m.input.economicCredits?.reference??'')]as[string,string,string,string]
|
||||
]:[]),
|
||||
...(m.internationalCredit?[
|
||||
...m.internationalCredit.positions.map(p=>['IRPC — L8','Crédito internacional — '+p.country,'Imposto pago seleccionado: '+show(p.paid)+'; fracção nacional: '+show(p.nationalLimit)+'; limite CDT: '+(p.input.treaty==='no'?'Não aplicável documentado':show(p.treatyLimit))+'; elegível: '+show(p.eligible)+'; utilização anterior: '+show(p.previousUse)+'; disponível: '+show(p.available)+'; imposto acima dos limites: '+show(p.aboveLimit)+'. '+p.pending.join(' | '),p.input.incomeReference+'; '+p.input.nationalLimitReference+'; '+p.input.treatyReference+'; '+p.input.previousUse.reference]as[string,string,string,string]),
|
||||
['IRPC — L8','Crédito internacional — antes do PEC','Disponível conhecido: '+show(m.internationalCredit.available)+'; dedução: '+show(m.internationalCredit.deducted)+'; colecta após crédito: '+show(m.internationalCredit.afterCredit)+'; elegível não deduzido por insuficiência de colecta: '+show(m.internationalCredit.insufficientCollecta)+(m.creditCarry?'; crédito corrente não escolhido: '+show(m.internationalCredit.notSelected??null):'')+'. Sem reembolso,transporte ou afectação oficial por país. Estado: '+stateLabel(m.input.internationalCredits?.state??'')+'. Créditos económicos/benefícios: '+stateLabel(m.input.internationalCredits?.economic.state??'')+' / '+stateLabel(m.input.internationalCredits?.benefits.state??'')+'.',m.internationalCredit.source+'; '+(m.input.internationalCredits?.reference??'')+'; '+(m.input.internationalCredits?.economic.reference??'')+'; '+(m.input.internationalCredits?.benefits.reference??'')]as[string,string,string,string]
|
||||
]:[]),
|
||||
...(m.creditCarry?[
|
||||
...m.creditCarry.lots.map(l=>['IRPC — L8','Saldo anterior — '+(l.kind==='pec'?'PEC':'Crédito internacional '+l.country)+' — '+l.year,
|
||||
'Remanescente da origem: '+show(l.opening)+'; usos efectivos: '+show(l.used)+'; disponível: '+show(l.available)+'; parcela escolhida: '+show(l.selected)+'; saldo aritmético posterior: '+show(l.remaining)+'; último exercício ordinário: '+(l.lastYear??'Indeterminado')+'; prazo terminado: '+(l.expired?'Sim':'Não')+'. '+l.pending.join(' | '),
|
||||
l.input.originDocument+'; '+l.input.originReference+'; '+l.input.ruleReference+'; '+l.input.usesReference+'; '+l.input.uses.map(u=>u.year+': '+u.amount+' MZN; '+u.document+'; '+u.reference).join(' | ')]as[string,string,string,string]),
|
||||
['IRPC — L8','Afectação escolhida entre exercícios',
|
||||
'Corrente internacional: '+show(m.creditCarry.international)+'; anterior internacional: '+show(m.creditCarry.priorInternational)+'; PEC corrente: '+show(m.creditCarry.pec)+'; PEC anterior: '+show(m.creditCarry.priorPec)+'; colecta após créditos internacionais: '+show(m.creditCarry.afterInternational)+'; colecta após todos os PEC: '+show(m.creditCarry.afterPec)+'. Parcelas só deduzidas se o saldo estiver determinado. Sem criação ou consumo de saldo oficial. '+m.creditCarry.pending.join(' | '),
|
||||
(m.input.creditCarry?.reference??'')+'; '+(m.input.creditCarry?.allocationReference??'')+'; '+m.creditCarry.source]as[string,string,string,string]
|
||||
]:[]),
|
||||
...(m.pecDeduction?[[
|
||||
'IRPC — L8','PEC — dedução limitada à colecta',
|
||||
'Estado: '+stateLabel(m.input.pecPayments?.state??'')+'; parcelas disponíveis: '+show(m.pecDeduction.available)+'; dedução: '+show(m.pecDeduction.deducted)+'; colecta após PEC, antes de retenções/PPC: '+show(m.pecDeduction.afterPec)+'; parcela seleccionada não deduzida: '+show(m.pecDeduction.remaining)+'. Excedente sem reembolso PEC; transporte/caducidade não automáticos. Subtotais não resolvem informação em falta.',
|
||||
(m.input.pecPayments?.reference??'')+'; '+m.pecDeduction.source
|
||||
]as[string,string,string,string]]:[]),
|
||||
['IRPC — L8','Saldo anual simulado',
|
||||
'Estado: '+status+'. Retenções conhecidas: '+show(m.knownWithholdings)+'; PPC conhecidos: '+show(m.knownPpc)+'; saldo aritmético: '+show(m.arithmeticBalance)+'; a pagar: '+show(m.payable)+'; a recuperar: '+show(m.recoverable)+'. '+m.pending.join(' | ')+' '+m.warnings.join(' '),m.source],
|
||||
['IRPC — L8','Estados dos comprovativos',
|
||||
'Retenções sofridas: '+stateLabel(m.input.withholdings.state)+'; PPC: '+stateLabel(m.input.ppc.state)+'. Subtotais conhecidos não substituem informação em falta.',m.input.withholdings.reference+'; '+m.input.ppc.reference],
|
||||
['IRPC — L8','PEC e créditos separados',
|
||||
'PEC: '+stateLabel(m.input.pec.state)+'; montante declarado: '+(m.input.pec.amount||'Não preenchido')+'; créditos: '+stateLabel(m.input.credits.state)+'; montante declarado: '+(m.input.credits.amount||'Não preenchido')+'. Sem dedução, transporte ou reembolso presumidos. '+m.limitations.join(' '),m.input.pec.reference+'; '+m.input.credits.reference]
|
||||
(m.input.pecPayments?'Declaração PEC anterior conservada (substituída nesta execução pelo detalhe comprovado): ':'PEC: ')+stateLabel(m.input.pec.state)+'; montante declarado: '+(m.input.pec.amount||'Não preenchido')+(m.input.internationalCredits?'; resumo anterior de créditos conservado,substituído nesta execução pelo detalhe: ':'; créditos: ')+stateLabel(m.input.credits.state)+'; montante declarado: '+(m.input.credits.amount||'Não preenchido')+'. Sem dedução, transporte ou reembolso presumidos. '+m.limitations.join(' '),m.input.pec.reference+'; '+m.input.credits.reference]
|
||||
];
|
||||
}catch(e){return[['IRPC — L8','Não recuperável',String(e),'Originais e históricos conservados.']];}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
import {countryIsoCodes} from './country-iso-codes.ts';
|
||||
import {creditCompare, creditMoney, creditLimit} from './irpc-international-credit.ts';
|
||||
import {addFractions, fraction, subtractFractions, type FiscalFraction} from './fiscal-rational.ts';
|
||||
|
||||
export const carryVersion = 'IRPC-credit-carry/1';
|
||||
export const carrySource = 'CIRPC, Lei 34/2007, art.65(3): crédito internacional elegível não deduzido por insuficiência de colecta, até ao fim dos cinco exercícios seguintes; art.66(1–2): PEC até três exercícios seguintes, depois dos créditos precedentes, sem reembolso. Exemplar BR52, PDF18/p.768-(72). Recorte: fecho2026, mesmo sujeito, exercícios anuais ordinários, montantes comprovados em MZN. A origem e os usos efectivos são documentados; simulações não criam nem consomem saldos oficiais.';
|
||||
type State = '' | 'known' | 'absent';
|
||||
export type CarryUse = {id: string; year: string; amount: string; document: string; reference: string};
|
||||
export type CarryLot = {
|
||||
id: string; kind: '' | 'international' | 'pec'; country: string; subjectNuit: string; year: string;
|
||||
currency: string; ordinary: '' | 'yes' | 'no'; originDocument: string; originReference: string;
|
||||
ruleReference: string; eligible: string; originDeducted: string; insufficient: '' | 'yes' | 'no';
|
||||
usesState: State; usesReference: string; uses: CarryUse[]; selected: string;
|
||||
};
|
||||
export type CreditCarry = {
|
||||
version: 1; state: State; reference: string; lots: CarryLot[];
|
||||
currentInternational: string; currentPec: string; allocationReference: string; assessmentKey: string;
|
||||
};
|
||||
export const newCreditCarry = (): CreditCarry => ({version: 1, state: '', reference: '', lots: [], currentInternational: '', currentPec: '', allocationReference: '', assessmentKey: ''});
|
||||
export const newCarryLot = (id: string): CarryLot => ({id, kind: '', country: '', subjectNuit: '', year: '', currency: 'MZN', ordinary: '', originDocument: '', originReference: '', ruleReference: '', eligible: '', originDeducted: '', insufficient: '', usesState: '', usesReference: '', uses: [], selected: ''});
|
||||
export const newCarryUse = (id: string): CarryUse => ({id, year: '', amount: '', document: '', reference: ''});
|
||||
const states = ['', 'known', 'absent'];
|
||||
const zero = () => fraction(0n);
|
||||
const canonical = (s: string) => s.normalize('NFKC').trim().toLocaleLowerCase('pt').replace(/\s+/g, ' ');
|
||||
// One consolidated balance per nature/country/origin exercise; changing the document does not create a second quota.
|
||||
export const carryIdentity = (r: CarryLot) => JSON.stringify([r.subjectNuit, r.kind, r.country, r.year]);
|
||||
export function validateCreditCarry(c: CreditCarry) {
|
||||
if (!c || c.version !== 1 || !states.includes(c.state) || ['reference', 'currentInternational', 'currentPec', 'allocationReference', 'assessmentKey'].some(k => typeof (c as unknown as Record<string, unknown>)[k] !== 'string') || !Array.isArray(c.lots)) throw Error('Saldos anteriores não recuperáveis; originais conservados.');
|
||||
const keys = Object.keys(newCarryLot('')).filter(k => k !== 'uses');
|
||||
const ids: string[] = [];
|
||||
for (const r of c.lots) {
|
||||
if (!r || keys.some(k => typeof (r as unknown as Record<string, unknown>)[k] !== 'string') || !['', 'international', 'pec'].includes(r.kind) || !['', 'yes', 'no'].includes(r.ordinary) || !['', 'yes', 'no'].includes(r.insufficient) || !states.includes(r.usesState) || !Array.isArray(r.uses)) throw Error('Origem do saldo anterior não recuperável.');
|
||||
ids.push(r.id);
|
||||
for (const u of r.uses) {
|
||||
if (!u || Object.keys(newCarryUse('')).some(k => typeof (u as unknown as Record<string, unknown>)[k] !== 'string')) throw Error('Utilização anterior não recuperável.');
|
||||
ids.push(u.id);
|
||||
}
|
||||
}
|
||||
if (new Set(ids).size !== ids.length || ids.some(id => !id.trim())) throw Error('Identificador de saldo/utilização ausente ou repetido.');
|
||||
}
|
||||
export function calculateCarryLot(r: CarryLot, subjectNuit: string, year: string) {
|
||||
let opening: FiscalFraction | null = null, used: FiscalFraction | null = null, available: FiscalFraction | null = null, selected: FiscalFraction | null = null, remaining: FiscalFraction | null = null;
|
||||
let lastYear: number | null = null, expired = false;
|
||||
const pending: string[] = [];
|
||||
try {
|
||||
if (!r.kind || r.subjectNuit !== subjectNuit || !/^\d{9}$/.test(r.subjectNuit) || year !== '2026' || !/^20\d{2}$/.test(r.year) || Number(r.year) >= Number(year) || r.ordinary !== 'yes' || r.currency !== 'MZN') throw Error('Origem exige mesmo titular, exercício anterior anual ordinário e montante documentado em MZN.');
|
||||
if (r.kind === 'international' ? !countryIsoCodes.includes(r.country) || r.country === 'MZ' : r.country !== '') throw Error('País estrangeiro obrigatório para crédito internacional; PEC não tem país estrangeiro.');
|
||||
if (!r.originDocument.trim() || !r.originReference.trim() || !r.ruleReference.trim()) throw Error('Faltam apuramento original e regra aplicável ao exercício de origem, com suporte.');
|
||||
if (r.insufficient !== 'yes') throw Error('Falta comprovar que o remanescente resulta de insuficiência de colecta na origem; imposto acima do limite não é saldo transportável.');
|
||||
lastYear = Number(r.year) + (r.kind === 'international' ? 5 : 3);
|
||||
expired = Number(year) > lastYear;
|
||||
const amount = r.kind === 'international' ? creditLimit : creditMoney;
|
||||
const eligible = amount(r.eligible), originDeducted = amount(r.originDeducted);
|
||||
if (creditCompare(originDeducted, eligible) > 0n) throw Error('Dedução na origem ultrapassa o montante elegível comprovado.');
|
||||
opening = subtractFractions(eligible, originDeducted);
|
||||
if (!r.usesState || !r.usesReference.trim() || r.usesState === 'absent' && r.uses.length || r.usesState === 'known' && !r.uses.length) throw Error('Utilizações efectivas posteriores à origem exigem inventário conhecido ou ausência expressa com suporte.');
|
||||
const documents = new Set<string>();
|
||||
const values = r.uses.map(u => {
|
||||
if (!/^20\d{2}$/.test(u.year) || Number(u.year) <= Number(r.year) || Number(u.year) > Number(year) || Number(u.year) > lastYear! || !u.document.trim() || !u.reference.trim()) throw Error('Uso exige exercício elegível, posterior à origem, documento e suporte; pode incluir uso efectivo já ocorrido no exercício corrente.');
|
||||
const identity = canonical(u.document);
|
||||
if (documents.has(identity)) throw Error('Documento de utilização repetido; consolide a dedução efectiva.');
|
||||
documents.add(identity);
|
||||
return amount(u.amount);
|
||||
});
|
||||
used = addFractions(values);
|
||||
if (creditCompare(used, opening) > 0n) throw Error('Utilizações efectivas ultrapassam o remanescente da origem.');
|
||||
const balance = subtractFractions(opening, used);
|
||||
selected = amount(r.selected);
|
||||
available = expired ? zero() : balance;
|
||||
if (creditCompare(selected, available) > 0n) throw Error(expired ? 'Prazo ordinário terminado; saldo conservado no histórico, sem dedução neste exercício.' : 'Parcela seleccionada ultrapassa o saldo documentado disponível.');
|
||||
remaining = subtractFractions(balance, selected);
|
||||
} catch (e) { pending.push(String(e)); selected = null; }
|
||||
return {id: r.id, kind: r.kind, country: r.country, year: r.year, opening, used, available, selected, remaining, lastYear, expired, pending, input: structuredClone(r)};
|
||||
}
|
||||
export function carryPlan(c: CreditCarry, subjectNuit: string, year: string, assessmentKey: string, currentInternational: FiscalFraction, currentPec: FiscalFraction, colecta: FiscalFraction | null, duplicates: string[] = []) {
|
||||
validateCreditCarry(c);
|
||||
const pending = [...duplicates];
|
||||
if (!c.state || !c.reference.trim() || c.state === 'absent' && c.lots.length || c.state === 'known' && !c.lots.length) pending.push('Inventário de saldos anteriores em falta ou contraditório; desconhecido não significa ausente.');
|
||||
const ids = c.lots.map(carryIdentity);
|
||||
if (new Set(ids).size !== ids.length) pending.push('Saldo de mesma natureza/país/exercício repetido; consolide a origem e os usos.');
|
||||
const lots = c.lots.map(r => calculateCarryLot(r, subjectNuit, year));
|
||||
pending.push(...lots.flatMap(r => r.pending.map(p => r.year + '/' + r.kind + ': ' + p)));
|
||||
let international: FiscalFraction | null = null, pec: FiscalFraction | null = null;
|
||||
try {
|
||||
if (c.state !== 'known') return {version: carryVersion, lots, international, pec, priorInternational: zero(), priorPec: zero(), pending, source: carrySource, officialBalancesConsumed: false, carryCreated: false};
|
||||
if (!c.allocationReference.trim() || !c.assessmentKey || c.assessmentKey !== assessmentKey) throw Error('Confirme a escolha das parcelas correntes e anteriores com suporte na colecta seleccionada; não se presume prioridade entre exercícios.');
|
||||
international = creditLimit(c.currentInternational); pec = creditMoney(c.currentPec);
|
||||
if (creditCompare(international, currentInternational) > 0n || creditCompare(pec, currentPec) > 0n) throw Error('Parcela corrente escolhida ultrapassa o respectivo crédito/PEC comprovado disponível.');
|
||||
} catch (e) {pending.push(String(e));}
|
||||
const priorInternational = addFractions(lots.filter(r => r.kind === 'international' && !r.pending.length && r.selected).map(r => r.selected!));
|
||||
const priorPec = addFractions(lots.filter(r => r.kind === 'pec' && !r.pending.length && r.selected).map(r => r.selected!));
|
||||
if (international && pec && colecta && creditCompare(addFractions([international, pec, priorInternational, priorPec]), colecta) > 0n) pending.push('As parcelas escolhidas ultrapassam a colecta; ajuste a afectação entre exercícios antes de confirmar, sem reduzir valores automaticamente.');
|
||||
return {version: carryVersion, lots, international, pec, priorInternational, priorPec, pending, source: carrySource, officialBalancesConsumed: false, carryCreated: false};
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import {addFractions, fraction, subtractFractions, type FiscalFraction} from './fiscal-rational.ts';
|
||||
import {creditCompare, creditMoney, creditLimit, type CreditEvidence, type CreditState} from './irpc-international-credit.ts';
|
||||
import {canonicalParcel} from './fiscal-proof-parcel.ts';
|
||||
|
||||
export const economicVersion = 'IRPC-economic-credit/1';
|
||||
export const economicSource = 'CIRPC64(1–2), Lei34/2007, BR52, PDF18/p768-(72):60% do IRPC correspondente aos lucros distribuídos incluídos na base, limitado à colecta proporcional após acréscimo53(1)a (PDF15/p768-(69)). Distinto de65 e da exclusão40(1), na redacçãoLei20/2009,PDF4/p272-(16). RIRPC21(2/5),PDF6/p121:económico antes do internacional/benefícios/PEC,até à colecta,sem reembolso. Recorte2026,residente geral,ano civil,MZN,lucros ordinários,qualificação e quota proporcional documentadas na colecta corrente. Sem alterar a base por presunção, transporte ou consumo de saldos oficiais.';
|
||||
type Choice = '' | 'yes' | 'no';
|
||||
export type EconomicPosition = {
|
||||
id: string; subjectNuit: string; year: string; assessmentKey: string;
|
||||
kind: '' | 'ordinary_profit' | 'liquidation' | 'association'; payerNuit: string;
|
||||
recipientResident: Choice; payerResident: Choice; payerIrpc: Choice; payerExempt: Choice;
|
||||
article40: Choice; qualificationReference: string; article40Reference: string;
|
||||
distributionDocument: string; distributionParcel: string; distributionDate: string;
|
||||
income: string; incomeIncluded: Choice; incomeReference: string;
|
||||
taxIssuer: string; taxDocument: string; taxParcel: string; parcelReference: string;
|
||||
currency: string; proofAmount: string; taxAmount: string; attributionReference: string;
|
||||
previouslyAttributed: string; priorAttributionReference: string;
|
||||
grossUpIncluded: string; grossUpReference: string; nationalLimit: string; nationalLimitReference: string;
|
||||
previousUse: CreditEvidence;
|
||||
};
|
||||
export type EconomicCredits = {version: 1; state: CreditState; reference: string; rows: EconomicPosition[]};
|
||||
export const newEconomicCredits = (): EconomicCredits => ({version: 1, state: '', reference: '', rows: []});
|
||||
export const newEconomicPosition = (id: string): EconomicPosition => ({id, subjectNuit: '', year: '', assessmentKey: '', kind: '', payerNuit: '', recipientResident: '', payerResident: '', payerIrpc: '', payerExempt: '', article40: '', qualificationReference: '', article40Reference: '', distributionDocument: '', distributionParcel: '', distributionDate: '', income: '', incomeIncluded: '', incomeReference: '', taxIssuer: '', taxDocument: '', taxParcel: '', parcelReference: '', currency: 'MZN', proofAmount: '', taxAmount: '', attributionReference: '', previouslyAttributed: '', priorAttributionReference: '', grossUpIncluded: '', grossUpReference: '', nationalLimit: '', nationalLimitReference: '', previousUse: {state: '', amount: '', reference: ''}});
|
||||
export function validateEconomic(c: EconomicCredits) {
|
||||
if (!c || c.version !== 1 || !['', 'known', 'absent'].includes(c.state) || typeof c.reference !== 'string' || !Array.isArray(c.rows) || new Set(c.rows.map(r => r?.id)).size !== c.rows.length) throw Error('Crédito económico não recuperável; originais conservados.');
|
||||
const keys = Object.keys(newEconomicPosition('')).filter(k => k !== 'previousUse');
|
||||
for (const r of c.rows) if (!r || keys.some(k => typeof (r as unknown as Record<string, unknown>)[k] !== 'string') || !['', 'ordinary_profit', 'liquidation', 'association'].includes(r.kind) || ['recipientResident', 'payerResident', 'payerIrpc', 'payerExempt', 'article40', 'incomeIncluded'].some(k => !['', 'yes', 'no'].includes(r[k as 'payerIrpc'])) || !r.previousUse || !['', 'known', 'absent'].includes(r.previousUse.state) || typeof r.previousUse.amount !== 'string' || typeof r.previousUse.reference !== 'string') throw Error('Posição económica não recuperável; originais conservados.');
|
||||
}
|
||||
const zero = () => fraction(0n);
|
||||
const minimum = (...v: FiscalFraction[]) => v.reduce((a, b) => creditCompare(a, b) < 0n ? a : b);
|
||||
const validDate = (s: string) => /^20\d{2}-\d{2}-\d{2}$/.test(s) && Number.isFinite(Date.parse(s)) && new Date(s).toISOString().slice(0, 10) === s;
|
||||
export const economicDistributionIdentity = (r: EconomicPosition) => JSON.stringify([r.payerNuit, canonicalParcel(r.distributionDocument), canonicalParcel(r.distributionParcel)]);
|
||||
export function economicTaxProof(r: EconomicPosition) {
|
||||
if (!r.taxIssuer.trim() || !r.taxDocument.trim() || !r.taxParcel.trim() || !r.parcelReference.trim() || !r.attributionReference.trim() || !r.priorAttributionReference.trim() || r.currency !== 'MZN') throw Error('IRPC correspondente exige documento, emitente/parcela, suporte da atribuição aos lucros e montante MZN; não é retenção do beneficiário nem imposto estrangeiro.');
|
||||
const proofAmount = creditMoney(r.proofAmount), value = creditMoney(r.taxAmount), previouslyUsed = creditMoney(r.previouslyAttributed);
|
||||
if (creditCompare(addFractions([value, previouslyUsed]), proofAmount) > 0n) throw Error('IRPC atribuído aos lucros mais parcelas já atribuídas ultrapassam o comprovado disponível.');
|
||||
return {proofAmount, value, previouslyUsed};
|
||||
}
|
||||
export function economicGroupPending(c: EconomicCredits) {
|
||||
const pending: string[] = [];
|
||||
if (!c.state || !c.reference.trim()) pending.push('Crédito económico: inventário conhecido/ausente e suporte em falta.');
|
||||
if (c.state === 'absent' && c.rows.length) pending.push('Ausência de crédito económico contradiz lucros registados; conserve e reconcilie.');
|
||||
if (c.state === 'known' && !c.rows.length) pending.push('Crédito económico conhecido sem lucros/IRPC discriminados; zero não é ausência presumida.');
|
||||
if (new Set(c.rows.map(economicDistributionIdentity)).size !== c.rows.length) pending.push('Mesma parcela de lucros distribuídos repetida; consolide a origem, mesmo que o documento de imposto ou a data tenham sido alterados.');
|
||||
return pending;
|
||||
}
|
||||
export function economicPosition(r: EconomicPosition, subjectNuit: string, year: string, assessmentKey: string, colecta: FiscalFraction | null, proofIssues: string[]) {
|
||||
const pending = [...proofIssues];
|
||||
let nominal: FiscalFraction | null = null, nationalLimit: FiscalFraction | null = null, eligible: FiscalFraction | null = null, previousUse: FiscalFraction | null = null, available: FiscalFraction | null = null, included: FiscalFraction | null = null;
|
||||
let status: 'qualified' | 'outside_scope' | 'indeterminate' = 'indeterminate';
|
||||
try {
|
||||
if (!r.id.trim() || !/^\d{9}$/.test(r.payerNuit) || r.subjectNuit !== subjectNuit || year !== '2026' || r.year !== year || r.currency !== 'MZN') throw Error('Titular, entidade distribuidora, exercício2026 e MZN por corresponder.');
|
||||
if (r.kind !== 'ordinary_profit') {status = r.kind ? 'outside_scope' : 'indeterminate'; throw Error('Lucros ordinários64(1–2) por identificar; liquidação64(3) e associações64(4) exigem correspondência específica, sem converter a parcela em crédito internacional.');}
|
||||
if (r.recipientResident !== 'yes' || r.payerResident !== 'yes' || r.payerIrpc !== 'yes' || r.payerExempt !== 'no' || !r.qualificationReference.trim()) throw Error('Exige sede/direcção efectiva em Moçambique de beneficiário e distribuidor, distribuidor sujeito a IRPC e não isento, com suporte.');
|
||||
if (r.article40 !== 'no' || !r.article40Reference.trim()) {status = r.article40 === 'yes' ? 'outside_scope' : 'indeterminate'; throw Error(r.article40 === 'yes' ? 'Exclusão40(1) identificada: tratar na base; não somar crédito64 à mesma parcela excluída.' : 'Falta corresponder a não aplicação de40(1), incluindo a redacção20/2009; desconhecido não significa não aplicável.');}
|
||||
if (!validDate(r.distributionDate) || !r.distributionDate.startsWith(year + '-') || !r.distributionDocument.trim() || !r.distributionParcel.trim()) throw Error('Documento/parcela e data da distribuição do exercício por identificar.');
|
||||
if (!r.assessmentKey || r.assessmentKey !== assessmentKey || r.incomeIncluded !== 'yes' || !r.incomeReference.trim()) throw Error('Confirme a inclusão dos lucros ilíquidos de retenção e a correspondência com a colecta corrente; a origem pode ter mudado.');
|
||||
const tax = economicTaxProof(r), income = creditMoney(r.income);
|
||||
nominal = fraction(BigInt(tax.value.numerator) * 3n, BigInt(tax.value.denominator) * 5n);
|
||||
if (creditCompare(income, zero()) === 0n && creditCompare(nominal, zero()) > 0n) throw Error('Lucros incluídos zero contradizem IRPC correspondente positivo.');
|
||||
if (!r.grossUpReference.trim() || creditCompare(creditLimit(r.grossUpIncluded), nominal) !== 0n) throw Error('Acréscimo53(1)a incluído na base deve corresponder ao crédito nominal60%; reconcilie L6 sem alterar a colecta ou o histórico automaticamente.');
|
||||
included = addFractions([income, nominal]);
|
||||
nationalLimit = creditLimit(r.nationalLimit);
|
||||
if (!r.nationalLimitReference.trim() || !colecta || creditCompare(nationalLimit, colecta) > 0n || creditCompare(included, zero()) === 0n && creditCompare(nationalLimit, zero()) > 0n) throw Error('Quota proporcional da colecta após acréscimo53 exige suporte e correspondência; não pode exceder a colecta nem ser positiva para rendimento zero.');
|
||||
if (!r.previousUse.state || !r.previousUse.reference.trim()) throw Error('Utilização anterior efectiva do crédito em falta; simulação não é utilização fiscal.');
|
||||
if (r.previousUse.state === 'absent') {if (r.previousUse.amount.trim()) throw Error('Ausência de uso anterior contradiz montante'); previousUse = zero();}
|
||||
else previousUse = creditLimit(r.previousUse.amount);
|
||||
eligible = minimum(nominal, nationalLimit);
|
||||
if (creditCompare(previousUse, eligible) > 0n) throw Error('Uso anterior excede o crédito económico elegível.');
|
||||
if (!pending.length) {available = subtractFractions(eligible, previousUse); status = 'qualified';}
|
||||
} catch (e) {pending.push(String(e));}
|
||||
return {id: r.id, nominal, nationalLimit, eligible, previousUse, available, included, status, pending, input: structuredClone(r)};
|
||||
}
|
||||
export function economicDeduction(rows: ReturnType<typeof economicPosition>[], colecta: FiscalFraction | null, ready: boolean) {
|
||||
const available = addFractions(rows.flatMap(r => r.available && !r.pending.length ? [r.available] : []));
|
||||
const deducted = ready && colecta ? minimum(available, colecta) : null;
|
||||
return {version: economicVersion, rows, available, deducted, afterCredit: deducted && colecta ? subtractFractions(colecta, deducted) : null, source: economicSource, officialBalancesConsumed: false, carryCreated: false};
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
import type {AnnualPpc} from './irpc-annual-settlement.ts';
|
||||
import {validateProofParcel, requireProofParcel} from './fiscal-proof-parcel.ts';
|
||||
import {countryIsoCodes} from './country-iso-codes.ts';
|
||||
import {addFractions, subtractFractions, decimalFraction, fraction, type FiscalFraction} from './fiscal-rational.ts';
|
||||
|
||||
export const internationalCreditVersion = 'IRPC-international-credit/1';
|
||||
export const internationalCreditSource = 'CIRPC art. 65(1–2), Lei 34/2007, PDF18/p.768-(72); ordem face ao PEC: art.66 e RIRPC21. Recorte: próprio exercício 2026, residente geral, ano civil, valores comprovados em MZN. Menor entre imposto sobre o rendimento pago no estrangeiro, fracção nacional documentada e limite da convenção quando aplicável; depois utilizações anteriores comprovadas e limite da colecta. Não determina a fracção nacional, a aplicabilidade da convenção ou a conversão cambial por presunção. Sem transporte, reembolso do excedente ou consumo de saldos oficiais.';
|
||||
export type CreditState = '' | 'known' | 'absent';
|
||||
export type CreditEvidence = {state: CreditState; amount: string; reference: string};
|
||||
export type InternationalTaxProof = AnnualPpc & {incomeTax: '' | 'yes' | 'no'; taxReference: string; exerciseReference: string};
|
||||
export type InternationalPosition = {
|
||||
id: string; country: string; subjectNuit: string; year: string; assessmentKey: string; qualificationCountry: string;
|
||||
incomeAmount: string; incomeIncluded: '' | 'yes' | 'no'; incomeReference: string;
|
||||
nationalLimit: string; nationalLimitReference: string;
|
||||
treaty: '' | 'yes' | 'no'; treatyLimit: string; treatyReference: string;
|
||||
previousUse: CreditEvidence; payments: InternationalTaxProof[];
|
||||
};
|
||||
export type InternationalCredits = {
|
||||
version: 1; state: CreditState; reference: string; positions: InternationalPosition[];
|
||||
economic: CreditEvidence; benefits: CreditEvidence;
|
||||
};
|
||||
const emptyEvidence = (): CreditEvidence => ({state: '', amount: '', reference: ''});
|
||||
export const newInternationalCredits = (): InternationalCredits => ({version: 1, state: '', reference: '', positions: [], economic: emptyEvidence(), benefits: emptyEvidence()});
|
||||
export const newInternationalPosition = (id: string): InternationalPosition => ({id, country: '', subjectNuit: '', year: '', assessmentKey: '', qualificationCountry: '', incomeAmount: '', incomeIncluded: '', incomeReference: '', nationalLimit: '', nationalLimitReference: '', treaty: '', treatyLimit: '', treatyReference: '', previousUse: emptyEvidence(), payments: []});
|
||||
const states = ['', 'known', 'absent'];
|
||||
const yesNo = ['', 'yes', 'no'];
|
||||
const evidenceValid = (e: CreditEvidence) => e && states.includes(e.state) && typeof e.amount === 'string' && typeof e.reference === 'string';
|
||||
const proofKeys = ['id','document','issuer','date','subjectNuit','year','currency','proofAmount','amount','effective','reference','incomeTax','taxReference','exerciseReference'];
|
||||
const positionKeys = ['id','country','subjectNuit','year','assessmentKey','qualificationCountry','incomeAmount','incomeIncluded','incomeReference','nationalLimit','nationalLimitReference','treaty','treatyLimit','treatyReference'];
|
||||
export function validateInternational(c: InternationalCredits) {
|
||||
if (!c || c.version !== 1 || !states.includes(c.state) || typeof c.reference !== 'string' || !evidenceValid(c.economic) || !evidenceValid(c.benefits) || !Array.isArray(c.positions) || new Set(c.positions.map(p => p?.id)).size !== c.positions.length) throw Error('Créditos internacionais não recuperáveis; originais conservados.');
|
||||
const proofIds: string[] = [];
|
||||
for (const p of c.positions) {
|
||||
if (!p || positionKeys.some(k => typeof (p as unknown as Record<string, unknown>)[k] !== 'string') || !yesNo.includes(p.incomeIncluded) || !yesNo.includes(p.treaty) || !evidenceValid(p.previousUse) || !Array.isArray(p.payments)) throw Error('Posição internacional não recuperável; originais conservados.');
|
||||
for (const r of p.payments) {
|
||||
validateProofParcel(r);
|
||||
if (!r || proofKeys.some(k => typeof (r as unknown as Record<string, unknown>)[k] !== 'string') || !yesNo.includes(r.effective) || !yesNo.includes(r.incomeTax)) throw Error('Comprovativo internacional não recuperável.');
|
||||
proofIds.push(r.id);
|
||||
}
|
||||
}
|
||||
if (new Set(proofIds).size !== proofIds.length) throw Error('Identificador de comprovativo internacional repetido.');
|
||||
}
|
||||
export const creditMoney = (raw: string) => {
|
||||
if (!/^\d{1,18}(\.\d{1,2})?$/.test(raw)) throw Error('Montante MZN em falta/inválido; zero exige declaração própria.');
|
||||
return decimalFraction(raw);
|
||||
};
|
||||
export const creditCompare = (a: FiscalFraction, b: FiscalFraction) => BigInt(a.numerator) * BigInt(b.denominator) - BigInt(b.numerator) * BigInt(a.denominator);
|
||||
export const creditLimit = (raw: string) => {
|
||||
const rational = /^(\d{1,18})\/(\d{1,18})$/.exec(raw);
|
||||
if (rational) return fraction(BigInt(rational[1]), BigInt(rational[2]));
|
||||
if (!/^\d{1,18}(\.\d{1,6})?$/.test(raw)) throw Error('Limite documentado em falta/inválido; use decimal ou fracção exacta n/d, sem arredondamento presumido.');
|
||||
return decimalFraction(raw);
|
||||
};
|
||||
const zero = () => fraction(0n);
|
||||
const minimum = (...values: FiscalFraction[]) => values.reduce((a, b) => creditCompare(a, b) < 0n ? a : b);
|
||||
const isDate = (s: string) => /^20\d{2}-\d{2}-\d{2}$/.test(s) && Number.isFinite(Date.parse(s)) && new Date(s).toISOString().slice(0, 10) === s;
|
||||
export function internationalProofValue(r: InternationalTaxProof, subjectNuit: string, year: string) {
|
||||
requireProofParcel(r);
|
||||
if (!r.id.trim() || !r.document.trim() || !r.issuer.trim() || !r.reference.trim() || !r.taxReference.trim() || r.incomeTax !== 'yes' || r.effective !== 'yes' || r.currency !== 'MZN' || !isDate(r.date) || r.subjectNuit !== subjectNuit || r.year !== year || year !== '2026') throw Error('Crédito internacional: exige imposto sobre o rendimento efectivamente pago, prova/titular/exercício 2026 e MZN; previsões, outras naturezas e conversões não correspondidas ficam separados.');
|
||||
if (!r.date.startsWith(year + '-') && !r.exerciseReference.trim()) throw Error('Data de pagamento fora do exercício: falta correspondência documental com o rendimento e imposto deste exercício; não é transporte presumido.');
|
||||
const value = creditMoney(r.amount), proofAmount = creditMoney(r.proofAmount);
|
||||
if (creditCompare(value, proofAmount) > 0n) throw Error('Parcela estrangeira ultrapassa o montante comprovado.');
|
||||
return {value, proofAmount};
|
||||
}
|
||||
export function internationalGroupPending(c: InternationalCredits, economicDetailed = false) {
|
||||
const pending: string[] = [];
|
||||
if (!c.state || !c.reference.trim()) pending.push('Crédito internacional: declare pagamentos conhecidos, ausência ou informação em falta, com suporte.');
|
||||
if (c.state === 'absent' && c.positions.length) pending.push('Ausência de crédito internacional contradiz posições presentes; conserve e reconcilie.');
|
||||
if (c.state === 'known' && !c.positions.length) pending.push('Crédito internacional conhecido sem posição/comprovativo; zero não é ausência presumida.');
|
||||
for (const [label, e] of [...(economicDetailed ? [] : [['Dupla tributação económica', c.economic] as const]), ['Benefícios/créditos próprios', c.benefits] as const]) {
|
||||
try {
|
||||
if (!e.state || !e.reference.trim()) throw Error('estado e suporte em falta');
|
||||
if (e.state === 'absent') {if (e.amount.trim()) throw Error('ausência contradiz montante');}
|
||||
else if (creditCompare(creditMoney(e.amount), zero()) !== 0n) throw Error('valor positivo exige regra própria; não é deduzido nem ignorado');
|
||||
} catch (e) {pending.push(label + ': ' + String(e));}
|
||||
}
|
||||
return pending;
|
||||
}
|
||||
export function internationalPosition(p: InternationalPosition, paid: FiscalFraction, proofIssues: string[], subjectNuit: string, year: string, assessmentKey: string) {
|
||||
const pending = [...proofIssues];
|
||||
let nationalLimit: FiscalFraction | null = null, treatyLimit: FiscalFraction | null = null, previousUse: FiscalFraction | null = null, eligible: FiscalFraction | null = null, available: FiscalFraction | null = null, aboveLimit: FiscalFraction | null = null;
|
||||
try {
|
||||
if (!p.id.trim() || !countryIsoCodes.includes(p.country) || p.country === 'MZ' || p.subjectNuit !== subjectNuit || p.year !== year || year !== '2026') throw Error('País estrangeiro do catálogo, titular e exercício não correspondem.');
|
||||
if (p.qualificationCountry !== p.country) throw Error('O país mudou; confirme a inclusão e os limites documentados para o país seleccionado.');
|
||||
if (p.assessmentKey !== assessmentKey || !p.assessmentKey) throw Error('Confirme a inclusão e a fracção nacional na colecta corrente; a origem mudou.');
|
||||
if (p.incomeIncluded !== 'yes' || !p.incomeReference.trim()) throw Error('Falta inclusão documentada do rendimento estrangeiro na matéria colectável.');
|
||||
const income = creditMoney(p.incomeAmount);
|
||||
nationalLimit = creditLimit(p.nationalLimit);
|
||||
if (!p.nationalLimitReference.trim()) throw Error('Fracção nacional exige suporte próprio; não é calculada nem presumida pelo portal.');
|
||||
if (creditCompare(income, zero()) === 0n && creditCompare(nationalLimit, zero()) !== 0n) throw Error('Rendimento incluído zero contradiz fracção nacional positiva.');
|
||||
if (!p.treaty || !p.treatyReference.trim()) throw Error('Aplicabilidade da convenção e suporte em falta.');
|
||||
if (p.treaty === 'yes') treatyLimit = creditLimit(p.treatyLimit);
|
||||
else if (p.treatyLimit.trim()) throw Error('Ausência de convenção contradiz limite preenchido.');
|
||||
if (!p.previousUse.state || !p.previousUse.reference.trim()) throw Error('Utilizações anteriores do crédito em falta; histórico de simulação não é consumo oficial.');
|
||||
if (p.previousUse.state === 'absent') {if (p.previousUse.amount.trim()) throw Error('Ausência de uso anterior contradiz montante');previousUse = zero();}
|
||||
else previousUse = creditMoney(p.previousUse.amount);
|
||||
if (!p.payments.length) throw Error('Sem comprovativos de imposto estrangeiro nesta posição.');
|
||||
if (!pending.length) {
|
||||
eligible = minimum(paid, nationalLimit, ...(treatyLimit ? [treatyLimit] : []));
|
||||
aboveLimit = subtractFractions(paid, eligible);
|
||||
if (creditCompare(previousUse, eligible) > 0n) throw Error('Utilização anterior excede o crédito elegível; reconcilie o saldo.');
|
||||
available = subtractFractions(eligible, previousUse);
|
||||
}
|
||||
} catch (e) {pending.push(String(e));available = null;}
|
||||
return {id: p.id, country: p.country, paid, nationalLimit, treatyLimit, previousUse, eligible, available, aboveLimit, pending, input: structuredClone(p)};
|
||||
}
|
||||
export function internationalDeduction(positions: ReturnType<typeof internationalPosition>[], colecta: FiscalFraction | null, ready: boolean, selected?: FiscalFraction | null) {
|
||||
const available = addFractions(positions.flatMap(p => p.available && !p.pending.length ? [p.available] : []));
|
||||
const deducted = ready && colecta ? selected ?? minimum(available, colecta) : null;
|
||||
return {version: internationalCreditVersion, positions, available, deducted, afterCredit: deducted && colecta ? subtractFractions(colecta, deducted) : null, insufficientCollecta: selected === undefined && deducted ? subtractFractions(available, deducted) : null, ...(selected !== undefined ? {notSelected: deducted ? subtractFractions(available, deducted) : null} : {}), source: internationalCreditSource, countryAllocationCreated: false, carryCreated: false, officialBalancesConsumed: false};
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import {subtractFractions, decimalFraction, type FiscalFraction} from './fiscal-rational.ts';
|
||||
import {validateProofParcel, requireProofParcel, type FiscalProofParcel} from './fiscal-proof-parcel.ts';
|
||||
|
||||
export const pecVersion = 'IRPC-PEC/1';
|
||||
export const pecSource = 'CIRPC, art. 66(1–2), Lei 34/2007, exemplar BR52, p. 768-(72): dedução após créditos anteriores, até à colecta, sem reembolso do PEC. Recorte demonstrado: pagamento efectivo do próprio exercício 2026, residente geral, ano civil, MZN. Não calcula a obrigação PEC, transporte, caducidade ou consumo de saldos oficiais.';
|
||||
export type PecPayment = FiscalProofParcel & {
|
||||
id: string; document: string; issuer: string; date: string; subjectNuit: string;
|
||||
year: string; currency: string; proofAmount: string; amount: string;
|
||||
effective: '' | 'yes' | 'no'; reference: string;
|
||||
previouslyUsed: string; priorUseReference: string;
|
||||
};
|
||||
export type PecPayments = {version: 1; state: '' | 'known' | 'absent'; reference: string; rows: PecPayment[]};
|
||||
export const newPecPayment = (id: string): PecPayment => ({id, document: '', issuer: '', date: '', subjectNuit: '', year: '', currency: 'MZN', proofAmount: '', amount: '', effective: '', reference: '', previouslyUsed: '', priorUseReference: ''});
|
||||
export function validatePec(p: PecPayments) {
|
||||
const keys = Object.keys(newPecPayment(''));
|
||||
if (!p || p.version !== 1 || !['', 'known', 'absent'].includes(p.state) || typeof p.reference !== 'string' || !Array.isArray(p.rows) || new Set(p.rows.map(r => r?.id)).size !== p.rows.length || p.rows.some(r => !r || keys.some(k => typeof (r as unknown as Record<string, unknown>)[k] !== 'string') || !['', 'yes', 'no'].includes(r.effective))) {
|
||||
throw Error('Comprovativos PEC não recuperáveis; originais conservados.');
|
||||
}
|
||||
for (const r of p.rows) validateProofParcel(r);
|
||||
}
|
||||
export const pecCompare = (a: FiscalFraction, b: FiscalFraction) => BigInt(a.numerator) * BigInt(b.denominator) - BigInt(b.numerator) * BigInt(a.denominator);
|
||||
const money = (s: string) => {
|
||||
if (!/^\d{1,18}(\.\d{1,2})?$/.test(s)) throw Error('Montante PEC em falta/inválido; declare zero explicitamente quando conhecido.');
|
||||
return decimalFraction(s);
|
||||
};
|
||||
export function pecPaymentValue(r: PecPayment, subjectNuit: string, year: string) {
|
||||
requireProofParcel(r);
|
||||
if (!r.id.trim() || !r.document.trim() || !r.issuer.trim() || !r.reference.trim() || !r.priorUseReference.trim() || r.effective !== 'yes' || r.currency !== 'MZN' || !/^20\d{2}-\d{2}-\d{2}$/.test(r.date) || !Number.isFinite(Date.parse(r.date)) || new Date(r.date).toISOString().slice(0, 10) !== r.date || !r.date.startsWith(year + '-') || r.subjectNuit !== subjectNuit || r.year !== year || year !== '2026') {
|
||||
throw Error('PEC: exige prova efectiva, natureza PEC, titular, exercício 2026, data válida e MZN; saldos anteriores e previsões ficam separados.');
|
||||
}
|
||||
const proofAmount = money(r.proofAmount), value = money(r.amount), previouslyUsed = money(r.previouslyUsed);
|
||||
if (pecCompare(previouslyUsed, proofAmount) > 0n || pecCompare(value, subtractFractions(proofAmount, previouslyUsed)) > 0n) throw Error('PEC: parcela e utilizações anteriores excedem o comprovativo disponível.');
|
||||
return {proofAmount, value, previouslyUsed};
|
||||
}
|
||||
export function pecDeduction(available: FiscalFraction, colecta: FiscalFraction | null, ready: boolean, selected?: FiscalFraction | null) {
|
||||
const deducted = ready && colecta ? selected ?? (pecCompare(available, colecta) > 0n ? colecta : available) : null;
|
||||
return {version: pecVersion, available, deducted, remaining: deducted ? subtractFractions(available, deducted) : null, afterPec: deducted && colecta ? subtractFractions(colecta, deducted) : null, source: pecSource, officialBalancesConsumed: false, carryCreated: false};
|
||||
}
|
||||
@@ -71,7 +71,6 @@ type Page<T> = {
|
||||
};
|
||||
type Props = {
|
||||
sectorEntry?: SectorDetailData['sectorEntry'];
|
||||
sectorDetails?: SectorDetailData[];
|
||||
countries?: Country[];
|
||||
section: string;
|
||||
filters?: Record<string, string | string[]>;
|
||||
@@ -518,41 +517,9 @@ export default function Services(p: Props) {
|
||||
description: sector.short_description,
|
||||
opportunitiesCount: sector.opportunities_count,
|
||||
specializations: sector.specializations,
|
||||
url: p.sectorDetails?.length
|
||||
? `#sector-${sector.slug}`
|
||||
: sector.url,
|
||||
url: sector.url,
|
||||
}))}
|
||||
/>
|
||||
{p.sectorDetails?.map((detail) => (
|
||||
<section
|
||||
id={`sector-${detail.sector.slug}`}
|
||||
key={detail.sector.id}
|
||||
aria-labelledby={`sector-heading-${detail.sector.slug}`}
|
||||
className="scroll-mt-24 space-y-5 border-t border-slate-200 pt-6"
|
||||
>
|
||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||
<h2
|
||||
id={`sector-heading-${detail.sector.slug}`}
|
||||
className="text-2xl font-bold"
|
||||
>
|
||||
{detail.sector.name}
|
||||
</h2>
|
||||
<Link
|
||||
href={`/sectores/${detail.sector.slug}`}
|
||||
className="inline-flex min-h-11 items-center text-sm font-semibold underline"
|
||||
>
|
||||
Abrir página do sector
|
||||
</Link>
|
||||
</div>
|
||||
<SectorContent {...detail} headingLevel={3} />
|
||||
<a
|
||||
href="#sectores-indice"
|
||||
className="inline-flex min-h-11 items-center text-sm font-semibold underline"
|
||||
>
|
||||
Voltar à lista de sectores
|
||||
</a>
|
||||
</section>
|
||||
))}
|
||||
{!p.sectors?.length && (
|
||||
<p>Não existem sectores publicados.</p>
|
||||
)}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
<?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 IrpcCreditCarryDraftTest extends TestCase
|
||||
{
|
||||
use DatabaseTransactions;
|
||||
|
||||
public function test_prior_credit_details_round_trip_with_frozen_history_and_preserved_records(): 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);
|
||||
$snapshot = static fn (string $table): array => collect(DB::select('select id::text as id,md5(row_to_json(t)::text) as hash from public."'.$table.'" t order by id'))->pluck('hash', 'id')->all();
|
||||
$protected = [];
|
||||
foreach (['opportunities', 'documents', 'opportunity_documents', 'simulation_form_drafts', 'users', 'organizations', 'audit_events'] as $table) {
|
||||
$protected[$table] = $snapshot($table);
|
||||
}
|
||||
config(['investor-path.enabled' => true, 'suitecrm.enabled' => false]);
|
||||
$this->actingAs(User::findOrFail(1021));
|
||||
$fixture = json_decode(file_get_contents(base_path('tests/frontend/fixtures/irpc-carry-preserved.json')), true, flags: JSON_THROW_ON_ERROR);
|
||||
$this->assertTrue($fixture['synthetic']);
|
||||
$this->assertFalse($fixture['actualBalances']);
|
||||
$id = null;
|
||||
$etag = null;
|
||||
foreach ($fixture['cases'] as $name => $draft) {
|
||||
$personal = ['person_type' => $draft['person_type']];
|
||||
unset($draft['person_type']);
|
||||
$payload = ['personal_snapshot' => $personal, 'economic_snapshot' => $draft];
|
||||
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($draft['modelo_a_snapshot'], $back['modelo_a_snapshot']);
|
||||
$this->assertSame($draft['business_plan_snapshot'], $back['business_plan_snapshot']);
|
||||
$original = json_decode($draft['modelo_a_snapshot'], true, flags: JSON_THROW_ON_ERROR);
|
||||
$recovered = json_decode($back['modelo_a_snapshot'], true, flags: JSON_THROW_ON_ERROR);
|
||||
foreach (['irpcNormal', 'irpcWithholdings', 'books', 'currencyEvents', 'ispcQuarters'] as $map) {
|
||||
$this->assertSame($original[$map], $recovered[$map]);
|
||||
}
|
||||
$book = $recovered['irpcAnnual'][$draft['context_opportunity']];
|
||||
$model = $book['result']['model'];
|
||||
$this->assertSame($original['irpcAnnual'][$draft['context_opportunity']], $book);
|
||||
$this->assertCount(1, $book['history']);
|
||||
$this->assertSame('IRPC-credit-carry/1', $model['creditCarry']['version']);
|
||||
$this->assertFalse($model['creditCarry']['carryCreated']);
|
||||
$this->assertFalse($model['creditCarry']['officialBalancesConsumed']);
|
||||
$this->assertFalse($model['submitted']);
|
||||
if (in_array($name, ['expired', 'missing-use', 'overallocated'], true)) {
|
||||
$this->assertSame('indeterminate', $model['status']);
|
||||
$this->assertNull($model['arithmeticBalance']);
|
||||
$this->assertNull($model['internationalCredit']['deducted']);
|
||||
$this->assertNull($model['pecDeduction']['deducted']);
|
||||
} else {
|
||||
$expected = ['payable' => ['550', '1'], 'fractional' => ['1822', '3'], 'recoverable' => ['-500', '1']][$name];
|
||||
$this->assertEmpty($model['pending']);
|
||||
$this->assertSame(['numerator' => $expected[0], 'denominator' => $expected[1]], $model['arithmeticBalance']);
|
||||
}
|
||||
if (getenv('INVESTMOZ_CARRY_QA_OUTPUT') === '1') {
|
||||
file_put_contents(base_path('outputs/irpc-l8-carry-20261028/recovered-'.$name.'.json'), json_encode($back, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
}
|
||||
$preserved = [];
|
||||
foreach ($protected as $table => $before) {
|
||||
$this->assertSame($before, array_intersect_key($snapshot($table), $before), 'Existing records changed: '.$table);
|
||||
$preserved[$table] = ['existing_rows' => count($before), 'unchanged' => true];
|
||||
}
|
||||
if (getenv('INVESTMOZ_CARRY_QA_OUTPUT') === '1') {
|
||||
file_put_contents(base_path('outputs/irpc-l8-carry-20261028/existing-records-preservation.json'), json_encode(['database' => $target->name, 'tables' => $preserved, 'transaction_rollback' => true], JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?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 IrpcEconomicCreditDraftTest extends TestCase
|
||||
{
|
||||
use DatabaseTransactions;
|
||||
|
||||
public function test_economic_credit_and_compatible_carry_round_trip_without_overwriting_accepted_history(): 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);
|
||||
$snapshot = static fn (string $table): array => collect(DB::select('select id::text as id,md5(row_to_json(t)::text) as hash from public."'.$table.'" t order by id'))->pluck('hash', 'id')->all();
|
||||
$protected = [];
|
||||
foreach (['opportunities', 'documents', 'opportunity_documents', 'simulation_form_drafts', 'users', 'organizations', 'audit_events'] as $table) {
|
||||
$protected[$table] = $snapshot($table);
|
||||
}
|
||||
config(['investor-path.enabled' => true, 'suitecrm.enabled' => false]);
|
||||
$this->actingAs(User::findOrFail(1021));
|
||||
$fixture = json_decode(file_get_contents(base_path('tests/frontend/fixtures/irpc-economic-preserved.json')), true, flags: JSON_THROW_ON_ERROR);
|
||||
$this->assertTrue($fixture['synthetic']);
|
||||
$this->assertFalse($fixture['actualTaxEvidence']);
|
||||
$id = null;
|
||||
$etag = null;
|
||||
foreach ($fixture['cases'] as $name => $draft) {
|
||||
$personal = ['person_type' => $draft['person_type']];
|
||||
unset($draft['person_type']);
|
||||
$payload = ['personal_snapshot' => $personal, 'economic_snapshot' => $draft];
|
||||
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($draft['modelo_a_snapshot'], $back['modelo_a_snapshot']);
|
||||
$this->assertSame($draft['business_plan_snapshot'], $back['business_plan_snapshot']);
|
||||
$original = json_decode($draft['modelo_a_snapshot'], true, flags: JSON_THROW_ON_ERROR);
|
||||
$recovered = json_decode($back['modelo_a_snapshot'], true, flags: JSON_THROW_ON_ERROR);
|
||||
foreach (['irpcNormal', 'irpcWithholdings', 'books', 'currencyEvents', 'ispcQuarters'] as $map) {
|
||||
$this->assertSame($original[$map], $recovered[$map]);
|
||||
}
|
||||
$book = $recovered['irpcAnnual'][$draft['context_opportunity']];
|
||||
$model = $book['result']['model'];
|
||||
$this->assertSame($original['irpcAnnual'][$draft['context_opportunity']], $book);
|
||||
$this->assertCount(1, $book['history']);
|
||||
$this->assertSame('IRPC-economic-credit/1', $model['economicCredit']['version']);
|
||||
$this->assertFalse($model['economicCredit']['carryCreated']);
|
||||
$this->assertFalse($model['economicCredit']['officialBalancesConsumed']);
|
||||
$this->assertFalse($model['submitted']);
|
||||
if (in_array($name, ['duplicate', 'missing-gross-up', 'article40', 'partial-parcel'], true)) {
|
||||
$this->assertSame('indeterminate', $model['status']);
|
||||
$this->assertNull($model['arithmeticBalance']);
|
||||
$this->assertNull($model['economicCredit']['deducted']);
|
||||
$this->assertNull($model['internationalCredit']['deducted']);
|
||||
$this->assertNull($model['pecDeduction']['deducted']);
|
||||
} else {
|
||||
$expected = ['payable' => ['688', '1'], 'combined' => ['538', '1'], 'quota' => ['695', '1'], 'prior-use' => ['691', '1'], 'shared-paper' => ['688', '1'], 'fractional' => ['349997', '500'], 'recoverable' => ['-500', '1']][$name];
|
||||
$this->assertEmpty($model['pending']);
|
||||
$this->assertSame(['numerator' => $expected[0], 'denominator' => $expected[1]], $model['arithmeticBalance']);
|
||||
}
|
||||
if (getenv('INVESTMOZ_ECONOMIC_QA_OUTPUT') === '1') {
|
||||
file_put_contents(base_path('outputs/irpc-l8-economic-20261028/recovered-'.$name.'.json'), json_encode($back, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
}
|
||||
$preserved = [];
|
||||
foreach ($protected as $table => $before) {
|
||||
$this->assertSame($before, array_intersect_key($snapshot($table), $before), 'Existing records changed: '.$table);
|
||||
$preserved[$table] = ['existing_rows' => count($before), 'unchanged' => true];
|
||||
}
|
||||
if (getenv('INVESTMOZ_ECONOMIC_QA_OUTPUT') === '1') {
|
||||
file_put_contents(base_path('outputs/irpc-l8-economic-20261028/existing-records-preservation.json'), json_encode(['database' => $target->name, 'tables' => $preserved, 'transaction_rollback' => true], JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?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 IrpcInternationalCreditDraftTest extends TestCase
|
||||
{
|
||||
use DatabaseTransactions;
|
||||
|
||||
public function test_international_credit_delta_round_trips_with_frozen_history_and_unchanged_existing_records(): 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);
|
||||
$snapshot=static fn(string $table):array=>collect(DB::select('select id::text as id,md5(row_to_json(t)::text) as hash from public."'.$table.'" t order by id'))->pluck('hash','id')->all();
|
||||
$protected=[];
|
||||
foreach(['opportunities','documents','opportunity_documents','simulation_form_drafts','users','organizations','audit_events']as$table)$protected[$table]=$snapshot($table);
|
||||
config(['investor-path.enabled'=>true,'suitecrm.enabled'=>false]);
|
||||
$this->actingAs(User::findOrFail(1021)); // Existing synthetic QA actor, no new grants.
|
||||
$base=json_decode(file_get_contents(base_path('tests/frontend/fixtures/irpc-assessment-preserved.json')),true,flags:JSON_THROW_ON_ERROR);
|
||||
$fixture=json_decode(file_get_contents(base_path('tests/frontend/fixtures/irpc-international-preserved.json')),true,flags:JSON_THROW_ON_ERROR);
|
||||
$this->assertTrue($fixture['synthetic']);$this->assertFalse($fixture['actualConventionData']);
|
||||
$personal=['person_type'=>$base['person_type']];unset($base['person_type']);
|
||||
$id=null;$etag=null;$previous=null;
|
||||
foreach($fixture['cases']as$name=>$book){
|
||||
$draft=$base;$s=json_decode($draft['modelo_a_snapshot'],true,flags:JSON_THROW_ON_ERROR);
|
||||
if($previous!==null)$book['history']=[$previous];
|
||||
$s['irpcAnnual'][$draft['context_opportunity']]=$book;
|
||||
$draft['modelo_a_snapshot']=json_encode($s,JSON_THROW_ON_ERROR|JSON_UNESCAPED_UNICODE);
|
||||
$payload=['personal_snapshot'=>$personal,'economic_snapshot'=>$draft];
|
||||
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($draft['modelo_a_snapshot'],$back['modelo_a_snapshot']);$this->assertSame($draft['business_plan_snapshot'],$back['business_plan_snapshot']);
|
||||
$recovered=json_decode($back['modelo_a_snapshot'],true,flags:JSON_THROW_ON_ERROR);
|
||||
foreach(['irpcNormal','irpcWithholdings','books','currencyEvents','ispcQuarters']as$map)$this->assertSame($s[$map],$recovered[$map]);
|
||||
$saved=$recovered['irpcAnnual'][$draft['context_opportunity']];$m=$saved['result']['model'];$credit=$m['internationalCredit'];
|
||||
$this->assertSame($book,$saved);$this->assertSame('IRPC-international-credit/1',$credit['version']);$this->assertFalse($credit['carryCreated']);$this->assertFalse($credit['officialBalancesConsumed']);$this->assertFalse($credit['countryAllocationCreated']);$this->assertFalse($m['submitted']);
|
||||
$this->assertSame(['numerator'=>'1408','denominator'=>'1'],$m['assessment']['colecta']);
|
||||
if(in_array($name,['missing','duplicate'],true)){
|
||||
$this->assertSame('indeterminate',$m['status']);$this->assertNull($m['arithmeticBalance']);$this->assertNull($credit['deducted']);$this->assertNull($m['pecDeduction']['deducted']);$this->assertNotEmpty($m['pending']);
|
||||
}else{
|
||||
$expected=['payable'=>['payable','700','8','1400','200'],'treaty'=>['payable','702','6','1402','200'],'limited-collecta'=>['recoverable','-500','1408','0','0'],'absent'=>['payable','708','0','1408','200'],'prior-use'=>['payable','703','5','1403','200']][$name];
|
||||
$this->assertSame($expected[0],$m['status']);$this->assertEmpty($m['pending']);
|
||||
$this->assertSame(['numerator'=>$expected[1],'denominator'=>'1'],$m['arithmeticBalance']);
|
||||
$this->assertSame(['numerator'=>$expected[2],'denominator'=>'1'],$credit['deducted']);
|
||||
$this->assertSame(['numerator'=>$expected[3],'denominator'=>'1'],$credit['afterCredit']);
|
||||
$this->assertSame(['numerator'=>$expected[4],'denominator'=>'1'],$m['pecDeduction']['deducted']);
|
||||
if($name==='limited-collecta'){$this->assertSame(['numerator'=>'592','denominator'=>'1'],$credit['insufficientCollecta']);$this->assertSame(['numerator'=>'200','denominator'=>'1'],$m['pecDeduction']['remaining']);}
|
||||
}
|
||||
if($previous!==null)$this->assertSame($previous,$saved['history'][0]);$previous=$saved['result'];
|
||||
if(getenv('INVESTMOZ_INTERNATIONAL_QA_OUTPUT')==='1')file_put_contents(base_path('outputs/irpc-l8-international-20261027/recovered-'.$name.'.json'),json_encode($back,JSON_THROW_ON_ERROR|JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
$preserved=[];
|
||||
foreach($protected as$table=>$before){$this->assertSame($before,array_intersect_key($snapshot($table),$before),'Existing records changed: '.$table);$preserved[$table]=['existing_rows'=>count($before),'unchanged'=>true];}
|
||||
if(getenv('INVESTMOZ_INTERNATIONAL_QA_OUTPUT')==='1')file_put_contents(base_path('outputs/irpc-l8-international-20261027/existing-records-preservation.json'),json_encode(['database'=>$target->name,'tables'=>$preserved,'transaction_rollback'=>true],JSON_THROW_ON_ERROR|JSON_PRETTY_PRINT));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?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 IrpcPecDraftTest extends TestCase
|
||||
{
|
||||
use DatabaseTransactions;
|
||||
|
||||
public function test_pec_delta_round_trips_without_consuming_balances_or_changing_existing_records(): 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);
|
||||
$snapshot=static function(string $table): array {
|
||||
return collect(DB::select('select id::text as id,md5(row_to_json(t)::text) as hash from public."'.$table.'" t order by id'))->pluck('hash','id')->all();
|
||||
};
|
||||
$protected=[];
|
||||
foreach(['opportunities','documents','opportunity_documents','simulation_form_drafts','users','organizations','audit_events']as$table)$protected[$table]=$snapshot($table);
|
||||
config(['investor-path.enabled'=>true,'suitecrm.enabled'=>false]);
|
||||
$this->actingAs(User::findOrFail(1021)); // Existing synthetic QA operator; no new grants.
|
||||
$base=json_decode(file_get_contents(base_path('tests/frontend/fixtures/irpc-assessment-preserved.json')),true,flags:JSON_THROW_ON_ERROR);
|
||||
$fixture=json_decode(file_get_contents(base_path('tests/frontend/fixtures/irpc-pec-preserved.json')),true,flags:JSON_THROW_ON_ERROR);
|
||||
$personal=['person_type'=>$base['person_type']];unset($base['person_type']);
|
||||
$id=null;$etag=null;$previous=null;
|
||||
foreach($fixture['cases']as$name=>$book){
|
||||
$draft=$base;$store=json_decode($draft['modelo_a_snapshot'],true,flags:JSON_THROW_ON_ERROR);
|
||||
if($previous!==null)$book['history']=[$previous];
|
||||
$store['irpcAnnual'][$draft['context_opportunity']]=$book;
|
||||
$draft['modelo_a_snapshot']=json_encode($store,JSON_THROW_ON_ERROR|JSON_UNESCAPED_UNICODE);
|
||||
$payload=['personal_snapshot'=>$personal,'economic_snapshot'=>$draft];
|
||||
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($draft['modelo_a_snapshot'],$back['modelo_a_snapshot']);
|
||||
$this->assertSame($draft['business_plan_snapshot'],$back['business_plan_snapshot']);
|
||||
$recovered=json_decode($back['modelo_a_snapshot'],true,flags:JSON_THROW_ON_ERROR);
|
||||
foreach(['irpcNormal','irpcWithholdings','books','currencyEvents','ispcQuarters']as$map)$this->assertSame($store[$map],$recovered[$map]);
|
||||
$saved=$recovered['irpcAnnual'][$draft['context_opportunity']];$m=$saved['result']['model'];
|
||||
$this->assertSame($book,$saved);$this->assertSame('IRPC-PEC/1',$m['pecDeduction']['version']);
|
||||
$this->assertFalse($m['officialBalancesConsumed']);$this->assertFalse($m['pecDeduction']['carryCreated']);
|
||||
$this->assertSame(['numerator'=>'1408','denominator'=>'1'],$m['assessment']['colecta']);
|
||||
if($name==='missing'){$this->assertSame('indeterminate',$m['status']);$this->assertNull($m['arithmeticBalance']);$this->assertNull($m['pecDeduction']['deducted']);}
|
||||
else{
|
||||
$expected=['payable'=>['payable','708','200','0'],'capped'=>['balanced','0','1408','592'],'recoverable'=>['recoverable','-500','1408','592'],'prior-use'=>['payable','1208','200','0']][$name];
|
||||
$this->assertSame($expected[0],$m['status']);
|
||||
foreach(['arithmeticBalance'=>1,'deducted'=>2,'remaining'=>3]as$field=>$index)$this->assertSame(['numerator'=>$expected[$index],'denominator'=>'1'],$field==='arithmeticBalance'?$m[$field]:$m['pecDeduction'][$field]);
|
||||
}
|
||||
if($previous!==null)$this->assertSame($previous,$saved['history'][0]);
|
||||
$previous=$saved['result'];
|
||||
if(getenv('INVESTMOZ_PEC_QA_OUTPUT')==='1')file_put_contents(base_path('outputs/irpc-l8-pec-20261027/recovered-'.$name.'.json'),json_encode($back,JSON_THROW_ON_ERROR|JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
$preserved=[];
|
||||
foreach($protected as$table=>$before){
|
||||
$after=$snapshot($table);
|
||||
$this->assertSame($before,array_intersect_key($after,$before),'Existing records changed: '.$table);
|
||||
$preserved[$table]=['existing_rows'=>count($before),'unchanged'=>true];
|
||||
}
|
||||
if(getenv('INVESTMOZ_PEC_QA_OUTPUT')==='1')file_put_contents(base_path('outputs/irpc-l8-pec-20261027/existing-records-preservation.json'),json_encode(['database'=>$target->name,'tables'=>$preserved,'transaction_rollback'=>true],JSON_THROW_ON_ERROR|JSON_PRETTY_PRINT));
|
||||
}
|
||||
}
|
||||
@@ -47,17 +47,22 @@ class SectorCatalogueContentTest extends TestCase
|
||||
$before = [DB::table('desk_cases')->count(), DB::table('opportunities')->count(), DB::table('portal_resources')->count()];
|
||||
|
||||
$props = $this->get('/sectores')->assertOk()->inertiaProps();
|
||||
$this->assertSame(array_column($props['sectors'], 'id'), array_column(array_column($props['sectorDetails'], 'sector'), 'id'));
|
||||
foreach ($props['sectorDetails'] as $detail) {
|
||||
$single = $this->get('/sectores/'.$detail['sector']['slug'])->assertOk()->inertiaProps();
|
||||
$this->assertSame($single['sector'], $detail['sector']);
|
||||
$this->assertSame($single['sectorEntry'], $detail['sectorEntry']);
|
||||
$this->assertSame($single['opportunities'], $detail['opportunities']);
|
||||
$this->assertIsArray($detail['sectorEntry']['articles']);
|
||||
$this->assertIsArray($detail['sectorEntry']['relations']);
|
||||
$this->assertIsArray($detail['sectorEntry']['locations']);
|
||||
$this->assertArrayNotHasKey('sectorDetails', $props);
|
||||
$ids = array_column($props['sectors'], 'id');
|
||||
$this->assertSame($ids, array_values(array_unique($ids)));
|
||||
foreach ($props['sectors'] as $card) {
|
||||
$this->assertSame(\App\Services\SectorOrganization::href($card['slug']), $card['url']);
|
||||
$this->assertStringNotContainsString('#', $card['url']);
|
||||
$single = $this->get($card['url'])->assertOk()->inertiaProps();
|
||||
$this->assertSame('sector', $single['section']);
|
||||
$this->assertSame($card['id'], $single['sector']['id']);
|
||||
$this->assertArrayNotHasKey('sectorDetails', $single);
|
||||
$this->assertArrayNotHasKey('sectors', $single);
|
||||
$this->assertIsArray($single['sectorEntry']['articles']);
|
||||
$this->assertIsArray($single['sectorEntry']['relations']);
|
||||
$this->assertIsArray($single['sectorEntry']['locations']);
|
||||
}
|
||||
$entry = collect($props['sectorDetails'])->firstWhere('sector.id', $withoutGuide->id)['sectorEntry'];
|
||||
$entry = $this->get('/sectores/'.$withoutGuide->slug)->assertOk()->inertiaProps()['sectorEntry'];
|
||||
$this->assertSame([], $entry['articles']);
|
||||
$this->assertSame('/minhas-oportunidades/create?sector_id='.$withoutGuide->id, $entry['register_href']);
|
||||
$this->assertNotNull($entry['start_href']);
|
||||
@@ -80,10 +85,11 @@ class SectorCatalogueContentTest extends TestCase
|
||||
}
|
||||
config(['investor-path.enabled' => false, 'portal.profile_access_enabled' => false]);
|
||||
$props = $this->get('/sectores')->assertOk()->inertiaProps();
|
||||
$ids = array_column(array_column($props['sectorDetails'], 'sector'), 'id');
|
||||
$this->assertArrayNotHasKey('sectorDetails', $props);
|
||||
$ids = array_column($props['sectors'], 'id');
|
||||
$this->assertNotContains($unpublished->id, $ids);
|
||||
$this->assertNotContains($inactive->id, $ids);
|
||||
$detail = collect($props['sectorDetails'])->firstWhere('sector.id', $public->id);
|
||||
$detail = $this->get('/sectores/'.$public->slug)->assertOk()->inertiaProps();
|
||||
$this->assertSame([$slugs['published']], array_column($detail['opportunities'], 'slug'));
|
||||
$this->assertNull($detail['sectorEntry']['start_href']);
|
||||
$this->assertNull($detail['sectorEntry']['register_href']);
|
||||
@@ -92,4 +98,29 @@ class SectorCatalogueContentTest extends TestCase
|
||||
$this->get('/sectores/'.$unpublished->slug)->assertNotFound();
|
||||
$this->get('/sectores/'.$inactive->slug)->assertNotFound();
|
||||
}
|
||||
public function test_selecting_a_sector_keeps_opportunities_scoped_on_reload_and_return(): void
|
||||
{
|
||||
$a = $this->sector();$b = $this->sector();
|
||||
foreach ([$a, $b] as $sector) {
|
||||
$slug = 'qa-isolated-'.Str::lower(Str::random(12));
|
||||
Opportunity::create(['sector_id'=>$sector->id, 'reference'=>$slug, 'title'=>$slug, 'slug'=>$slug,
|
||||
'summary'=>'Synthetic scoped opportunity', 'status'=>'published', 'published_at'=>now()->subDay()]);
|
||||
}
|
||||
$index = $this->get('/sectores')->assertOk()->inertiaProps();
|
||||
foreach ([$a, $b, $a] as $sector) {
|
||||
$url = collect($index['sectors'])->firstWhere('id', $sector->id)['url'];
|
||||
$first = $this->get($url)->assertOk()->inertiaProps();
|
||||
$reload = $this->get($url)->assertOk()->inertiaProps();
|
||||
$this->assertSame($first['sector'], $reload['sector']);
|
||||
$this->assertSame($first['opportunities'], $reload['opportunities']);
|
||||
$this->assertSame($sector->id, $first['sector']['id']);
|
||||
$this->assertSame(Opportunity::where('sector_id',$sector->id)->pluck('slug')->all(), array_column($first['opportunities'],'slug'));
|
||||
$this->assertSame('/investimentos/a-partir-do-setor/'.$sector->slug, $first['sectorEntry']['start_href']);
|
||||
$this->get($first['sectorEntry']['start_href'])->assertRedirect('/login');
|
||||
$returned = $this->get('/sectores')->assertOk()->inertiaProps();
|
||||
$this->assertSame($index['sectors'], $returned['sectors']);
|
||||
$this->assertArrayNotHasKey('sectorDetails', $returned);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"fixture_notice": "Synthetic public projection of accepted API QA consumers;no profiles,real pilots,credentials,dossier or personal data. Test input only;never import into application data.",
|
||||
"consumer_contexts": [
|
||||
{
|
||||
"prefill": {
|
||||
"simulator": {
|
||||
"context_applicability_snapshot": "{\"version\":1,\"consumer_opportunity\":\"opportunity:399\",\"opportunity_choices\":[{\"opportunity\":{\"key\":\"opportunity:399\",\"title\":\"SYNTHETIC QA — Cahora Bassa\",\"version\":2,\"analysis\":{\"proactive\":{\"need\":\"SYNTHETIC QA necessidade por verificar.\",\"basis\":\"Fixture pública sintética; não é evidência de procura local.\",\"group\":\"ideas\",\"stage\":\"Apoio — correspondência real por confirmar\",\"state\":\"hypothesis\",\"notice\":\"A ausência de registo não comprova ausência de oferta. Proximidade não comprova contrato, participação, energia acessível, terra disponível ou benefício fiscal. Necessidade confirmada não comprova viabilidade comercial; hipótese de fornecimento não é encomenda.\",\"version\":1,\"chain_id\":null,\"evidence\":[{\"id\":\"3587d2e4-7428-4cd7-994b-60607b20d7da\",\"scope\":\"Cahora Bassa — contexto piloto\",\"period\":\"Exercício2026 — cenário sintético\",\"source\":\"SYNTHETIC QA exemplar público fictício Cahora Bassa\",\"locator\":\"Fixture QA, linha1\",\"purpose\":\"local\",\"category\":\"infrastructure\",\"coverage\":\"Cobertura fictícia; informação real por confirmar\",\"statement\":\"SYNTHETIC QA: capacidade fictícia em Cahora Bassa; não é diagnóstico económico real.\",\"verified_on\":\"2026-10-27\"}],\"proposal\":\"SYNTHETIC QA solução em preparação.\",\"relations\":[],\"limitations\":\"Sem validação económica, contrato, terra ou energia garantidos.\",\"next_action\":\"Confirmar evidências reais antes de decidir.\",\"evidence_ids\":[\"3587d2e4-7428-4cd7-994b-60607b20d7da\"],\"economic_validation\":false,\"availability_confirmed\":false}}}}]}"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"prefill": {
|
||||
"simulator": {
|
||||
"context_applicability_snapshot": "{\"version\":1,\"consumer_opportunity\":\"opportunity:400\",\"opportunity_choices\":[{\"opportunity\":{\"key\":\"opportunity:400\",\"title\":\"SYNTHETIC QA — Gurué\",\"version\":2,\"analysis\":{\"proactive\":{\"need\":\"SYNTHETIC QA necessidade por verificar.\",\"basis\":\"Fixture pública sintética; não é evidência de procura local.\",\"group\":\"ideas\",\"stage\":\"Apoio — correspondência real por confirmar\",\"state\":\"hypothesis\",\"notice\":\"A ausência de registo não comprova ausência de oferta. Proximidade não comprova contrato, participação, energia acessível, terra disponível ou benefício fiscal. Necessidade confirmada não comprova viabilidade comercial; hipótese de fornecimento não é encomenda.\",\"version\":1,\"chain_id\":null,\"evidence\":[{\"id\":\"1421bb80-78c0-40c9-9de0-f45e18c5a093\",\"scope\":\"Gurué — contexto piloto\",\"period\":\"Exercício2026 — cenário sintético\",\"source\":\"SYNTHETIC QA exemplar público fictício Gurué\",\"locator\":\"Fixture QA, linha1\",\"purpose\":\"local\",\"category\":\"infrastructure\",\"coverage\":\"Cobertura fictícia; informação real por confirmar\",\"statement\":\"SYNTHETIC QA: capacidade fictícia em Gurué; não é diagnóstico económico real.\",\"verified_on\":\"2026-10-27\"}],\"proposal\":\"SYNTHETIC QA solução em preparação.\",\"relations\":[],\"limitations\":\"Sem validação económica, contrato, terra ou energia garantidos.\",\"next_action\":\"Confirmar evidências reais antes de decidir.\",\"evidence_ids\":[\"1421bb80-78c0-40c9-9de0-f45e18c5a093\"],\"economic_validation\":false,\"availability_confirmed\":false}}}}]}"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import {internationalCase} from './irpc-international-case.mjs';
|
||||
import {prepareCreditCarry} from '../../../resources/js/lib/irpc-annual-settlement.ts';
|
||||
import {newCarryLot} from '../../../resources/js/lib/irpc-credit-carry.ts';
|
||||
|
||||
export function carryCase() {
|
||||
const c = internationalCase();
|
||||
c.b = prepareCreditCarry(c.b);
|
||||
Object.assign(c.b.creditCarry, {
|
||||
state: 'known', reference: 'SYNTHETIC QA complete prior balance inventory',
|
||||
currentInternational: '8', currentPec: '200', assessmentKey: c.b.assessmentKey,
|
||||
allocationReference: 'SYNTHETIC QA explicit allocation: current international8, prior international50, currentPEC200, priorPEC100',
|
||||
lots: [
|
||||
{...newCarryLot('international-2021'), kind: 'international', country: 'ZA', subjectNuit: c.b.subjectNuit,
|
||||
year: '2021', ordinary: 'yes', originDocument: 'SYNTHETIC foreign-credit-assessment2021',
|
||||
originReference: 'SYNTHETIC QA origin inclusion, eligible tax, national/CDT limits, insufficient collecta',
|
||||
ruleReference: 'SYNTHETIC QA documented ordinary origin rule art65, no real foreign treaty claim',
|
||||
eligible: '200', originDeducted: '100', insufficient: 'yes', usesState: 'known',
|
||||
usesReference: 'SYNTHETIC QA all actual uses reconciled through current exercise',
|
||||
uses: [{id: 'use-2023', year: '2023', amount: '30', document: 'SYNTHETIC use2023', reference: 'SYNTHETIC QA actual documented use, not simulated'}], selected: '50'},
|
||||
{...newCarryLot('pec-2023'), kind: 'pec', subjectNuit: c.b.subjectNuit,
|
||||
year: '2023', ordinary: 'yes', originDocument: 'SYNTHETIC PEC2023 assessment and receipts',
|
||||
originReference: 'SYNTHETIC QA actual PEC paid, initial deduction and collecta insufficiency',
|
||||
ruleReference: 'SYNTHETIC QA origin ordinary art66', eligible: '300', originDeducted: '150',
|
||||
insufficient: 'yes', usesState: 'absent', usesReference: 'SYNTHETIC QA no actual subsequent use', selected: '100'},
|
||||
],
|
||||
});
|
||||
return c;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import {internationalCase} from './irpc-international-case.mjs';
|
||||
import {carryCase} from './irpc-carry-case.mjs';
|
||||
import {prepareEconomicBook} from '../../../resources/js/lib/irpc-annual-settlement.ts';
|
||||
import {newEconomicPosition} from '../../../resources/js/lib/irpc-economic-credit.ts';
|
||||
|
||||
export function economicCase(withCarry = false) {
|
||||
const c = withCarry ? carryCase() : internationalCase();
|
||||
c.b = prepareEconomicBook(c.b);
|
||||
Object.assign(c.b.economicCredits, {state: 'known', reference: 'SYNTHETIC QA full ordinary economic-credit inventory', rows: [{
|
||||
...newEconomicPosition('economic-1'), subjectNuit: c.b.subjectNuit, year: c.b.year, assessmentKey: c.b.assessmentKey,
|
||||
kind: 'ordinary_profit', payerNuit: '111222333', recipientResident: 'yes', payerResident: 'yes', payerIrpc: 'yes', payerExempt: 'no',
|
||||
article40: 'no', qualificationReference: 'SYNTHETIC QA entities seated/effectively managed in MZ; payer subject nonexempt IRPC',
|
||||
article40Reference: 'SYNTHETIC QA explicit nonapplication of40(1), including2009 participation/period/recipient conditions',
|
||||
distributionDocument: 'SYNTHETIC-DIVIDEND2026', distributionParcel: 'distribution-1', distributionDate: '2026-09-30',
|
||||
income: '100', incomeIncluded: 'yes', incomeReference: 'SYNTHETIC QA100 gross of beneficiary withholding already included in current L6; no actual dividend assertion',
|
||||
taxIssuer: 'SYNTHETIC domestic IRPC issuer', taxDocument: 'SYNTHETIC-SOURCE-IRPC2026', taxParcel: 'underlying-tax-1',
|
||||
parcelReference: 'SYNTHETIC QA underlying domestic IRPC parcel, distinct from recipient withholding or foreign income tax',
|
||||
proofAmount: '20', taxAmount: '20', attributionReference: 'SYNTHETIC QA documented IRPC20 corresponding to included profits100, not inferred by applying a rate to dividends',
|
||||
previouslyAttributed: '0', priorAttributionReference: 'SYNTHETIC QA actual previous attribution to other profits known zero',
|
||||
grossUpIncluded: '12', grossUpReference: 'SYNTHETIC QA53 nominal credit12 already included in reconciled current base4400; no base mutation',
|
||||
nationalLimit: '32', nationalLimitReference: 'SYNTHETIC QA documented proportional current collecta ceiling32 after credit addition; not an automatic fraction derivation',
|
||||
previousUse: {state: 'known', amount: '0', reference: 'SYNTHETIC QA actual prior economic-credit use known zero'},
|
||||
}]});
|
||||
return c;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import {pecCase} from './irpc-pec-case.mjs';
|
||||
import {prepareInternationalBook,newInternationalProof} from '../../../resources/js/lib/irpc-annual-settlement.ts';
|
||||
import {newInternationalPosition} from '../../../resources/js/lib/irpc-international-credit.ts';
|
||||
export function internationalCase(tax='10',national='8',pec='200',ppc='500',rf='0') {
|
||||
const c=pecCase(pec,ppc,rf);c.b=prepareInternationalBook(c.b);
|
||||
const p={...newInternationalPosition('country-za'),country:'ZA',qualificationCountry:'ZA',subjectNuit:c.b.subjectNuit,year:c.b.year,assessmentKey:c.b.assessmentKey,incomeAmount:'100',incomeIncluded:'yes',incomeReference:'SYNTHETIC QA: foreign income included in valid 2026 base',nationalLimit:national,nationalLimitReference:'SYNTHETIC QA: documented national fraction, no automatic derivation',treaty:'no',treatyReference:'SYNTHETIC QA assumption only; not an assertion about actual ZA convention applicability',previousUse:{state:'known',amount:'0',reference:'SYNTHETIC QA: actual previous credit use known zero'},payments:[{...newInternationalProof('foreign-tax'),document:'SYNTHETIC-FOREIGN-TAX-2026',issuer:'SYNTHETIC QA foreign authority',date:'2026-09-30',subjectNuit:c.b.subjectNuit,year:c.b.year,proofAmount:tax,amount:tax,effective:'yes',incomeTax:'yes',reference:'SYNTHETIC QA paid MZN tax amount, no real receipt',taxReference:'SYNTHETIC QA income tax nature and entitlement'}]};
|
||||
Object.assign(c.b.internationalCredits,{state:'known',reference:'SYNTHETIC QA complete inventory for entity/year',positions:[p]});
|
||||
return c;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import {irpcBook,newIrpcRow,saveIrpcBook,recordIrpc} from '../../../resources/js/lib/irpc-withholdings.ts';
|
||||
import fs from 'node:fs';
|
||||
import {newPecPayment} from '../../../resources/js/lib/irpc-pec.ts';
|
||||
import {newAnnualBook,newAnnualPpc,reuseAnnualAssessment,reuseAnnualWithholding} from '../../../resources/js/lib/irpc-annual-settlement.ts';
|
||||
export function pecCase(amount='200',ppc='500',rf='0') {
|
||||
const d=JSON.parse(fs.readFileSync(new URL('../fixtures/irpc-assessment-preserved.json',import.meta.url),'utf8'));
|
||||
if(rf!=='0'){
|
||||
const l5=irpcBook(d);
|
||||
l5.rows.push({...newIrpcRow('pec-rf'),direction:'received',origin:'effective',counterparty:'QA AT',documentRef:'QA rendimento próprio 2026',base:String(Number(rf)*5),currency:'MZN',kind:'royalty',recipientIrpc:'yes',payerIrpc:'yes',residence:'resident',mozambique:'yes',qualificationRef:'QA royalties próprios incluídos no resultado anual',specialRelief:'none',eventsComplete:'yes',events:[{kind:'payment',date:'2026-10-02',reference:'QA rendimento primeiro evento'}],settlement:'complete',settlementRef:'QA retenção sofrida reconciliada',allocations:[{paymentId:'pec-rf-proof',amount:rf}],ordinary:{nonDigital:'yes',capitalType:'',rentOwnPropertyManagement:'',specialFacts:'no',capitalSeatAndManagement:'',reference:'QA royalties não digitais de residente'}});
|
||||
l5.payments.push({id:'pec-rf-proof',kind:'withholding_proof',reference:'QA-RF-PEC-2026',date:'2026-10-02',amount:rf,currency:'MZN'});
|
||||
d.modelo_a_snapshot=saveIrpcBook(d,recordIrpc(d,l5,'2026-10-27T15:00:00+02:00'));
|
||||
}
|
||||
let b=reuseAnnualAssessment(d,newAnnualBook());
|
||||
b.reference='QA PEC: inventário completo da entidade/exercício';b.assessmentReference='QA colecta válida já demonstrada';
|
||||
b.withholdings={state:'absent',reference:'QA sem retenções próprias neste caso',rows:[]};
|
||||
b.ppc={state:ppc==='0'?'absent':'known',reference:'QA inventário de PPC efectivo',rows:ppc==='0'?[]:[{...newAnnualPpc('ppc'),document:'QA-PPC-PEC-2026',issuer:'QA AT',date:'2026-06-30',subjectNuit:b.subjectNuit,year:b.year,proofAmount:ppc,amount:ppc,effective:'yes',reference:'QA prova efectiva de natureza PPC'}]};
|
||||
b.pec={state:'absent',amount:'',reference:'QA resumo anterior preservado'};
|
||||
b.credits={state:'known',amount:'0',reference:'QA créditos precedentes conhecidos zero'};
|
||||
b.pecPayments={version:1,state:'known',reference:'QA inventário de pagamentos PEC 2026',rows:[{...newPecPayment('pec'),document:'QA-PEC-2026',issuer:'QA AT',date:'2026-08-31',subjectNuit:b.subjectNuit,year:b.year,proofAmount:amount,amount,effective:'yes',previouslyUsed:'0',priorUseReference:'QA reconciliação: zero utilizações fora destas parcelas',reference:'QA comprovativo pago de natureza PEC'}]};
|
||||
if(rf!=='0'){
|
||||
b.withholdings.state='known';b=reuseAnnualWithholding(d,b,'pec-rf','pec-rf-proof','rf-claim');
|
||||
Object.assign(b.withholdings.rows[0],{incomeIncluded:'yes',reference:'QA titular e rendimento incluído no exercício'});
|
||||
}
|
||||
return {d,b};
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {carryCase} from './helpers/irpc-carry-case.mjs';
|
||||
import {internationalCase} from './helpers/irpc-international-case.mjs';
|
||||
import {newCarryUse, calculateCarryLot} from '../../resources/js/lib/irpc-credit-carry.ts';
|
||||
import {calculateAnnual, prepareCreditCarry, recordAnnual, annualBook, annualCurrent, saveAnnualBook, annualHtml, annualReportRows} from '../../resources/js/lib/irpc-annual-settlement.ts';
|
||||
import {createProjectDossierReport} from '../../resources/js/lib/project-dossier-report.ts';
|
||||
const number = v => v ? Number(v.numerator) / Number(v.denominator) : null;
|
||||
const calc = c => calculateAnnual(c.d, c.b);
|
||||
const lot = (c, i = 0) => c.b.creditCarry.lots[i];
|
||||
const pending = c => {const m = calc(c); assert.equal(m.status, 'indeterminate', JSON.stringify(m.pending)); assert.equal(m.arithmeticBalance, null); assert.equal(m.internationalCredit.deducted, null); assert.equal(m.pecDeduction.deducted, null); return m;};
|
||||
|
||||
test('2021 international and2023 PEC remain eligible in2026; origin deductions/uses count once:1408-8-50-200-100-500=550', () => {
|
||||
const m = calc(carryCase()); assert.deepEqual(m.pending, []); assert.equal(number(m.payable), 550);
|
||||
assert.equal(number(m.creditCarry.lots[0].opening), 100); assert.equal(number(m.creditCarry.lots[0].used), 30);
|
||||
assert.equal(number(m.creditCarry.lots[0].available), 70); assert.equal(number(m.creditCarry.lots[0].remaining), 20);
|
||||
assert.equal(m.creditCarry.lots[0].lastYear, 2026); assert.equal(m.creditCarry.lots[1].lastYear, 2026);
|
||||
assert.equal(number(m.creditCarry.lots[1].remaining), 50); assert.equal(m.creditCarry.carryCreated, false); assert.equal(m.creditCarry.officialBalancesConsumed, false);
|
||||
});
|
||||
test('five subsequent exercises differs from PEC three; expired source retained with zero selection', () => {
|
||||
for (const [i, year] of [[0, '2020'], [1, '2022']]) {
|
||||
const c = carryCase(); lot(c, i).year = year;
|
||||
if (i === 0) lot(c).uses[0].year = '2023';
|
||||
assert.match(pending(c).pending.join(' '), /Prazo ordinário terminado/);
|
||||
lot(c, i).selected = '0'; const m = calc(c);
|
||||
assert.deepEqual(m.pending, []); assert.equal(m.creditCarry.lots[i].expired, true);
|
||||
assert.equal(number(m.creditCarry.lots[i].available), 0); assert.ok(number(m.creditCarry.lots[i].remaining) > 0);
|
||||
}
|
||||
});
|
||||
test('future/current origin, nonordinary periods, currency and wrong subject do not create an eligible carry', () => {
|
||||
for (const patch of [{year: '2026'}, {year: '2027'}, {year: ''}, {ordinary: ''}, {ordinary: 'no'}, {currency: 'USD'}, {subjectNuit: '999999999'}, {country: 'MZ'}, {country: 'ZZ'}, {kind: ''}]) {
|
||||
const c = carryCase(); Object.assign(lot(c), patch); pending(c);
|
||||
}
|
||||
const c = carryCase(); lot(c, 1).country = 'ZA'; pending(c);
|
||||
});
|
||||
test('origin eligibility, insufficient collecta and applicable rule require concrete evidence', () => {
|
||||
for (const patch of [{originDocument: ''}, {originReference: ''}, {ruleReference: ''}, {insufficient: ''}, {insufficient: 'no'}, {eligible: ''}, {originDeducted: ''}, {originDeducted: '201'}, {selected: ''}, {selected: '70.01'}]) {
|
||||
const c = carryCase(); Object.assign(lot(c), patch); pending(c);
|
||||
}
|
||||
});
|
||||
test('unknown uses are not zero; every actual use must fit its origin/window and documentary identity', () => {
|
||||
for (const patch of [{usesState: ''}, {usesReference: ''}, {usesState: 'absent'}, {uses: []}, {uses: [{...newCarryUse('u'), year: '2021', amount: '1', document: 'D', reference: 'R'}]}]) {
|
||||
const c = carryCase(); Object.assign(lot(c), patch); pending(c);
|
||||
}
|
||||
for (const patch of [{year: '2027'}, {amount: '101'}, {document: ''}, {reference: ''}, {amount: ''}]) {
|
||||
const c = carryCase(); Object.assign(lot(c).uses[0], patch); pending(c);
|
||||
}
|
||||
const c = carryCase(); lot(c).uses.push({...lot(c).uses[0], id: 'copy', year: '2024', document: ' synthetic USE2023 '});
|
||||
assert.match(pending(c).pending.join(' '), /repetido/);
|
||||
});
|
||||
test('actual current-year use is subtracted before the proposed new deduction', () => {
|
||||
const c = carryCase(); lot(c).uses.push({id: 'current-actual', year: '2026', amount: '10', document: 'SYNTHETIC current actual use', reference: 'SYNTHETIC actual assessment, not this simulation'});
|
||||
const m = calc(c); assert.equal(number(m.creditCarry.lots[0].available), 60); assert.equal(number(m.creditCarry.lots[0].remaining), 10); assert.equal(number(m.payable), 550);
|
||||
});
|
||||
test('original country/nature/year cannot be duplicated by changing document or id', () => {
|
||||
const c = carryCase(); c.b.creditCarry.lots.push({...structuredClone(lot(c)), id: 'other', originDocument: 'OTHER', uses: [] , usesState: 'absent'});
|
||||
assert.match(pending(c).pending.join(' '), /mesma natureza/);
|
||||
});
|
||||
test('cross-context duplicate origin blocks and external edits invalidate saved current result', () => {
|
||||
const c = carryCase(); c.d.modelo_a_snapshot = saveAnnualBook(c.d, recordAnnual(c.d, c.b));
|
||||
const b = annualBook(c.d); assert.equal(annualCurrent(c.d, b), true);
|
||||
const s = JSON.parse(c.d.modelo_a_snapshot), other = structuredClone(c.b);
|
||||
other.creditCarry.lots = [{...structuredClone(lot(c)), id: 'copied-origin', originDocument: 'OTHER', uses: [], usesState: 'absent'}];
|
||||
s.irpcAnnual['opportunity:other'] = other; c.d.modelo_a_snapshot = JSON.stringify(s);
|
||||
assert.equal(annualCurrent(c.d, b), false); assert.match(pending(c).pending.join(' '), /noutro mapa/);
|
||||
});
|
||||
test('explicit allocation can favour older balances without automatic reduction or FIFO', () => {
|
||||
const c = carryCase(); Object.assign(c.b.creditCarry, {currentInternational: '0', currentPec: '100'});
|
||||
const m = calc(c); assert.equal(number(m.payable), 658); assert.equal(number(m.internationalCredit.deducted), 0);
|
||||
assert.equal(number(m.internationalCredit.notSelected), 8); assert.equal(m.internationalCredit.insufficientCollecta, null);
|
||||
assert.equal(number(m.pecDeduction.deducted), 100); assert.equal(number(m.pecDeduction.remaining), 100);
|
||||
});
|
||||
test('current selection cannot exceed known availability or total collecta; planned choices are kept on failure', () => {
|
||||
for (const patch of [{currentInternational: '9'}, {currentPec: '201'}, {currentInternational: ''}, {currentPec: ''}, {allocationReference: ''}, {assessmentKey: 'stale'}]) {
|
||||
const c = carryCase(); Object.assign(c.b.creditCarry, patch); pending(c);
|
||||
}
|
||||
const c = carryCase(); lot(c).eligible = '5000'; lot(c).selected = '1400';
|
||||
const before = structuredClone(c.b); assert.match(pending(c).pending.join(' '), /ultrapassam a colecta/); assert.deepEqual(c.b, before);
|
||||
});
|
||||
test('no credit or PEC reimbursement is generated by old balances; PPC refund stays separate', () => {
|
||||
const c = carryCase(); Object.assign(c.b.creditCarry, {currentInternational: '8', currentPec: '0'}); lot(c).eligible = '2000'; lot(c).selected = '1300'; lot(c, 1).selected = '100';
|
||||
const m = calc(c); assert.equal(number(m.recoverable), 500); assert.equal(number(m.pecDeduction.deducted), 0);
|
||||
assert.equal(m.creditCarry.carryCreated, false); assert.equal(number(m.creditCarry.lots[0].remaining), 570);
|
||||
});
|
||||
test('exact foreign-credit fractions are preserved; decimal presentation is never fiscal rounding', () => {
|
||||
const c = carryCase(); Object.assign(lot(c), {eligible: '2/3', originDeducted: '1/3', selected: '1/3', usesState: 'absent', uses: []});
|
||||
Object.assign(c.b.creditCarry, {currentInternational: '1/3'});
|
||||
const m = calc(c); assert.deepEqual(m.pending, []); assert.deepEqual(m.creditCarry.lots[0].available, {numerator: '1', denominator: '3'});
|
||||
assert.deepEqual(m.payable, {numerator: '1822', denominator: '3'});
|
||||
lot(c).selected = '1/0'; pending(c);
|
||||
});
|
||||
test('missing/absent/known-zero remain distinct; absence does not demand unused allocation fields', () => {
|
||||
const c = internationalCase(), baseline = calc(c); c.b = prepareCreditCarry(c.b); pending(c);
|
||||
c.b.creditCarry.state = 'absent'; c.b.creditCarry.reference = 'SYNTHETIC QA documented absence';
|
||||
let m = calc(c); assert.equal(number(m.payable), number(baseline.payable)); assert.deepEqual(m.internationalCredit, baseline.internationalCredit);
|
||||
c.b.creditCarry.lots.push({...carryCase().b.creditCarry.lots[0]}); pending(c);
|
||||
const z = carryCase(); Object.assign(lot(z), {eligible: '0', originDeducted: '0', selected: '0', usesState: 'absent', uses: []});
|
||||
m = calc(z); assert.deepEqual(m.pending, []); assert.equal(number(m.creditCarry.lots[0].available), 0);
|
||||
});
|
||||
test('optional preparation, recording, JSON recovery, HTML and dossier preserve accepted books and frozen history', () => {
|
||||
const c = internationalCase(); c.d.modelo_a_snapshot = saveAnnualBook(c.d, recordAnnual(c.d, c.b, '2026-10-27T17:00:00+02:00'));
|
||||
const old = annualBook(c.d), frozen = structuredClone(old.result), existing = JSON.parse(c.d.modelo_a_snapshot), plan = c.d.business_plan_snapshot;
|
||||
const next = prepareCreditCarry(old); next.creditCarry = carryCase().b.creditCarry;
|
||||
c.d.modelo_a_snapshot = saveAnnualBook(c.d, recordAnnual(c.d, next, '2026-10-28T12:00:00+02:00'));
|
||||
const recovered = annualBook(JSON.parse(JSON.stringify(c.d))); assert.deepEqual(recovered.history, [frozen]); assert.equal(number(recovered.result.model.payable), 550);
|
||||
assert.equal(annualCurrent(c.d, recovered), true); assert.equal(c.d.business_plan_snapshot, plan);
|
||||
const after = JSON.parse(c.d.modelo_a_snapshot); delete after.irpcAnnual; delete existing.irpcAnnual; assert.deepEqual(after, existing);
|
||||
assert.match(annualHtml(c.d), /Saldo anterior — Crédito internacional ZA — 2021/);
|
||||
assert.match(annualHtml(c.d), /550.00/); assert.match(annualHtml(c.d), /2026/);
|
||||
assert.match(JSON.stringify(createProjectDossierReport(c.d, {selectedSections: new Set(['finance'])})), /Afectação escolhida entre exercícios/);
|
||||
recovered.creditCarry.lots[0].selected = '49'; c.d.modelo_a_snapshot = saveAnnualBook(c.d, recovered);
|
||||
assert.equal(annualCurrent(c.d, recovered), false); assert.deepEqual(recovered.result.model, annualBook(c.d).result.model);
|
||||
assert.throws(() => annualHtml(c.d), /fontes actuais/); assert.match(annualReportRows(c.d).flat().join(' '), /Histórico/);
|
||||
});
|
||||
test('malformed detail and duplicate internal ids cannot overwrite original snapshots', () => {
|
||||
for (const mutate of [c => c.b.creditCarry.lots[0].uses = null, c => c.b.creditCarry.version = 2, c => c.b.creditCarry.lots[0].uses[0].amount = null, c => c.b.creditCarry.lots[1].id = c.b.creditCarry.lots[0].id]) {
|
||||
const c = carryCase(), before = c.d.modelo_a_snapshot; mutate(c);
|
||||
assert.throws(() => saveAnnualBook(c.d, c.b), /recuper|repetido/); assert.equal(c.d.modelo_a_snapshot, before);
|
||||
}
|
||||
});
|
||||
test('standalone expired PEC keeps origin and is not silently converted to international five-year window', () => {
|
||||
const c = carryCase(), r = {...lot(c, 1), year: '2022', selected: '0'};
|
||||
const m = calculateCarryLot(r, c.b.subjectNuit, c.b.year); assert.equal(m.expired, true); assert.equal(m.lastYear, 2025); assert.equal(number(m.remaining), 150);
|
||||
});
|
||||
@@ -0,0 +1,141 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {economicCase} from './helpers/irpc-economic-case.mjs';
|
||||
import {internationalCase} from './helpers/irpc-international-case.mjs';
|
||||
import {calculateAnnual, prepareEconomicBook, annualBook, recordAnnual, saveAnnualBook, annualCurrent, annualHtml, annualReportRows} from '../../resources/js/lib/irpc-annual-settlement.ts';
|
||||
import {createProjectDossierReport} from '../../resources/js/lib/project-dossier-report.ts';
|
||||
import {irpcBook, recordIrpc, saveIrpcBook} from '../../resources/js/lib/irpc-withholdings.ts';
|
||||
const number = v => v ? Number(v.numerator) / Number(v.denominator) : null;
|
||||
const row = c => c.b.economicCredits.rows[0];
|
||||
const foreign = c => c.b.internationalCredits.positions[0].payments[0];
|
||||
const calc = c => calculateAnnual(c.d, c.b);
|
||||
const pending = c => {const m = calc(c); assert.equal(m.status, 'indeterminate', JSON.stringify(m.pending)); assert.equal(m.arithmeticBalance, null); assert.equal(m.economicCredit.deducted, null); assert.equal(m.internationalCredit.deducted, null); assert.equal(m.pecDeduction.deducted, null); return m;};
|
||||
|
||||
test('economic60% of documented sourceIRPC20 yields12 before international8,PEC200 andPPC500:688', () => {
|
||||
const m = calc(economicCase()); assert.deepEqual(m.pending, []); assert.equal(number(m.economicCredit.rows[0].nominal), 12);
|
||||
assert.equal(number(m.economicCredit.deducted), 12); assert.equal(number(m.economicCredit.afterCredit), 1396);
|
||||
assert.equal(number(m.internationalCredit.deducted), 8); assert.equal(number(m.internationalCredit.afterCredit), 1388); assert.equal(number(m.payable), 688);
|
||||
assert.equal(m.economicCredit.carryCreated, false); assert.equal(m.economicCredit.officialBalancesConsumed, false); assert.equal(m.submitted, false);
|
||||
});
|
||||
test('documented proportional ceiling and prior actual use limit economic deduction,not a second tax rate on dividends', () => {
|
||||
const c = economicCase(); row(c).nationalLimit = '5'; let m = calc(c); assert.equal(number(m.economicCredit.rows[0].nominal), 12); assert.equal(number(m.payable), 695);
|
||||
row(c).previousUse.amount = '3'; m = calc(c); assert.equal(number(m.economicCredit.deducted), 2); assert.equal(number(m.payable), 698);
|
||||
row(c).previousUse.amount = '6'; pending(c);
|
||||
});
|
||||
test('article40 exclusion, payer exemption/foreign residence and special distributions do not become international credit', () => {
|
||||
for (const patch of [{article40: ''}, {article40: 'yes'}, {article40Reference: ''}, {payerResident: ''}, {payerResident: 'no'}, {recipientResident: 'no'}, {payerIrpc: 'no'}, {payerExempt: ''}, {payerExempt: 'yes'}, {kind: 'liquidation'}, {kind: 'association'}, {kind: ''}, {qualificationReference: ''}]) {
|
||||
const c = economicCase(); Object.assign(row(c), patch); const m = pending(c); assert.equal(m.internationalCredit.positions.length, 1); assert.equal(number(m.internationalCredit.positions[0].eligible), 8);
|
||||
if (patch.article40 === 'yes' || patch.kind === 'liquidation' || patch.kind === 'association') assert.equal(m.economicCredit.rows[0].status, 'outside_scope');
|
||||
}
|
||||
});
|
||||
test('inclusion,credit gross-up and current assessment must be reconciled without silently changing L6', () => {
|
||||
for (const patch of [{incomeIncluded: ''}, {incomeIncluded: 'no'}, {incomeReference: ''}, {assessmentKey: ''}, {assessmentKey: 'stale'}, {grossUpIncluded: ''}, {grossUpIncluded: '0'}, {grossUpIncluded: '11.99'}, {grossUpReference: ''}, {nationalLimit: ''}, {nationalLimit: '1409'}, {nationalLimitReference: ''}, {income: '0'}]) {
|
||||
const c = economicCase(), before = c.d.modelo_a_snapshot; Object.assign(row(c), patch); pending(c); assert.equal(c.d.modelo_a_snapshot, before);
|
||||
}
|
||||
});
|
||||
test('dividend/source tax identity,date,year,subject and actual allocated source amount require support', () => {
|
||||
for (const patch of [{subjectNuit: '999999999'}, {payerNuit: ''}, {year: '2025'}, {distributionDate: '2026-02-30'}, {distributionDate: '2025-12-31'}, {distributionDocument: ''}, {distributionParcel: ''}, {taxIssuer: ''}, {taxDocument: ''}, {taxParcel: ''}, {parcelReference: ''}, {currency: 'USD'}, {proofAmount: ''}, {taxAmount: '21'}, {taxAmount: ''}, {attributionReference: ''}, {previouslyAttributed: ''}, {previouslyAttributed: '1'}, {priorAttributionReference: ''}]) {
|
||||
const c = economicCase(); Object.assign(row(c), patch); pending(c);
|
||||
}
|
||||
});
|
||||
test('same distributed profit parcel cannot be re-used by changing source-tax document,id or date', () => {
|
||||
const c = economicCase(); c.b.economicCredits.rows.push({...structuredClone(row(c)), id: 'other', taxDocument: 'OTHER', distributionDate: '2026-10-01', distributionDocument: ' synthetic-dividend2026 ', distributionParcel: ' DISTRIBUTION-1 '});
|
||||
assert.match(pending(c).pending.join(' '), /Mesma parcela de lucros/);
|
||||
});
|
||||
test('one source-tax parcel can support distinct documented distributions only within its actual available amount', () => {
|
||||
const c = economicCase(), other = {...structuredClone(row(c)), id: 'second', distributionParcel: 'distribution-2'};
|
||||
c.b.economicCredits.rows.push(other); assert.match(pending(c).pending.join(' '), /disponível/);
|
||||
for (const r of c.b.economicCredits.rows) {r.taxAmount = '10'; r.grossUpIncluded = '6';}
|
||||
let m = calc(c); assert.deepEqual(m.pending, []); assert.equal(number(m.economicCredit.deducted), 12);
|
||||
row(c).previouslyAttributed = '1'; other.previouslyAttributed = '1'; pending(c);
|
||||
row(c).taxAmount = '9'; other.taxAmount = '9'; row(c).grossUpIncluded = '5.4'; other.grossUpIncluded = '5.4'; m = calc(c); assert.deepEqual(m.pending, []); assert.equal(number(m.economicCredit.deducted), 10.8);
|
||||
other.previouslyAttributed = '0'; assert.match(pending(c).pending.join(' '), /divergente/);
|
||||
});
|
||||
test('economic and international use of the same actual tax parcel is rejected even with changed receipt date', () => {
|
||||
const c = economicCase(); Object.assign(foreign(c), {issuer: row(c).taxIssuer, document: row(c).taxDocument, parcel: ' UNDERLYING-TAX-1 ', parcelReference: 'SYNTHETIC same parcel', proofAmount: '20', date: '2026-10-01'});
|
||||
assert.match(pending(c).pending.join(' '), /natureza|naturezas/);
|
||||
});
|
||||
test('an old proof with no parcel overlaps the whole document;new names require supported partition on both sides', () => {
|
||||
const c = economicCase(); Object.assign(foreign(c), {issuer: row(c).taxIssuer, document: row(c).taxDocument, proofAmount: '20'}); pending(c);
|
||||
foreign(c).parcel = 'foreign-tax-line'; pending(c); foreign(c).parcelReference = 'SYNTHETIC evidence of a distinct foreign-tax line in shared document';
|
||||
const m = calc(c); assert.deepEqual(m.pending, []); assert.equal(number(m.payable), 688);
|
||||
});
|
||||
test('economic source IRPC is not beneficiary withholding,PPC or PEC;distinct supported parcels may share a document', () => {
|
||||
for (const kind of ['ppc', 'pec']) {
|
||||
const c = economicCase(), proof = kind === 'ppc' ? c.b.ppc.rows[0] : c.b.pecPayments.rows[0];
|
||||
Object.assign(row(c), {taxIssuer: proof.issuer, taxDocument: proof.document, proofAmount: proof.proofAmount}); pending(c);
|
||||
Object.assign(proof, {parcel: 'own-' + kind, parcelReference: 'SYNTHETIC distinct payment parcel'}); assert.deepEqual(calc(c).pending, []);
|
||||
proof.parcel = row(c).taxParcel; pending(c);
|
||||
}
|
||||
});
|
||||
test('underlying domestic IRPC and beneficiary withholding remain separate even when supported by the same paper', () => {
|
||||
const c = internationalCase('10', '8', '200', '500', '300'); c.b = prepareEconomicBook(c.b); c.b.economicCredits = economicCase().b.economicCredits;
|
||||
const withholding = c.b.withholdings.rows[0], proof = calc(c).claims.find(x => x.kind === 'withholding');
|
||||
Object.assign(row(c), {taxIssuer: proof.issuer, taxDocument: proof.document, proofAmount: '300'}); pending(c);
|
||||
Object.assign(withholding, {parcel: 'beneficiary-withholding', parcelReference: 'SYNTHETIC separate recipient-tax parcel'});
|
||||
assert.equal(number(calc(c).payable), 388);
|
||||
const other = structuredClone(c.b), s = JSON.parse(c.d.modelo_a_snapshot);
|
||||
other.economicCredits.rows = []; other.economicCredits.state = 'absent'; other.internationalCredits.positions = []; other.internationalCredits.state = 'absent';
|
||||
other.ppc.rows = []; other.ppc.state = 'absent'; other.pecPayments.rows = []; other.pecPayments.state = 'absent';
|
||||
other.withholdings.rows[0].parcel = 'relabelled-same-L5-allocation';
|
||||
s.irpcAnnual = {'opportunity:other': other}; s.irpcWithholdings['opportunity:other'] = structuredClone(Object.values(s.irpcWithholdings)[0]); c.d.modelo_a_snapshot = JSON.stringify(s);
|
||||
c.d.modelo_a_snapshot = saveIrpcBook(c.d, recordIrpc(c.d, irpcBook(c.d), '2026-10-28T14:00:00+02:00'));
|
||||
assert.match(pending(c).pending.join(' '), /calculada e reconciliada emL5|afectação L5/);
|
||||
});
|
||||
test('documented economic inclusion is not rejected merely because fiscal losses reduce total taxable base below dividends', () => {
|
||||
const c = economicCase(); row(c).income = '5000';
|
||||
const m = calc(c); assert.deepEqual(m.pending, []); assert.equal(number(m.payable), 688);
|
||||
});
|
||||
test('cross-context source capacity and distributed-income duplication cannot be evaded with new ids/documents', () => {
|
||||
const c = economicCase(), other = structuredClone(c.b), s = JSON.parse(c.d.modelo_a_snapshot);
|
||||
other.internationalCredits.positions = []; other.internationalCredits.state = 'absent'; other.ppc.rows = []; other.ppc.state = 'absent'; other.pecPayments.rows = []; other.pecPayments.state = 'absent';
|
||||
other.economicCredits.rows[0].id = 'other-id'; other.economicCredits.rows[0].taxDocument = 'OTHER';
|
||||
s.irpcAnnual = {'opportunity:other': other}; c.d.modelo_a_snapshot = JSON.stringify(s); assert.match(pending(c).pending.join(' '), /lucros económicos já/);
|
||||
other.economicCredits.rows[0].distributionParcel = 'other-distribution'; other.economicCredits.rows[0].taxDocument = row(c).taxDocument; c.d.modelo_a_snapshot = JSON.stringify(s); pending(c);
|
||||
other.economicCredits.rows[0].taxAmount = '10'; row(c).taxAmount = '10'; row(c).grossUpIncluded = '6'; c.d.modelo_a_snapshot = JSON.stringify(s);
|
||||
assert.deepEqual(calc(c).pending, []);
|
||||
});
|
||||
test('economic and accepted carry increments compose:1408-12-8-50-200-100-500=538', () => {
|
||||
const m = calc(economicCase(true)); assert.deepEqual(m.pending, []); assert.equal(number(m.payable), 538);
|
||||
assert.equal(number(m.creditCarry.afterInternational), 1338); assert.equal(number(m.creditCarry.afterPec), 1038);
|
||||
});
|
||||
test('economic priority leaves prior explicit allocations intact when remaining collecta becomes insufficient', () => {
|
||||
const c = economicCase(true); c.b.creditCarry.lots[0].eligible = '2000'; c.b.creditCarry.lots[0].selected = '1300'; c.b.creditCarry.currentPec = '0';
|
||||
const before = structuredClone(c.b.creditCarry); assert.match(pending(c).pending.join(' '), /ultrapassam a colecta/); assert.deepEqual(c.b.creditCarry, before);
|
||||
c.b.creditCarry.lots[0].selected = '1288'; const m = calc(c); assert.deepEqual(m.pending, []); assert.equal(number(m.recoverable), 500); assert.equal(m.economicCredit.carryCreated, false);
|
||||
});
|
||||
test('zero,absence and missing differ; earlier deliberate economic summary remains frozen and does not block detailed calculation', () => {
|
||||
const c = economicCase(); c.b.internationalCredits.economic = {state: 'known', amount: '999', reference: 'SYNTHETIC previous deliberate summary'};
|
||||
assert.equal(number(calc(c).payable), 688); assert.equal(c.b.internationalCredits.economic.amount, '999');
|
||||
c.b.economicCredits.state = 'absent'; pending(c); c.b.economicCredits.rows = []; assert.equal(number(calc(c).payable), 700);
|
||||
c.b.economicCredits.state = ''; pending(c);
|
||||
const z = economicCase(); Object.assign(row(z), {taxAmount: '0', proofAmount: '0', grossUpIncluded: '0', income: '0', nationalLimit: '0'}); assert.equal(number(calc(z).payable), 700);
|
||||
});
|
||||
test('exact60% arithmetic and fractional quota never invent fiscal rounding', () => {
|
||||
const c = economicCase(); Object.assign(row(c), {taxAmount: '0.01', proofAmount: '0.01', grossUpIncluded: '0.006'});
|
||||
const m = calc(c); assert.deepEqual(m.economicCredit.deducted, {numerator: '3', denominator: '500'}); assert.deepEqual(m.payable, {numerator: '349997', denominator: '500'});
|
||||
row(c).nationalLimit = '1/1000'; assert.deepEqual(calc(c).economicCredit.deducted, {numerator: '1', denominator: '1000'});
|
||||
});
|
||||
test('save/recover/history,HTML and dossier preserve current L6,original snapshots and accepted carry results', () => {
|
||||
const c = economicCase(true), prior = structuredClone(c.b); delete prior.economicCredits;
|
||||
c.d.modelo_a_snapshot = saveAnnualBook(c.d, recordAnnual(c.d, prior, '2026-10-28T12:00:00+02:00'));
|
||||
const old = annualBook(c.d), frozen = structuredClone(old.result), base = JSON.parse(c.d.modelo_a_snapshot), plan = c.d.business_plan_snapshot;
|
||||
const next = prepareEconomicBook(old); next.economicCredits = c.b.economicCredits;
|
||||
c.d.modelo_a_snapshot = saveAnnualBook(c.d, recordAnnual(c.d, next, '2026-10-28T14:00:00+02:00'));
|
||||
const back = annualBook(JSON.parse(JSON.stringify(c.d))); assert.equal(annualCurrent(c.d, back), true); assert.deepEqual(back.history, [frozen]); assert.equal(number(back.result.model.payable), 538);
|
||||
const after = JSON.parse(c.d.modelo_a_snapshot); delete after.irpcAnnual; delete base.irpcAnnual; assert.deepEqual(after, base); assert.equal(c.d.business_plan_snapshot, plan);
|
||||
assert.match(annualHtml(c.d), /Crédito económico — antes do internacional/); assert.match(annualHtml(c.d), /538.00/);
|
||||
assert.match(JSON.stringify(createProjectDossierReport(c.d, {selectedSections: new Set(['finance'])})), /Crédito económico/);
|
||||
back.economicCredits.rows[0].nationalLimit = '5'; c.d.modelo_a_snapshot = saveAnnualBook(c.d, back); assert.equal(annualCurrent(c.d, back), false); assert.deepEqual(back.result, annualBook(c.d).result); assert.throws(() => annualHtml(c.d), /fontes actuais/); assert.match(annualReportRows(c.d).flat().join(' '), /Histórico/);
|
||||
});
|
||||
test('malformed optional evidence and parcels are rejected without resetting snapshots', () => {
|
||||
for (const mutate of [c => c.b.economicCredits.version = 2, c => row(c).previousUse = null, c => row(c).article40 = null, c => foreign(c).parcel = 1]) {
|
||||
const c = economicCase(), before = c.d.modelo_a_snapshot; mutate(c); assert.throws(() => saveAnnualBook(c.d, c.b), /recuperável/); assert.equal(c.d.modelo_a_snapshot, before);
|
||||
}
|
||||
const c = internationalCase(); const once = prepareEconomicBook(c.b); assert.equal(prepareEconomicBook(once), once);
|
||||
});
|
||||
test('a partially identified document parcel remains recoverable and indeterminate rather than acquiring absent fields', () => {
|
||||
const c = economicCase(); foreign(c).parcel = 'foreign-tax-line';
|
||||
const model = pending(c); c.d.modelo_a_snapshot = saveAnnualBook(c.d, recordAnnual(c.d, c.b));
|
||||
const back = annualBook(JSON.parse(JSON.stringify(c.d))); assert.deepEqual(calculateAnnual(c.d, back), back.result.model); assert.equal(back.result.model.status, model.status);
|
||||
});
|
||||
@@ -0,0 +1,91 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import crypto from 'node:crypto';
|
||||
import {countryIsoCodes} from '../../resources/js/lib/country-iso-codes.ts';
|
||||
import {internationalCase} from './helpers/irpc-international-case.mjs';
|
||||
import {calculateAnnual,annualBook,recordAnnual,saveAnnualBook,annualCurrent,annualHtml,annualReportRows,prepareInternationalBook} from '../../resources/js/lib/irpc-annual-settlement.ts';
|
||||
import {createProjectDossierReport} from '../../resources/js/lib/project-dossier-report.ts';
|
||||
const value=v=>v?Number(v.numerator)/Number(v.denominator):null;
|
||||
const now='2026-10-27T17:00:00+02:00';
|
||||
const calc=({d,b})=>calculateAnnual(d,b);
|
||||
const pending=c=>{const m=calc(c);assert.equal(m.status,'indeterminate',JSON.stringify(m.pending));assert.equal(m.arithmeticBalance,null);assert.equal(m.internationalCredit.deducted,null);assert.equal(m.pecDeduction.deducted,null);return m;};
|
||||
const position=c=>c.b.internationalCredits.positions[0];
|
||||
const proof=c=>position(c).payments[0];
|
||||
|
||||
test('country code projection exactly reuses the existing master and its source hash',()=>{
|
||||
const raw=Buffer.from(fs.readFileSync(new URL('../../resources/data/iso3166.tab',import.meta.url),'utf8').replace(/\r\n/g,'\n'));
|
||||
const codes=raw.toString('utf8').split(/\r?\n/).filter(l=>l&&!l.startsWith('#')).map(l=>l.split('\t')[0]);
|
||||
assert.deepEqual(countryIsoCodes,codes);const source=fs.readFileSync(new URL('../../resources/js/lib/country-iso-codes.ts',import.meta.url),'utf8');assert.ok(source.includes(crypto.createHash('sha256').update(raw).digest('hex')));
|
||||
});
|
||||
test('10 paid capped at documented national8 precedes PEC200/PPC500:1408-8-200-500=700',()=>{
|
||||
const m=calc(internationalCase());assert.deepEqual(m.pending,[]);const p=m.internationalCredit.positions[0];
|
||||
assert.equal(value(p.paid),10);assert.equal(value(p.eligible),8);assert.equal(value(p.aboveLimit),2);assert.equal(value(m.internationalCredit.deducted),8);assert.equal(value(m.internationalCredit.afterCredit),1400);assert.equal(value(m.pecDeduction.deducted),200);assert.equal(value(m.payable),700);assert.equal(m.internationalCredit.carryCreated,false);assert.equal(m.officialBalancesConsumed,false);
|
||||
});
|
||||
test('documented treaty limit6 caps credit without country-based automatic entitlement',()=>{
|
||||
const c=internationalCase();Object.assign(position(c),{treaty:'yes',treatyLimit:'6',treatyReference:'SYNTHETIC QA convention/article/exercise/limit, not real treaty data'});
|
||||
const m=calc(c);assert.equal(value(m.internationalCredit.deducted),6);assert.equal(value(m.payable),702);assert.equal(value(m.internationalCredit.positions[0].aboveLimit),4);
|
||||
position(c).treatyLimit='';pending(c);position(c).treatyLimit='6';position(c).treatyReference='';pending(c);
|
||||
});
|
||||
test('multiple actual receipts share one country limit;two countries keep separate limits',()=>{
|
||||
const c=internationalCase();proof(c).amount='6';proof(c).proofAmount='6';position(c).payments.push({...proof(c),id:'foreign-second',document:'OTHER-RECEIPT',amount:'4',proofAmount:'4'});assert.equal(value(calc(c).internationalCredit.deducted),8);
|
||||
const other=structuredClone(position(c));other.id='country-pt';other.country='PT';other.qualificationCountry='PT';other.nationalLimit='4';other.payments=[{...other.payments[0],id:'pt-tax',document:'PT-OTHER-RECEIPT',proofAmount:'5',amount:'5'}];c.b.internationalCredits.positions.push(other);assert.equal(value(calc(c).internationalCredit.deducted),12);assert.equal(value(calc(c).payable),696);
|
||||
});
|
||||
test('same country cannot multiply the national fraction across positions or other context maps',()=>{
|
||||
const a=internationalCase(),copy=structuredClone(position(a));copy.id='duplicate-country';copy.payments[0].id='other-proof';copy.payments[0].document='OTHER';a.b.internationalCredits.positions.push(copy);assert.match(pending(a).pending.join(' '),/país\/exercício/);
|
||||
const b=internationalCase(),other=structuredClone(b.b);other.internationalCredits.positions[0].payments[0].id='elsewhere';other.internationalCredits.positions[0].payments[0].document='ELSEWHERE-RECEIPT';const s=JSON.parse(b.d.modelo_a_snapshot);s.irpcAnnual={'opportunity:other':other};b.d.modelo_a_snapshot=JSON.stringify(s);assert.match(pending(b).pending.join(' '),/noutro mapa/);
|
||||
});
|
||||
test('actual previous credit use reduces eligible credit,not gross tax or simulated history',()=>{
|
||||
const c=internationalCase();position(c).previousUse.amount='3';let m=calc(c);assert.equal(value(m.internationalCredit.positions[0].eligible),8);assert.equal(value(m.internationalCredit.positions[0].available),5);assert.equal(value(m.payable),703);
|
||||
position(c).previousUse.amount='9';assert.match(pending(c).pending.join(' '),/excede/);position(c).previousUse={state:'absent',amount:'',reference:'QA no actual previous use'};assert.equal(value(calc(c).payable),700);
|
||||
});
|
||||
test('lack of collecta is separated from tax above eligibility limits and never refunds credit or PEC',()=>{
|
||||
const c=internationalCase('2000','2000','200','0'),m=calc(c);assert.equal(m.status,'balanced');assert.equal(value(m.internationalCredit.deducted),1408);assert.equal(value(m.internationalCredit.insufficientCollecta),592);assert.equal(value(m.internationalCredit.positions[0].aboveLimit),0);assert.equal(value(m.pecDeduction.deducted),0);assert.equal(value(m.pecDeduction.remaining),200);assert.equal(value(m.recoverable),0);assert.equal(m.internationalCredit.countryAllocationCreated,false);
|
||||
c.b.ppc={...internationalCase().b.ppc};const recovered=calc(c);assert.equal(value(recovered.recoverable),500);assert.equal(value(recovered.internationalCredit.insufficientCollecta),592);assert.equal(recovered.internationalCredit.carryCreated,false);
|
||||
});
|
||||
test('known zero,declared absence and missing retain distinct inputs and outcomes',()=>{
|
||||
const zero=internationalCase('0');assert.equal(value(calc(zero).internationalCredit.deducted),0);assert.equal(value(calc(zero).payable),708);
|
||||
const a=internationalCase();a.b.internationalCredits.state='absent';a.b.internationalCredits.positions=[];assert.equal(value(calc(a).payable),708);
|
||||
a.b.internationalCredits.state='';pending(a);a.b.internationalCredits.state='known';pending(a);
|
||||
const b=internationalCase();b.b.internationalCredits.state='absent';pending(b);position(b).previousUse={state:'absent',amount:'0',reference:'QA contradiction'};pending(b);
|
||||
});
|
||||
test('country,entity,year,inclusion,current assessment,domestic fraction and convention require support',()=>{
|
||||
for(const patch of [{country:'MZ'},{country:'ZZ'},{country:''},{country:'PT'},{qualificationCountry:''},{subjectNuit:'999999999'},{year:'2025'},{incomeIncluded:''},{incomeIncluded:'no'},{incomeAmount:''},{incomeAmount:'0'},{incomeReference:''},{nationalLimit:''},{nationalLimitReference:''},{assessmentKey:'stale'},{treaty:''},{treaty:'no',treatyLimit:'0'},{treatyReference:''}]){const c=internationalCase();Object.assign(position(c),patch);pending(c);}
|
||||
for(const patch of [{state:''},{amount:''},{reference:''}]){const c=internationalCase();Object.assign(position(c).previousUse,patch);pending(c);}
|
||||
});
|
||||
test('actual foreign income tax proof is required,not projections,other taxes,wrong exercise or unsupported currency',()=>{
|
||||
for(const patch of [{effective:''},{effective:'no'},{incomeTax:''},{incomeTax:'no'},{taxReference:''},{document:''},{issuer:''},{reference:''},{subjectNuit:'999999999'},{year:'2025'},{date:'2026-02-30'},{date:'2025-09-30'},{currency:'USD'},{amount:'10.01'},{amount:'0.001'}]){const c=internationalCase();Object.assign(proof(c),patch);pending(c);}
|
||||
const c=internationalCase();position(c).payments=[];pending(c);
|
||||
});
|
||||
test('payment date differs from income exercise only with explicit correspondence,never an automatic carry',()=>{
|
||||
const c=internationalCase();proof(c).date='2027-01-15';pending(c);proof(c).exerciseReference='SYNTHETIC QA own-exercise2026 assessment and payment relation';const m=calc(c);assert.equal(value(m.payable),700);assert.equal(m.internationalCredit.carryCreated,false);
|
||||
});
|
||||
test('same physical foreign receipt cannot be duplicated,re-dated or used as PPC/PEC/RF',()=>{
|
||||
const a=internationalCase();position(a).payments.push({...proof(a),id:'other-date',date:'2026-10-01'});pending(a);
|
||||
for(const kind of ['ppc','pec','rf']){const c=internationalCase('10','8','200','500',kind==='rf'?'300':'0');const physical=kind==='ppc'?c.b.ppc.rows[0]:kind==='pec'?c.b.pecPayments.rows[0]:{issuer:'QA AT',document:'QA-RF-PEC-2026',proofAmount:'300'};Object.assign(proof(c),{issuer:physical.issuer,document:physical.document,proofAmount:physical.proofAmount});assert.match(pending(c).pending.join(' '),/naturez|comprovativo/i);}
|
||||
});
|
||||
test('a foreign receipt cannot move country/entity across context maps;external changes invalidate current result',()=>{
|
||||
const c=internationalCase(),other=structuredClone(c.b);other.ppc.rows=[];other.ppc.state='absent';other.pecPayments.rows=[];other.pecPayments.state='absent';other.internationalCredits.positions[0].country='PT';const s=JSON.parse(c.d.modelo_a_snapshot);s.irpcAnnual={'opportunity:other':other};c.d.modelo_a_snapshot=JSON.stringify(s);pending(c);
|
||||
other.internationalCredits.positions[0].payments[0].document='OTHER-PT';c.d.modelo_a_snapshot=JSON.stringify(s);assert.equal(value(calc(c).payable),700);c.d.modelo_a_snapshot=saveAnnualBook(c.d,recordAnnual(c.d,c.b,now));const saved=annualBook(c.d);assert.equal(annualCurrent(c.d,saved),true);
|
||||
const edit=JSON.parse(c.d.modelo_a_snapshot);edit.irpcAnnual['opportunity:other'].internationalCredits.positions[0].country='ZA';c.d.modelo_a_snapshot=JSON.stringify(edit);assert.equal(annualCurrent(c.d,saved),false);pending({...c,b:saved});
|
||||
});
|
||||
test('economic/benefit positive or missing states block only the dependent annual balance,not known eligibility',()=>{
|
||||
for(const kind of ['economic','benefits'])for(const patch of [{state:''},{state:'known',amount:'1'},{reference:''}]){const c=internationalCase();Object.assign(c.b.internationalCredits[kind],patch);const m=pending(c);assert.equal(value(m.internationalCredit.positions[0].eligible),8);assert.equal(value(m.assessment.colecta),1408);}
|
||||
});
|
||||
test('exact decimals and withholding are included once after credit and PEC',()=>{
|
||||
const c=internationalCase('10.25','8.15','200','500','300');position(c).previousUse.amount='3.01';const m=calc(c);assert.deepEqual(m.internationalCredit.deducted,{numerator:'257',denominator:'50'});assert.equal(value(m.payable),402.86);assert.equal(value(m.knownWithholdings),300);
|
||||
});
|
||||
test('documented fractional ceilings retain exact arithmetic without assumed tax rounding',()=>{
|
||||
const c=internationalCase('10','1/3');const m=calc(c);assert.deepEqual(m.internationalCredit.deducted,{numerator:'1',denominator:'3'});assert.deepEqual(m.payable,{numerator:'2123',denominator:'3'});
|
||||
position(c).nationalLimit='1/0';pending(c);
|
||||
});
|
||||
test('prepare preserves deliberate prior summary and history;save/recover/report appends frozen records',()=>{
|
||||
const c=internationalCase(),detail=structuredClone(c.b.internationalCredits);delete c.b.internationalCredits;const original=JSON.parse(c.d.modelo_a_snapshot),plan=c.d.business_plan_snapshot;c.d.modelo_a_snapshot=saveAnnualBook(c.d,recordAnnual(c.d,c.b,now));const prior=structuredClone(annualBook(c.d).result);assert.equal(value(prior.model.payable),708);
|
||||
let next=prepareInternationalBook(annualBook(c.d));next.internationalCredits=detail;c.d.modelo_a_snapshot=saveAnnualBook(c.d,recordAnnual(c.d,next,now));const back=annualBook(JSON.parse(JSON.stringify(c.d)));assert.deepEqual(back.history,[prior]);assert.equal(annualCurrent(c.d,back),true);assert.equal(value(back.result.model.payable),700);assert.deepEqual(back.credits,{state:'known',amount:'0',reference:'QA créditos precedentes conhecidos zero'});
|
||||
const after=JSON.parse(c.d.modelo_a_snapshot);delete after.irpcAnnual;assert.deepEqual(after,original);assert.equal(c.d.business_plan_snapshot,plan);
|
||||
const html=annualHtml(c.d);assert.match(html,/Crédito internacional — antes do PEC/);assert.match(html,/700.00/);assert.match(html,/1400.00/);assert.match(JSON.stringify(createProjectDossierReport(c.d,{selectedSections:new Set(['finance'])})),/Crédito internacional — ZA/);
|
||||
const frozen=structuredClone(back.result);back.internationalCredits.positions[0].nationalLimit='7';c.d.modelo_a_snapshot=saveAnnualBook(c.d,back);assert.equal(annualCurrent(c.d,back),false);assert.deepEqual(back.result,frozen);assert.match(annualReportRows(c.d).flat().join(' '),/Histórico/);assert.throws(()=>annualHtml(c.d),/fontes actuais/);
|
||||
});
|
||||
test('malformed optional international detail never resets existing snapshots',()=>{
|
||||
const c=internationalCase(),snapshot=c.d.modelo_a_snapshot;proof(c).incomeTax=null;assert.throws(()=>saveAnnualBook(c.d,c.b),/não recuperável/);assert.equal(c.d.modelo_a_snapshot,snapshot);
|
||||
});
|
||||
@@ -0,0 +1,67 @@
|
||||
import {pecCase} from './helpers/irpc-pec-case.mjs';
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {annualBook,newAnnualBook,newAnnualPpc,reuseAnnualAssessment,calculateAnnual,recordAnnual,saveAnnualBook,annualCurrent,annualReportRows,annualHtml} from '../../resources/js/lib/irpc-annual-settlement.ts';
|
||||
import {createProjectDossierReport} from '../../resources/js/lib/project-dossier-report.ts';
|
||||
const value=x=>x?Number(x.numerator)/Number(x.denominator):null;
|
||||
const now='2026-10-27T15:00:00+02:00';
|
||||
|
||||
const model=({d,b})=>calculateAnnual(d,b);
|
||||
const indeterminate=c=>{const m=model(c);assert.equal(m.status,'indeterminate',JSON.stringify(m.pending));assert.equal(m.arithmeticBalance,null);assert.equal(m.pecDeduction.deducted,null);assert.equal(m.pecDeduction.remaining,null);return m;};
|
||||
|
||||
test('PEC precedes PPC; 1408 - 200 - 500 = 708, no official consumption',()=>{
|
||||
const m=model(pecCase());assert.deepEqual(m.pending,[]);assert.equal(value(m.pecDeduction.deducted),200);assert.equal(value(m.pecDeduction.afterPec),1208);assert.equal(value(m.payable),708);assert.equal(m.pecDeduction.carryCreated,false);assert.equal(m.officialBalancesConsumed,false);
|
||||
});
|
||||
test('existing received L5 proof is reused after PEC and cannot become another PEC receipt',()=>{
|
||||
const c=pecCase('200','500','300');assert.equal(value(model(c).payable),408);
|
||||
Object.assign(c.b.pecPayments.rows[0],{document:'QA-RF-PEC-2026',proofAmount:'300'});
|
||||
assert.match(indeterminate(c).pending.join(' '),/natureza/);
|
||||
});
|
||||
test('PEC cap never generates refund; PPC can still generate recoverable balance',()=>{
|
||||
const a=model(pecCase('2000','0'));assert.equal(value(a.pecDeduction.deducted),1408);assert.equal(value(a.pecDeduction.remaining),592);assert.equal(value(a.arithmeticBalance),0);assert.equal(value(a.recoverable),0);
|
||||
const b=model(pecCase('2000','500'));assert.equal(value(b.recoverable),500);assert.equal(value(b.pecDeduction.remaining),592);assert.equal(value(b.pecDeduction.afterPec),0);
|
||||
});
|
||||
test('previous use explicit, precise decimals and available portion are respected',()=>{
|
||||
const c=pecCase('200.25','0');Object.assign(c.b.pecPayments.rows[0],{proofAmount:'300.75',previouslyUsed:'100.50'});assert.equal(value(model(c).payable),1207.75);
|
||||
c.b.pecPayments.rows[0].amount='200.26';indeterminate(c);
|
||||
});
|
||||
test('absence, known zero and missing PEC are distinct; contradictory rows block',()=>{
|
||||
const c=pecCase();c.b.pecPayments.rows=[];c.b.pecPayments.state='absent';assert.equal(value(model(c).payable),908);
|
||||
c.b.pecPayments.state='';indeterminate(c);c.b.pecPayments.state='known';indeterminate(c);
|
||||
const z=pecCase('0');assert.equal(value(model(z).payable),908);z.b.pecPayments.rows[0].previouslyUsed='';indeterminate(z);
|
||||
z.b.pecPayments.state='absent';indeterminate(z);
|
||||
});
|
||||
test('payment proof, unused balance, entity, year and currency cannot be presumed',()=>{
|
||||
for(const change of[{effective:''},{effective:'no'},{document:''},{reference:''},{priorUseReference:''},{previouslyUsed:''},{previouslyUsed:'201'},{amount:'201'},{amount:'1.001'},{subjectNuit:'999999999'},{year:'2025'},{date:'2025-08-31'},{date:'2026-02-30'},{currency:'USD'}]){const c=pecCase();Object.assign(c.b.pecPayments.rows[0],change);indeterminate(c);}
|
||||
});
|
||||
test('same physical proof cannot be duplicated, re-dated or relabelled PPC',()=>{
|
||||
const a=pecCase();a.b.pecPayments.rows.push({...a.b.pecPayments.rows[0],id:'copy',date:'2026-09-01'});assert.match(indeterminate(a).pending.join(' '),/repetido|saldo disponível/);
|
||||
const b=pecCase();Object.assign(b.b.pecPayments.rows[0],{document:b.b.ppc.rows[0].document,proofAmount:'500'});assert.match(indeterminate(b).pending.join(' '),/naturezas|natureza incompatível/);
|
||||
});
|
||||
test('cross-context allocations respect prior use and changing external claims invalidates current result',()=>{
|
||||
const c=pecCase('100','0'),r=c.b.pecPayments.rows[0];r.proofAmount='500';r.previouslyUsed='100';
|
||||
const other=structuredClone(c.b);other.pecPayments.rows[0].amount='300';
|
||||
const s=JSON.parse(c.d.modelo_a_snapshot);s.irpcAnnual={'opportunity:other':other};c.d.modelo_a_snapshot=JSON.stringify(s);
|
||||
c.d.modelo_a_snapshot=saveAnnualBook(c.d,recordAnnual(c.d,c.b,now));const saved=annualBook(c.d);assert.equal(annualCurrent(c.d,saved),true);assert.equal(value(saved.result.model.payable),1308);
|
||||
const edit=JSON.parse(c.d.modelo_a_snapshot);edit.irpcAnnual['opportunity:other'].pecPayments.rows[0].amount='301';c.d.modelo_a_snapshot=JSON.stringify(edit);assert.equal(annualCurrent(c.d,saved),false);assert.match(indeterminate({...c,b:saved}).pending.join(' '),/saldo disponível/);
|
||||
edit.irpcAnnual['opportunity:other'].pecPayments.rows[0].amount='100';edit.irpcAnnual['opportunity:other'].pecPayments.rows[0].previouslyUsed='0';c.d.modelo_a_snapshot=JSON.stringify(edit);indeterminate({...c,b:saved});
|
||||
});
|
||||
test('other contexts cannot change PEC titular, origin or payment nature',()=>{
|
||||
for(const change of[{subjectNuit:'999999999'},{year:'2025'}]){const c=pecCase('100','0'),other=structuredClone(c.b);other.pecPayments.rows[0].proofAmount='500';Object.assign(other.pecPayments.rows[0],change);const s=JSON.parse(c.d.modelo_a_snapshot);s.irpcAnnual={'opportunity:other':other};c.d.modelo_a_snapshot=JSON.stringify(s);indeterminate(c);}
|
||||
const c=pecCase('100','0'),other=structuredClone(c.b);other.ppc.rows=[{...c.b.pecPayments.rows[0],amount:'50',proofAmount:'100'}];other.ppc.state='known';other.pecPayments.rows=[];other.pecPayments.state='absent';const s=JSON.parse(c.d.modelo_a_snapshot);s.irpcAnnual={'opportunity:other':other};c.d.modelo_a_snapshot=JSON.stringify(s);indeterminate(c);
|
||||
});
|
||||
test('positive special credits remain scoped and cannot be silently skipped before PEC',()=>{
|
||||
const c=pecCase();c.b.credits.amount='10';assert.match(indeterminate(c).pending.join(' '),/Créditos/);assert.equal(value(model(c).pecDeduction.available),200);
|
||||
});
|
||||
test('save/recover/recalculate preserves old annual results and all other fiscal books, plan and reports',()=>{
|
||||
const c=pecCase(),before=JSON.parse(c.d.modelo_a_snapshot),plan=c.d.business_plan_snapshot;
|
||||
const legacy=structuredClone(c.b);delete legacy.pecPayments;c.d.modelo_a_snapshot=saveAnnualBook(c.d,recordAnnual(c.d,legacy,now));const old=structuredClone(annualBook(c.d).result);
|
||||
c.b={...annualBook(c.d),pecPayments:c.b.pecPayments};c.d.modelo_a_snapshot=saveAnnualBook(c.d,recordAnnual(c.d,c.b,now));const back=annualBook(JSON.parse(JSON.stringify(c.d)));
|
||||
assert.deepEqual(back.history,[old]);assert.equal(value(back.result.model.payable),708);assert.equal(annualCurrent(c.d,back),true);const after=JSON.parse(c.d.modelo_a_snapshot);delete after.irpcAnnual;assert.deepEqual(after,before);assert.equal(c.d.business_plan_snapshot,plan);
|
||||
const html=annualHtml(c.d);assert.match(html,/PEC — dedução limitada/);assert.match(html,/708.00/);assert.match(html,/sem reembolso PEC/);
|
||||
const dossier=createProjectDossierReport(c.d,{selectedSections:new Set(['finance'])});assert.match(JSON.stringify(dossier),/PEC — dedução limitada/);
|
||||
const frozen=structuredClone(back.result);back.pecPayments.rows[0].amount='100';c.d.modelo_a_snapshot=saveAnnualBook(c.d,back);assert.equal(annualCurrent(c.d,back),false);assert.deepEqual(back.result,frozen);assert.match(annualReportRows(c.d).flat().join(' '),/Histórico/);assert.throws(()=>annualHtml(c.d),/fontes actuais/);
|
||||
});
|
||||
test('malformed PEC extension never replaces legacy snapshots',()=>{
|
||||
const c=pecCase(),original=c.d.modelo_a_snapshot;c.b.pecPayments.rows[0].previouslyUsed=null;assert.throws(()=>saveAnnualBook(c.d,c.b),/não recuperáveis/);assert.equal(c.d.modelo_a_snapshot,original);
|
||||
});
|
||||
@@ -6,7 +6,7 @@ test('absent proactivity is a no-op for accepted dossiers',()=>{assert.deepEqual
|
||||
test('each idea retains origin,source,need,limits without summing or inventing economic values',()=>{const choices=[{opportunity:source(1,'A')},{opportunity:source(2,'B')}],d={context_applicability_snapshot:JSON.stringify({version:1,opportunity_choices:choices})},r=proactiveInvestmentRows(d);assert.equal(r.filter(x=>x[1]==='Origem da análise e ideia').length,2);assert.match(JSON.stringify(r),/opportunity:1/);assert.match(JSON.stringify(r),/opportunity:2/);assert.match(JSON.stringify(r),/Hipótese analítica/);assert.match(JSON.stringify(r),/não é zero/);assert.equal(d.context_applicability_snapshot,JSON.stringify({version:1,opportunity_choices:choices}));});
|
||||
test('consumer selection only reports its idea even when other source contexts are present',()=>{const choices=[{opportunity:source(1,'A')},{opportunity:source(2,'B')}],r=proactiveInvestmentRows({context_opportunity:'opportunity:2',context_applicability_snapshot:JSON.stringify({version:1,opportunity_choices:choices})});assert.doesNotMatch(JSON.stringify(r),/opportunity:1/);assert.match(JSON.stringify(r),/opportunity:2/);});
|
||||
test('explicit consumer snapshot is retained and repeat references do not duplicate report rows',()=>{const a=source(1,'A');let r=proactiveInvestmentRows({context_opportunity_snapshot:JSON.stringify({choice:{opportunity:a}})});assert.equal(r.filter(x=>x[1]==='Origem da análise e ideia').length,1);r=proactiveInvestmentRows({context_applicability_snapshot:JSON.stringify({version:1,opportunity_choices:[{opportunity:a},{opportunity:a}]})});assert.equal(r.filter(x=>x[1]==='Origem da análise e ideia').length,1);});
|
||||
test('new API-recovered multi idea snapshot produces an independently scoped report',()=>{const d=JSON.parse(fs.readFileSync('outputs/proactive-investment-20261027/recovered-context.json','utf8'));const r=d.consumer_contexts.flatMap(c=>proactiveInvestmentRows(c.prefill.simulator));assert.equal(r.filter(x=>x[1]==='Origem da análise e ideia').length,2);assert.doesNotMatch(JSON.stringify(r),/PRIVATE-QA-/);fs.writeFileSync('outputs/proactive-investment-20261027/recovered-report-rows.json',JSON.stringify(r,null,2));});
|
||||
test('portable API consumer snapshot produces an independently scoped report',()=>{const d=JSON.parse(fs.readFileSync(new URL('./fixtures/proactive-api-consumers.json',import.meta.url),'utf8'));const before=JSON.stringify(d);const perConsumer=d.consumer_contexts.map(c=>proactiveInvestmentRows(c.prefill.simulator));for(const rows of perConsumer)assert.equal(rows.filter(x=>x[1]==='Origem da análise e ideia').length,1);const r=perConsumer.flat();assert.equal(r.filter(x=>x[1]==='Origem da análise e ideia').length,2);assert.match(JSON.stringify(perConsumer[0]),/opportunity:399/);assert.doesNotMatch(JSON.stringify(perConsumer[0]),/opportunity:400/);assert.match(JSON.stringify(perConsumer[1]),/opportunity:400/);assert.doesNotMatch(JSON.stringify(perConsumer[1]),/opportunity:399/);assert.doesNotMatch(JSON.stringify(r),/PRIVATE-QA-/);assert.equal(JSON.stringify(d),before);});
|
||||
|
||||
test('resources,assumptions and selected relationships are retained in the existing dossier report',()=>{const a=source(7,'A');Object.assign(a.analysis.proactive,{resources:'QA recurso declarado',assumptions:'QA pressuposto explícito',conditions:'QA condição',relations:[{kind:'project',target_id:'QA-reference',label:'QA projecto existente',relationship:'hypothesis',basis:'QA fundamento público'}]});const draft={context_applicability_snapshot:JSON.stringify({version:1,opportunity_choices:[{opportunity:a}]})},rows=buildProjectDossierRows(draft);assert.match(JSON.stringify(rows),/QA pressuposto explícito/);assert.match(JSON.stringify(rows),/QA projecto existente/);assert.match(JSON.stringify(rows),/sem contrato, acesso ou participação presumidos/);});
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import fs from 'node:fs';
|
||||
import crypto from 'node:crypto';
|
||||
const source = new URL('../resources/data/iso3166.tab', import.meta.url);
|
||||
const text = fs.readFileSync(source, 'utf8').replace(/\r\n/g, '\n');
|
||||
const codes = text.split('\n').filter(line => line && !line.startsWith('#')).map(line => line.split('\t')[0]);
|
||||
if (new Set(codes).size !== codes.length || codes.some(code => !/^[A-Z]{2}$/.test(code))) throw Error('Invalid existing ISO catalogue');
|
||||
const hash = crypto.createHash('sha256').update(text).digest('hex');
|
||||
fs.writeFileSync(new URL('../resources/js/lib/country-iso-codes.ts', import.meta.url),
|
||||
'// Derived code projection of resources/data/iso3166.tab; keep the existing PHP catalogue authoritative.\n' +
|
||||
'// Source SHA256 (LF): ' + hash + '; regenerate with node tools/country-iso-projection.mjs\n' +
|
||||
"export const countryIsoCodes = '" + codes.join(' ') + "'.split(' ');\n");
|
||||
@@ -0,0 +1,23 @@
|
||||
import fs from 'node:fs';
|
||||
import {carryCase} from '../tests/frontend/helpers/irpc-carry-case.mjs';
|
||||
import {internationalCase} from '../tests/frontend/helpers/irpc-international-case.mjs';
|
||||
import {recordAnnual, saveAnnualBook} from '../resources/js/lib/irpc-annual-settlement.ts';
|
||||
const cases = {};
|
||||
for (const name of ['payable', 'expired', 'missing-use', 'overallocated', 'fractional', 'recoverable']) {
|
||||
const c = carryCase(), previous = internationalCase();
|
||||
const frozen = recordAnnual(previous.d, previous.b, '2026-10-27T17:00:00+02:00').result;
|
||||
c.b.history = [frozen];
|
||||
const l = c.b.creditCarry.lots[0];
|
||||
if (name === 'expired') l.year = '2020';
|
||||
if (name === 'missing-use') l.usesState = '';
|
||||
if (name === 'overallocated') {l.eligible = '5000'; l.selected = '1400';}
|
||||
if (name === 'fractional') {
|
||||
Object.assign(l, {eligible: '2/3', originDeducted: '1/3', selected: '1/3', usesState: 'absent', uses: []});
|
||||
c.b.creditCarry.currentInternational = '1/3';
|
||||
}
|
||||
if (name === 'recoverable') {l.eligible = '2000'; l.selected = '1300'; c.b.creditCarry.currentPec = '0';}
|
||||
c.d.modelo_a_snapshot = saveAnnualBook(c.d, recordAnnual(c.d, c.b, '2026-10-28T12:00:00+02:00'));
|
||||
cases[name] = c.d;
|
||||
}
|
||||
fs.writeFileSync(new URL('../tests/frontend/fixtures/irpc-carry-preserved.json', import.meta.url), JSON.stringify({synthetic: true, actualBalances: false, cases}));
|
||||
console.log('Prepared six synthetic carry API cases with frozen international-credit history.');
|
||||
@@ -0,0 +1,27 @@
|
||||
import fs from 'node:fs';
|
||||
import assert from 'node:assert/strict';
|
||||
import {annualBook, annualCurrent, calculateAnnual, annualHtml} from '../resources/js/lib/irpc-annual-settlement.ts';
|
||||
import {createProjectDossierReport} from '../resources/js/lib/project-dossier-report.ts';
|
||||
const output = new URL('../outputs/irpc-l8-carry-20261028/', import.meta.url);
|
||||
const receipts = [];
|
||||
for (const name of ['payable', 'expired', 'missing-use', 'overallocated', 'fractional', 'recoverable']) {
|
||||
const d = JSON.parse(fs.readFileSync(new URL('recovered-' + name + '.json', output), 'utf8'));
|
||||
const b = annualBook(d), m = b.result.model, fresh = calculateAnnual(d, b);
|
||||
assert.equal(annualCurrent(d, b), true);
|
||||
assert.deepEqual(fresh, m);
|
||||
assert.equal(b.history.length, 1);
|
||||
assert.equal(b.history[0].model.payable.numerator, '700');
|
||||
const html = annualHtml(d);
|
||||
const dossier = createProjectDossierReport(d, {selectedSections: new Set(['finance']), generatedAt: '2026-10-28T12:00:00+02:00'});
|
||||
assert.match(html, /Afectação escolhida entre exercícios/);
|
||||
assert.match(JSON.stringify(dossier), /Saldo anterior — PEC — 2023/);
|
||||
assert.match(html, /último exercício ordinário/);
|
||||
if (name === 'payable') assert.match(html, /550.00/);
|
||||
if (name === 'expired') {assert.equal(m.creditCarry.lots[0].expired, true); assert.equal(m.status, 'indeterminate');}
|
||||
if (name === 'fractional') assert.deepEqual(m.arithmeticBalance, {numerator: '1822', denominator: '3'});
|
||||
fs.writeFileSync(new URL('report-' + name + '.html', output), html);
|
||||
fs.writeFileSync(new URL('dossier-' + name + '.json', output), JSON.stringify(dossier));
|
||||
receipts.push({name, status: m.status, balance: m.arithmeticBalance, apiRecovered: true, recalculationEqual: true, html: true, dossier: true, frozenHistoryPreserved: true});
|
||||
}
|
||||
fs.writeFileSync(new URL('recovered-report-receipt.json', output), JSON.stringify({synthetic: true, activeChanged: false, receipts}, null, 2));
|
||||
console.log(JSON.stringify(receipts, null, 2));
|
||||
@@ -0,0 +1,24 @@
|
||||
import fs from 'node:fs';
|
||||
import {economicCase} from '../tests/frontend/helpers/irpc-economic-case.mjs';
|
||||
import {recordAnnual, saveAnnualBook} from '../resources/js/lib/irpc-annual-settlement.ts';
|
||||
const cases = {};
|
||||
for (const name of ['payable', 'combined', 'quota', 'prior-use', 'duplicate', 'missing-gross-up', 'article40', 'shared-paper', 'partial-parcel', 'fractional', 'recoverable']) {
|
||||
const c = economicCase(name === 'combined');
|
||||
const previous = structuredClone(c.b); delete previous.economicCredits;
|
||||
c.b.history = [recordAnnual(c.d, previous, '2026-10-28T12:00:00+02:00').result];
|
||||
const r = c.b.economicCredits.rows[0], p = c.b.internationalCredits.positions[0], f = p.payments[0];
|
||||
if (name === 'quota') r.nationalLimit = '5';
|
||||
if (name === 'prior-use') r.previousUse.amount = '3';
|
||||
if (name === 'duplicate' || name === 'shared-paper') {
|
||||
Object.assign(f, {issuer: r.taxIssuer, document: r.taxDocument, proofAmount: '20', parcel: name === 'duplicate' ? r.taxParcel : 'foreign-tax-line', parcelReference: 'SYNTHETIC QA documented tax line in shared paper, no actual tax evidence'});
|
||||
}
|
||||
if (name === 'missing-gross-up') r.grossUpIncluded = '';
|
||||
if (name === 'article40') r.article40 = 'yes';
|
||||
if (name === 'partial-parcel') f.parcel = 'foreign-tax-line';
|
||||
if (name === 'fractional') Object.assign(r, {proofAmount: '0.01', taxAmount: '0.01', grossUpIncluded: '0.006'});
|
||||
if (name === 'recoverable') {f.proofAmount = '2000'; f.amount = '2000'; p.nationalLimit = '2000'; p.incomeAmount = '6250';}
|
||||
c.d.modelo_a_snapshot = saveAnnualBook(c.d, recordAnnual(c.d, c.b, '2026-10-28T14:00:00+02:00'));
|
||||
cases[name] = c.d;
|
||||
}
|
||||
fs.writeFileSync(new URL('../tests/frontend/fixtures/irpc-economic-preserved.json', import.meta.url), JSON.stringify({synthetic: true, actualTaxEvidence: false, cases}));
|
||||
console.log('Prepared eleven synthetic economic-credit cases with accepted annual/carry history.');
|
||||
@@ -0,0 +1,22 @@
|
||||
import fs from 'node:fs';
|
||||
import assert from 'node:assert/strict';
|
||||
import {annualBook, annualCurrent, calculateAnnual, annualHtml} from '../resources/js/lib/irpc-annual-settlement.ts';
|
||||
import {createProjectDossierReport} from '../resources/js/lib/project-dossier-report.ts';
|
||||
const output = new URL('../outputs/irpc-l8-economic-20261028/', import.meta.url);
|
||||
const receipts = [];
|
||||
for (const name of ['payable', 'combined', 'quota', 'prior-use', 'duplicate', 'missing-gross-up', 'article40', 'shared-paper', 'partial-parcel', 'fractional', 'recoverable']) {
|
||||
const d = JSON.parse(fs.readFileSync(new URL('recovered-' + name + '.json', output), 'utf8'));
|
||||
const b = annualBook(d), m = b.result.model;
|
||||
assert.equal(annualCurrent(d, b), true); assert.deepEqual(calculateAnnual(d, b), m);
|
||||
assert.equal(b.history.length, 1); assert.equal(b.history[0].model.payable.numerator, name === 'combined' ? '550' : '700');
|
||||
const html = annualHtml(d), dossier = createProjectDossierReport(d, {selectedSections: new Set(['finance']), generatedAt: '2026-10-28T14:00:00+02:00'});
|
||||
assert.match(html, /Crédito económico — antes do internacional/); assert.match(JSON.stringify(dossier), /Crédito económico/);
|
||||
if (name === 'combined') {assert.match(html, /538.00/); assert.match(html, /Saldo anterior — PEC — 2023/);}
|
||||
if (name === 'payable') {assert.match(html, /688.00/); assert.match(html, /1396.00/);}
|
||||
if (name === 'fractional') assert.deepEqual(m.arithmeticBalance, {numerator: '349997', denominator: '500'});
|
||||
fs.writeFileSync(new URL('report-' + name + '.html', output), html);
|
||||
fs.writeFileSync(new URL('dossier-' + name + '.json', output), JSON.stringify(dossier));
|
||||
receipts.push({name, status: m.status, balance: m.arithmeticBalance, economicDeduction: m.economicCredit.deducted, apiRecovered: true, recalculationEqual: true, html: true, dossier: true, frozenHistoryPreserved: true});
|
||||
}
|
||||
fs.writeFileSync(new URL('recovered-report-receipt.json', output), JSON.stringify({synthetic: true, activeChanged: false, receipts}, null, 2));
|
||||
console.log(JSON.stringify(receipts.map(({name, status, balance}) => ({name, status, balance})), null, 2));
|
||||
@@ -0,0 +1,16 @@
|
||||
import fs from 'node:fs';
|
||||
import {internationalCase} from '../tests/frontend/helpers/irpc-international-case.mjs';
|
||||
import {recordAnnual} from '../resources/js/lib/irpc-annual-settlement.ts';
|
||||
const cases = {};
|
||||
for (const name of ['payable','treaty','missing','limited-collecta','duplicate','absent','prior-use']) {
|
||||
const c = name === 'limited-collecta' ? internationalCase('2000','2000') : internationalCase();
|
||||
const p = c.b.internationalCredits.positions[0];
|
||||
if (name === 'treaty') Object.assign(p, {treaty: 'yes', treatyLimit: '6', treatyReference: 'SYNTHETIC QA convention ceiling only, no actual treaty assertion'});
|
||||
if (name === 'missing') p.treaty = '';
|
||||
if (name === 'limited-collecta') p.incomeAmount = '6250';
|
||||
if (name === 'duplicate') Object.assign(p.payments[0], {issuer: c.b.ppc.rows[0].issuer, document: c.b.ppc.rows[0].document, proofAmount: '500'});
|
||||
if (name === 'absent') {c.b.internationalCredits.state = 'absent';c.b.internationalCredits.positions = [];}
|
||||
if (name === 'prior-use') Object.assign(p.previousUse, {amount: '3', reference: 'SYNTHETIC QA: documented actual previous credit use of 3 MZN'});
|
||||
cases[name] = recordAnnual(c.d, c.b, '2026-10-27T17:00:00+02:00');
|
||||
}
|
||||
fs.writeFileSync(new URL('../tests/frontend/fixtures/irpc-international-preserved.json', import.meta.url), JSON.stringify({synthetic: true, actualConventionData: false, base: 'irpc-assessment-preserved.json', cases}));
|
||||
@@ -0,0 +1,26 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import assert from 'node:assert/strict';
|
||||
import {fileURLToPath} from 'node:url';
|
||||
import {annualBook,annualCurrent,calculateAnnual,annualHtml,annualReportRows} from '../resources/js/lib/irpc-annual-settlement.ts';
|
||||
import {createProjectDossierReport} from '../resources/js/lib/project-dossier-report.ts';
|
||||
const root=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
||||
assert.equal(path.basename(root),'portal-investidor-irpc-international-qa-20261027');
|
||||
const output=path.join(root,'outputs/irpc-l8-international-20261027');
|
||||
const results=[];
|
||||
for(const name of ['payable','treaty','missing','limited-collecta','duplicate','absent','prior-use']){
|
||||
const d=JSON.parse(fs.readFileSync(path.join(output,'recovered-'+name+'.json'),'utf8'));
|
||||
const book=annualBook(d),m=book.result.model;
|
||||
assert.equal(annualCurrent(d,book),true);
|
||||
const fresh=calculateAnnual(d,book);assert.equal(fresh.status,m.status);assert.deepEqual(fresh.arithmeticBalance,m.arithmeticBalance);assert.deepEqual(fresh.internationalCredit,m.internationalCredit);
|
||||
const html=annualHtml(d),rows=annualReportRows(d);
|
||||
const dossier=createProjectDossierReport(d,{selectedSections:new Set(['finance']),generatedAt:'2026-10-27T17:00:00+02:00'});
|
||||
assert.match(html,/Crédito internacional — antes do PEC/);assert.match(JSON.stringify(dossier),/Crédito internacional — antes do PEC/);
|
||||
if(['missing','duplicate'].includes(name)){assert.match(html,/Indeterminado/);assert.equal(m.arithmeticBalance,null);}
|
||||
if(name==='payable'){assert.match(html,/700.00/);assert.match(html,/1400.00/);assert.match(html,/1200.00/);}
|
||||
fs.writeFileSync(path.join(output,'report-'+name+'.html'),html);
|
||||
fs.writeFileSync(path.join(output,'dossier-'+name+'.json'),JSON.stringify(dossier));
|
||||
results.push({case:name,status:m.status,balance:m.arithmeticBalance,international:m.internationalCredit,pec:m.pecDeduction,reportRows:rows.length,history:book.history.length,apiRoundTrip:true,current:true,dossier:true});
|
||||
}
|
||||
fs.writeFileSync(path.join(output,'recovered-report-receipt.json'),JSON.stringify({synthetic:true,activeChanged:false,results},null,2));
|
||||
console.log(JSON.stringify(results.map(({case:name,status,balance,history})=>({case:name,status,balance,history})),null,2));
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
$root=dirname(__DIR__);$operation=$argv[1]??'';
|
||||
if(!in_array($operation,['routes','test'],true)||basename($root)!=='portal-investidor-sequential-delivery-20261027')throw new RuntimeException('Explicit PEC QA workspace and routes/test operation required.');
|
||||
foreach(['storage/framework/views','storage/framework/cache','storage/framework/sessions','bootstrap/cache','storage/logs','outputs/irpc-l8-pec-20261027']as$dir)if(!is_dir($root.'/'.$dir))mkdir($root.'/'.$dir,0700,true);
|
||||
foreach(['APP_BASE_PATH'=>$root,'APP_ENV'=>'testing','APP_KEY'=>'base64:'.base64_encode(str_repeat('Q',32)),'DB_CONNECTION'=>'pgsql','DB_HOST'=>'127.0.0.1','DB_PORT'=>'55441','DB_DATABASE'=>'proactive_investment_20261027_test','DB_USERNAME'=>'postgres','DB_PASSWORD'=>'','DB_URL'=>'','CACHE_STORE'=>'array','SESSION_DRIVER'=>'array','QUEUE_CONNECTION'=>'sync','MAIL_MAILER'=>'array','INVESTMOZ_PEC_QA_OUTPUT'=>'1']as$k=>$v){putenv($k.'='.$v);$_ENV[$k]=$_SERVER[$k]=$v;}
|
||||
require $root.'/vendor/autoload.php';
|
||||
spl_autoload_register(function($class)use($root){foreach(['App\\'=>'app/','Tests\\'=>'tests/','Database\\Factories\\'=>'database/factories/']as$prefix=>$dir)if(str_starts_with($class,$prefix)){$file=$root.'/'.$dir.str_replace('\\','/',substr($class,strlen($prefix))).'.php';if(is_file($file)){require $file;return;}}},true,true);
|
||||
$app=require $root.'/bootstrap/app.php';
|
||||
(new Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables)->bootstrap($app);(new Illuminate\Foundation\Bootstrap\LoadConfiguration)->bootstrap($app);
|
||||
$c=config('database.connections.pgsql');
|
||||
if($app->configurationIsCached()||$c['host']!=='127.0.0.1'||(int)$c['port']!==55441||$c['database']!=='proactive_investment_20261027_test'||!empty($c['url']))throw new RuntimeException('PEC QA target rejected');
|
||||
$pdo=new PDO('pgsql:host=127.0.0.1;port=55441;dbname='.$c['database'],$c['username'],$c['password']);
|
||||
if($pdo->query('select current_database()')->fetchColumn()!==$c['database'])throw new RuntimeException('PEC QA identity');
|
||||
if($operation==='routes'){$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();$status=Illuminate\Support\Facades\Artisan::call('wayfinder:generate',['--with-form'=>true]);echo Illuminate\Support\Facades\Artisan::output();exit($status);}
|
||||
$argv=['phpunit','-c','phpunit.investmoz.xml','tests/Feature/IrpcPecDraftTest.php'];$argc=count($argv);$_SERVER['argv']=$argv;$_SERVER['argc']=$argc;require $root.'/vendor/phpunit/phpunit/phpunit';
|
||||
@@ -0,0 +1,25 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import assert from 'node:assert/strict';
|
||||
import {fileURLToPath} from 'node:url';
|
||||
import {annualBook,annualCurrent,calculateAnnual,annualHtml,annualReportRows} from '../resources/js/lib/irpc-annual-settlement.ts';
|
||||
import {createProjectDossierReport} from '../resources/js/lib/project-dossier-report.ts';
|
||||
const root=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
||||
assert.equal(path.basename(root),'portal-investidor-sequential-delivery-20261027');
|
||||
const output=path.join(root,'outputs/irpc-l8-pec-20261027');
|
||||
const results=[];
|
||||
for(const name of ['payable','capped','recoverable','missing','prior-use']){
|
||||
const d=JSON.parse(fs.readFileSync(path.join(output,'recovered-'+name+'.json'),'utf8'));
|
||||
const book=annualBook(d),model=book.result.model;
|
||||
assert.equal(annualCurrent(d,book),true);
|
||||
assert.equal(calculateAnnual(d,book).status,model.status);
|
||||
const html=annualHtml(d),rows=annualReportRows(d);
|
||||
const dossier=createProjectDossierReport(d,{selectedSections:new Set(['finance']),generatedAt:'2026-10-27T15:00:00+02:00'});
|
||||
assert.match(html,/PEC — dedução limitada/);assert.match(JSON.stringify(dossier),/PEC — dedução limitada/);
|
||||
if(name==='missing'){assert.match(html,/Indeterminado/);assert.equal(model.arithmeticBalance,null);}
|
||||
fs.writeFileSync(path.join(output,'report-'+name+'.html'),html);
|
||||
fs.writeFileSync(path.join(output,'dossier-'+name+'.json'),JSON.stringify(dossier));
|
||||
results.push({case:name,status:model.status,balance:model.arithmeticBalance,pec:model.pecDeduction,rows:rows.length,history:book.history.length,apiRoundTrip:true,current:true,dossier:true});
|
||||
}
|
||||
fs.writeFileSync(path.join(output,'recovered-report-receipt.json'),JSON.stringify({synthetic:true,activeChanged:false,results},null,2));
|
||||
console.log(JSON.stringify(results.map(({case:name,status,balance,history})=>({case:name,status,balance,history})),null,2));
|
||||
Reference in New Issue
Block a user