Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7fdb6b424b | ||
|
|
dc14f01e14 | ||
|
|
1ee0c6d21d | ||
|
|
9fda6d6e12 |
@@ -1403,3 +1403,67 @@ 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 — 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.
|
||||
|
||||
@@ -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,10 @@ 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 .
|
||||
|
||||
@@ -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,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 | Primeiro saldo com retenções/PPC entregue em9fda6d6e;dedução de PEC corrente comprovado emQA,sem instalação | Residente geral2026/MZN;PEC limitado à colecta,sem reembolso do excedente;ausência/zero/falta explícitos | ColectaL6/L7,L5received/effective/on_account,comprovativos e histórico anual reutilizados | Transporte/obrigaçãoPEC,créditos próprios,autónomos,períodos/moedas eM20M22 específicos;pendências só dependentes | 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,88 @@ 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 — 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.
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,16 +1,17 @@
|
||||
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')}
|
||||
@@ -18,7 +19,7 @@ export function SimulationIrpcAnnual({draft,onChange}:Props){
|
||||
{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>
|
||||
<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>)}
|
||||
<SimulationIrpcPec book={b} save={save}/>{([...(b.pecPayments?[]:[['pec','PEC']as const]),['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,23 @@
|
||||
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>
|
||||
<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>;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
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';
|
||||
@@ -12,7 +13,8 @@ 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};
|
||||
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;result?:AnnualRecord;history:AnnualRecord[]};
|
||||
export const annualSource=(b:AnnualBook)=>b.pecPayments?annualSettlementSource.replace('PEC/créditos positivos','Créditos próprios positivos')+' '+pecSource:annualSettlementSource;
|
||||
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:''});
|
||||
@@ -24,6 +26,7 @@ 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?.pecPayments!==undefined)validatePec(b.pecPayments);
|
||||
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.');
|
||||
@@ -53,7 +56,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]:[])])];}
|
||||
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,8 +72,8 @@ 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]),['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'|'pec';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(' ')});
|
||||
@@ -79,21 +82,24 @@ export function calculateAnnual(d:Record<string,unknown>,b:AnnualBook){
|
||||
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(' ')});
|
||||
}
|
||||
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));}}
|
||||
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({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});}}
|
||||
const elsewhere:Array<{identity:string;subjectNuit:string;year:string;value:FiscalFraction;proofAmount:FiscalFraction;sourceKey:string;kind:'ppc'|'pec'|'withholding';previouslyUsed?:FiscalFraction}>=[];
|
||||
for(const[scope,other]of Object.entries(books(d))){if(scope===acquisitionScope(d))continue;try{validateAnnual(other);for(const r of other.pecPayments?.rows??[]){const p=pecPaymentValue(r,other.subjectNuit,other.year);elsewhere.push({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({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({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=>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(same.filter(x=>x.kind!=='withholding').length>1||new Set(same.map(x=>x.kind)).size>1)issue='Mesmo comprovativo repetido ou usado com naturezas diferentes (retenção/PPC/PEC);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==='pec'&&c.previouslyUsed){if(other.some(x=>x.kind!=='pec')||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 com utilizaçã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);sem dupla dedução.';
|
||||
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(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 ready=pending.length===0&&colecta!==null,pec=b.pecPayments?pecDeduction(addFractions(claims.filter(c=>c.kind==='pec'&&!c.reason&&c.value).map(c=>c.value!)),colecta,ready):null,arithmeticBalance=ready?subtractFractions(subtractFractions(pec?.afterPec??colecta!,knownWithholdings),knownPpc):null;
|
||||
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,pecDeduction:pec,knownWithholdings,knownPpc,arithmeticBalance,payable,recoverable,belowMinimum,status,pending:[...new Set(pending)],warnings:warn,source:annualSource(b),officialBalancesConsumed:false,submitted:false,refundReceived:false,limitations:[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 +114,21 @@ 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),
|
||||
'IRPC — L8',(c.kind==='withholding'?'Retenção sofrida':c.kind==='pec'?'PEC':'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??'')
|
||||
c.kind==='withholding'?m.input.withholdings.reference+'; '+(m.input.withholdings.rows.find(r=>r.id===c.id)?.reference??''):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.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')+'; 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,35 @@
|
||||
import {subtractFractions, decimalFraction, type FiscalFraction} from './fiscal-rational.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 = {
|
||||
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.');
|
||||
}
|
||||
}
|
||||
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) {
|
||||
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) {
|
||||
const deducted = ready && colecta ? (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};
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
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,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,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,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