Prepare scoped MRFTA and documented compensatory interest with QA recovery

This commit is contained in:
2026-10-25 20:19:08 +02:00
parent ce746d889d
commit 85e3cd5c7b
14 changed files with 2466 additions and 4 deletions
@@ -370,3 +370,25 @@ Reutilizados contraparte, residência/EP, documento, qualificação, montante e
**Saldo na mesma matriz:** restantes alíneas68, categorias digitais/residentes não correspondidas, outras convenções/Estados e casos com EP, dividendos/juros ou excesso por relações especiais mantêm correspondência própria. Juros de mora, rectificações, agregação, rubricas/CER e preenchimento do MRFTA permanecem discriminados. O cálculo convencional não declara cumpridas as formalidades de accionamento, recepção ou aceitação AT. Não são transportados formulários portugueses21-RFI para obrigações moçambicanas. Pendências condicionam apenas os casos dependentes. L5, IRPC e IRPS não estão integralmente concluídos. L3/ISPC/câmbio não foram retestados; as suas provas são reutilizadas. Entrega conjunta preparada apenas para os recortes demonstrados. Instalação futura segue o procedimento Git e os controlos de destino/backup/restauro já existentes.
Recibo: `docs/investmoz/simulations/irpc-l5-reliefs-qa-20261025.json`; provas locais em `outputs/irpc-reliefs/`.
## 25/10/2026 — dispensas IRPC e convenção PT–MZ instaladas pelo Git
Instalada `ce746d88`, com `2b33a1c3` preservada como release anterior. Backup fresco2026-10-25T18:00:39Z:174 tabelas e369 ficheiros/configuração;restauro isolado em contentor sem rede/55441 passou. Quatro ficheiros de execução conferidos,28 protegidos L3/ISPC/câmbio e29 originais preservados. Schema,permissões e configuração iguais;zero migrações/importações. As172 tabelas de negócio mantêm checksums;apenas cache operacional e sessões anónimas das verificações foram reconciliadas. O mínimo de espaço do instalador foi reposto pelo arquivo e verificação de dependências de5 releases antigas,sem tocar na actual/anterior nem apagar dados/backups.
Manifesto público/revisão servida,8marcadores compilados e simuladorHTTP200 conferidos. Provas QA aceites reutilizadas;CI habitual do procedimento Git passou. Sem ampliar a cobertura:68(d/f) e royalties/uso de equipamento PT–MZ nos casos efectivamente demonstrados. Juros,MRFTA e restantes casos continuam na mesma matriz;L5 permanece parcial. Seguimento em QA separado,sem instalar novos cálculos. Recibo:`irpc-l5-reliefs-delivery-20261025.json`.
## 25/10/2026 — juros e preenchimento MRFTA: incremento QA após ce746d88
O portal mantém a entrega instalada `ce746d88`, com `2b33a1c3` preservada;recibo de instalação `irpc-l5-reliefs-delivery-20261025.json`. Seguimento isolado em `codex/irpc-juros-mrfta-qa-20261025`,sem nova instalação. Reutilizadas as provas aceites;29 originais e29 ficheiros protegidos conferidos por hash. Sem delta de schema,configuração ou permissões.
**Preenchimento:** quadros1–7 do MRFTA de preparação,um mês/ano;identidade fiscal reutilizável entre livros sem sobrescrever edições nem misturar NUIT distintos. Base,taxa,imposto,documentos e entregas vêm do cálculo IRPC guardado. Agrupamento apenas da mesma rubrica/taxa/CER. Retenções sofridas,dispensas e outros períodos ficam separados. CER requer referência aplicável;os números de campos do M/39 antigo não são convertidos em códigos de receita. Rubricas digitais/especiais desconhecidas e múltiplas taxas na mesma rubrica permanecem discriminadas. Quadros8/9 não são assinados/preenchidos pelo portal. Exportação HTML de preparação,não alteração do PDF oficial nem declaração submetida.
**Juros:** Decreto9/2008,25/35,PDF6/8,p121/123 identifica MAIBOR12meses+2p.p.;BM documenta a descontinuidade da publicação. Não foi obtido parâmetro fiscal sucessor aplicável2026;MIMO/Prime não o substituem. Implementada transcrição de juros compensatórios de liquidação documentada,com período,data,montante e referência;ou ausência de atraso quando entrega integral e datas/prazo documentados coincidem. Informação insuficiente não vira zero. Juros de mora continuam por implementar. O subtotal do principal permanece disponível;total do formulário fica indeterminado enquanto faltarem juros,CER,completude ou situação de outras rubricas (IRPS/autónoma),sem isenções ou créditos presumidos. A completude é um dado da preparação,não novo aprovador organizacional.
**Prova do delta:**12 testes novos+1 de apresentação IRPC;TypeScript e compilação QA passaram. API privada1teste/14verificações com rollback no55441;preparação→gravação→recuperação→relatório/dossier. UI real em exemplo sintético:preparação,gravação/recuperação local,informação insuficiente e download HTML;prova API separada. CER999 é fictício de teste,não taxa/código oficial activado. Exemplo:principal20000MZN,entrega3000,juros documentados500,total17500;alterar provas conserva histórico e desactualiza a preparação. Não foram repetidas manualmente suites L3/ISPC/câmbio.
**Saldo:**cálculo automático dos juros e taxa aplicável2026,juros de mora,catálogo CER integral,rubricas digitais/especiais,mistura de taxas e obrigações IRPS/autónomas fora do recorte. Limita apenas as parcelas dependentes;L5/IRPC/IRPS continuam parciais. Recibo `irpc-juros-mrfta-qa-20261025.json`;provas locais `outputs/irpc-mrfta/`.
Reprodução QA: `php tools/irpc-mrfta-qa.php routes`; `node --test tests/frontend/irpc-mrfta.test.mjs`; `php tools/irpc-mrfta-qa.php test`. PHP8.5,Node24,dependências existentes;alvo e identidadeQA guardados antes de fornecedores/testes.
@@ -0,0 +1,360 @@
{
"status": "QA_juros_MRFTA_demonstrated_not_installed",
"installed_baseline": "ce746d889d5711f23f4e0ac8d9802997b5e106f9",
"branch": "codex/irpc-juros-mrfta-qa-20261025",
"implemented": [
"MRFTA preparation fields1–7 by single month;reserved8/9;HTML preparation export",
"Rubric/base/rate/tax and reconciled payments reused from frozen IRPC result;separate suffered/dispensed/other-month rows",
"Documented CER transcription with reference;no inferred CER from old guide field numbers",
"Documented compensatory-interest assessment amount or zero only after full timely remittance with documented deadline",
"Unknown rates/interest,digital/special rubrics,unknown CER,multiple rates or other taxes keep final total indeterminate while principal rows remain available",
"Private draft persistence,recovery,history;standalone MRFTA and full dossier reports;old IRPC inputs/calculations retained"
],
"not_implemented": [
"Automatic compensatory-interest arithmetic for2026:applicable successor/parameter for discontinuedMAIBOR12months not obtained",
"Juros de mora;autonomous tax;IRPS beyond prior delivered case",
"Digital/e-money and unqualified special rubrics;MRFTA CER catalogue not supplied by this increment",
"Mixed rate/CER same official rubric remains separate pending correspondence",
"No official PDF changed/filled;HTML preparation reflects fields and retains sourcePDF original;no submission/signatures/ATacceptance"
],
"verification": {
"frontend": "12 new tests+1 directed existing IRPC presentation test passed",
"types": "tsc --noEmit exit0",
"build": "complete QA build exit0",
"private_api": "1test/14assertions;DatabaseTransactions rollback;127.0.0.1:55441/simulator_fiscal_delta_20261021_test",
"dossier": "generated from actual private API recovered model",
"UI": {
"calculation": true,
"localSaveRecovery": true,
"privateApiProof": "IrpcMrftaDraftTest, separate",
"report": true,
"documentedInterest": true,
"insufficientInformation": true,
"download": true,
"errors": []
},
"CER_example": "999 is explicitly fictional QA data,not an official recipe-code mapping",
"unchanged_proofs": "AcceptedL3/ISPC/FX and ce746d88 calculation proofs reused;no manual repeat suites"
},
"runtime_delta": [
{
"path": "resources/js/lib/irpc-mrfta.ts",
"sha256_LF": "1ff1725724dbdaaa9a3857941d03e312c6a7da573d369ee86015edc90cd2d0d1"
},
{
"path": "resources/js/lib/irpc-withholdings.ts",
"sha256_LF": "8e5f3c035ce5e4d6c3fc82f933439ab580fa5519492f9c4d32cfb71b77b54803"
},
{
"path": "resources/js/components/simulation-irpc-mrfta.tsx",
"sha256_LF": "9cf7d5e447e1a9ed37d1bd24b4ac5104b1adec3790ff8db4356c3b68b339d89a"
},
{
"path": "resources/js/components/simulation-irpc-withholdings.tsx",
"sha256_LF": "751af4fab346659bc5ccb08b8a2169769af7521ea0035050af32b41ea7f5aa76"
}
],
"protected_runtime": [
{
"path": "resources/js/components/acquisition-beverage-restitution.tsx",
"sha256": "6f44897b8b12569314551df0022c26da57a165634087942a724293e66837558d",
"sha256_LF": "6f44897b8b12569314551df0022c26da57a165634087942a724293e66837558d"
},
{
"path": "resources/js/components/fiscal-calculation-gaps.tsx",
"sha256": "1dab6ceb73756d4e023096b3b2ba767aaa843ff6cc69046ea630ab449a00b0dd",
"sha256_LF": "1dab6ceb73756d4e023096b3b2ba767aaa843ff6cc69046ea630ab449a00b0dd"
},
{
"path": "resources/js/components/fiscal-l3-preparation.tsx",
"sha256": "e84a7b1ccadaa1b5bd78d778a3287f96ccaf530855eef404786cd7aa3d321b73",
"sha256_LF": "e84a7b1ccadaa1b5bd78d778a3287f96ccaf530855eef404786cd7aa3d321b73"
},
{
"path": "resources/js/components/simulation-acquisitions.tsx",
"sha256": "c206d4cfe575d6ba007aa2b425b5f898efa97e4162b8710e358b6ecfd9458542",
"sha256_LF": "c206d4cfe575d6ba007aa2b425b5f898efa97e4162b8710e358b6ecfd9458542"
},
{
"path": "resources/js/components/simulation-operations.tsx",
"sha256": "a7e55972fa790063dcf11945055de2af46a77db9e509900af22ede7ec47bb4cf",
"sha256_LF": "a7e55972fa790063dcf11945055de2af46a77db9e509900af22ede7ec47bb4cf"
},
{
"path": "resources/js/lib/iva-acquisition-bc.ts",
"sha256": "b0ef55e0917bdb79f14b53ba9e95d73c08153befeb7d4ce73f81e6fb13992225",
"sha256_LF": "b0ef55e0917bdb79f14b53ba9e95d73c08153befeb7d4ce73f81e6fb13992225"
},
{
"path": "resources/js/lib/iva-beverage-allocation.ts",
"sha256": "32968d198c7fe0c53ca89641c22d5749be6636be2c79127b867e4e3afd0dd40d",
"sha256_LF": "32968d198c7fe0c53ca89641c22d5749be6636be2c79127b867e4e3afd0dd40d"
},
{
"path": "resources/js/lib/iva-beverage-restitution.ts",
"sha256": "5c650cbd9469d5f99001f56fc4ff99ac973eb368036d4e96c6d171f1b220bd9e",
"sha256_LF": "5c650cbd9469d5f99001f56fc4ff99ac973eb368036d4e96c6d171f1b220bd9e"
},
{
"path": "resources/js/lib/iva-calculation-gaps.ts",
"sha256": "2e7d84a0c196bd5b56fd67cc1a389d43d5041894184e675680a4de3cd10b31a2",
"sha256_LF": "2e7d84a0c196bd5b56fd67cc1a389d43d5041894184e675680a4de3cd10b31a2"
},
{
"path": "resources/js/lib/iva-operations.ts",
"sha256": "ec37b949f1233c5f5c14a87450c421ed29fabf1ed5b73fb32564564a2bc31f54",
"sha256_LF": "ec37b949f1233c5f5c14a87450c421ed29fabf1ed5b73fb32564564a2bc31f54"
},
{
"path": "resources/js/lib/l3-form-preparation.ts",
"sha256": "fc47f5fa5ec1420fbd847d0d081f00d99ebbb524164eef785b20877e0562f704",
"sha256_LF": "fc47f5fa5ec1420fbd847d0d081f00d99ebbb524164eef785b20877e0562f704"
},
{
"path": "resources/js/lib/ispc-residuals.ts",
"sha256": "40d5751c06c7c155ab4b7a17ee2d1653da2fbbf075807fbb8561cdbe19820955",
"sha256_LF": "40d5751c06c7c155ab4b7a17ee2d1653da2fbbf075807fbb8561cdbe19820955"
},
{
"path": "resources/js/components/simulation-ispc-residuals.tsx",
"sha256": "a9219d6f41ccbd0ce45659d4f6aa0fa1c169a685840f5cb1e18daeca377d5e18",
"sha256_LF": "a9219d6f41ccbd0ce45659d4f6aa0fa1c169a685840f5cb1e18daeca377d5e18"
},
{
"path": "resources/js/lib/ispc-eligibility.ts",
"sha256": "44675c5efacdc1ae19505667611ba3b522d3043cebacc8754597c3a68ca89d25",
"sha256_LF": "44675c5efacdc1ae19505667611ba3b522d3043cebacc8754597c3a68ca89d25"
},
{
"path": "resources/js/components/simulation-ispc-quarter.tsx",
"sha256": "2c2555309b32d4261f14e7164f39314c9188129c2990357fc54090a2468dd2b4",
"sha256_LF": "2c2555309b32d4261f14e7164f39314c9188129c2990357fc54090a2468dd2b4"
},
{
"path": "resources/js/components/simulation-ispc-withholding.tsx",
"sha256": "e0445d015b72859e48726959aa52686f79b1cf5c2ade29751efa83257068b878",
"sha256_LF": "e0445d015b72859e48726959aa52686f79b1cf5c2ade29751efa83257068b878"
},
{
"path": "resources/js/lib/ispc-quarter.ts",
"sha256": "f66bd52e9cdfe1ddaa6b00a931465862b3d890b9be216b7a056740f027f24129",
"sha256_LF": "f66bd52e9cdfe1ddaa6b00a931465862b3d890b9be216b7a056740f027f24129"
},
{
"path": "resources/js/lib/ispc-withholding.ts",
"sha256": "6c31b240299edd3262efa28f7f3d5aee21123f94e30ef4ab90e26c35777cb6e4",
"sha256_LF": "6c31b240299edd3262efa28f7f3d5aee21123f94e30ef4ab90e26c35777cb6e4"
},
{
"path": "resources/js/components/simulation-ispc-eligibility.tsx",
"sha256": "f48dd4a8f87a994eda0447f81a463589905e5873b2e1044debac9d10825a880c",
"sha256_LF": "f48dd4a8f87a994eda0447f81a463589905e5873b2e1044debac9d10825a880c"
},
{
"path": "resources/js/lib/iva-acquisitions.ts",
"sha256": "cce283b1d0a07fb383fcb527225f4dd5bec829010a662cbaae1d60e595221977",
"sha256_LF": "cce283b1d0a07fb383fcb527225f4dd5bec829010a662cbaae1d60e595221977"
},
{
"path": "resources/js/lib/iva-modelo-a.ts",
"sha256": "c641619a05613c1620385baad0e9ffbf810dd970aafb30e12b3a7f9a3bf11846",
"sha256_LF": "c641619a05613c1620385baad0e9ffbf810dd970aafb30e12b3a7f9a3bf11846"
},
{
"path": "resources/js/lib/iva-fx-selection.ts",
"sha256": "9aa07ad443bc777b80510a3b5bd6aff9ddb9814d2450f3c690e531e74d71d998",
"sha256_LF": "9aa07ad443bc777b80510a3b5bd6aff9ddb9814d2450f3c690e531e74d71d998"
},
{
"path": "resources/js/components/simulation-fx-selection.tsx",
"sha256": "39bb6d87d404745b0b66349bc11626545a3672ae27321ce9b4658416592059bf",
"sha256_LF": "39bb6d87d404745b0b66349bc11626545a3672ae27321ce9b4658416592059bf"
},
{
"path": "resources/js/lib/business-financial-plan.ts",
"sha256": "c20274722d1ffcd0360f041a6cacb5d868fe0621cb27a1aca6e06826612c3ebb",
"sha256_LF": "c20274722d1ffcd0360f041a6cacb5d868fe0621cb27a1aca6e06826612c3ebb"
},
{
"path": "resources/js/lib/iva-currency-events.ts",
"sha256": "cc9f2045d302d7987310da4006cc250fc5c99a27397918d166d649bfc32fab08",
"sha256_LF": "cc9f2045d302d7987310da4006cc250fc5c99a27397918d166d649bfc32fab08"
},
{
"path": "resources/js/lib/irpc-fx.ts",
"sha256": "5c4a7a477429f8d775acf6db3e117008468f5554e5731dec81f60fbb75e948fb",
"sha256_LF": "5c4a7a477429f8d775acf6db3e117008468f5554e5731dec81f60fbb75e948fb"
},
{
"path": "resources/js/components/simulation-form.tsx",
"sha256": "01d20f4761933a3e4dd7c776bd4adcf9610ac0435d918a1998ae9f0c3ce3f234",
"sha256_LF": "01d20f4761933a3e4dd7c776bd4adcf9610ac0435d918a1998ae9f0c3ce3f234"
},
{
"path": "resources/js/lib/project-dossier.ts",
"sha256": "df8b224b9472449711d0b69a873fa75f9cc9f4d84dce1a314fc576fae9c6eae2",
"sha256_LF": "df8b224b9472449711d0b69a873fa75f9cc9f4d84dce1a314fc576fae9c6eae2"
},
{
"path": "resources/js/lib/project-dossier-report.ts",
"sha256_LF": "4523365e2d4a2fcd014e13393deb33a5ec4b9396ff580d8a237aa8c4f02265e4"
}
],
"accepted_originals_inventory": [
{
"path": "docs/investmoz/simulations/source-archive/AT-Comunicado-9-2026-ISPC.pdf",
"sha256": "106f8a9ce9255c03549c70bf07fe999d32e6e55302d009b509862aa74ccb4973"
},
{
"path": "docs/investmoz/simulations/source-archive/AT-Modelo-30-ISPC.pdf",
"sha256": "6802edd06b6ff638a0b851a3ec7ecf69af8f07840faa47446becad67d35d445c"
},
{
"path": "docs/investmoz/simulations/source-archive/AT-Modelo-B-Geral.pdf",
"sha256": "bd977c7c59d4d98befd92e2c566b1bf168cb1343bb519f51b41a3807f431fc57"
},
{
"path": "docs/investmoz/simulations/source-archive/AT-MRFTA-19-39.pdf",
"sha256": "8ea62c53066b482f87078a7a7b1ecf15ca98a12a122e8d7abca63acfc993186b"
},
{
"path": "docs/investmoz/simulations/source-archive/BR-16-I-Serie-2008-04-16.pdf",
"sha256": "fc373ff5637edf75ec6a9595dec8763a8503d4ee786e3b7d8f1c742eae9d80ee"
},
{
"path": "docs/investmoz/simulations/source-archive/BR-248-I-Suplemento-1-2025.pdf",
"sha256": "ccf2a8deeca820f2dd076b4b2141b1efe3f6a806568c3a587ca59a8e92f4c251"
},
{
"path": "docs/investmoz/simulations/source-archive/BR-89-I-Serie-2017-06-08.pdf",
"sha256": "11f0cab15eb6e51010952203ffa24fdc533a24fd642e19525fe4f46b17a1f5e5"
},
{
"path": "docs/investmoz/simulations/source-archive/BR-95-I-Suplemento-2025-05-21.pdf",
"sha256": "e0e682e2c027c3cd9c1d46a73a3f48bc5f6bdbf05667fd97314d7a3f6a456358"
},
{
"path": "docs/investmoz/simulations/source-archive/BR252-I-Serie-2022-Lei20-PESOE.pdf",
"sha256": "1159fd3ce1ba5581671796d083534da48bd3c5578298819e896f1c2a8026318d"
},
{
"path": "docs/investmoz/simulations/source-archive/Decreto-22-2021-BR70.pdf",
"sha256": "a8fac38dcfe08f9c7a8e46254cff10e1ec82fc4f7a8d88ad6e4fe7d91ffc21a5"
},
{
"path": "docs/investmoz/simulations/source-archive/Decreto-27-2026-BR123.pdf",
"sha256": "26249c3fa58f149c81de36e99d1aac2819a4771379d9e8833159660dabf539bb"
},
{
"path": "docs/investmoz/simulations/source-archive/Decreto-29-1998-BR22-5-suplemento.pdf",
"sha256": "6242cbe675faadf085f43f95ec8c7e8868aafe67c3b2ab2bb010992288f954f8"
},
{
"path": "docs/investmoz/simulations/source-archive/Decreto-3-2011-BR-11-I-mineiros.pdf",
"sha256": "5bca1133666ba9c69870b89823420175b7b5b8d0eccb667ecdf5c29716f3b6be"
},
{
"path": "docs/investmoz/simulations/source-archive/Decreto-37-2023-BR124.pdf",
"sha256": "7e680b191481faa0e2e9057fa5053587eb2a29d1d8c819e0eb9f02814255a04c"
},
{
"path": "docs/investmoz/simulations/source-archive/Decreto-9-2008-BR16.pdf",
"sha256": "96fccdecbfdc11c9eafb90fbc6c7bc259f88bbd4c7507d1ac3af1fa03f960697"
},
{
"path": "docs/investmoz/simulations/source-archive/Lei-13-2016-CIVA-BR-156-10-suplemento.pdf",
"sha256": "b90dc7c14ecea2e378c4a62e8e5a47d54a5644c457baeb8b8053b92fd2a5710b"
},
{
"path": "docs/investmoz/simulations/source-archive/Lei-14-1992-BR42.pdf",
"sha256": "089fff7423e99a70a230a73ca379e4ea3a91d124703f91016e57ff82794b9e56"
},
{
"path": "docs/investmoz/simulations/source-archive/Lei-17-2022-BR251-exemplar-MCNet5.pdf",
"sha256": "14eb34f0af91a8f5cb1770c7b52d6ff839c6818302d1ed96d79afbfb9fe999d4"
},
{
"path": "docs/investmoz/simulations/source-archive/Lei-17-2022-IPP-BR250-Parlamento.pdf",
"sha256": "978cd22403ffd336a631704ecd7a5bbd814a427c815b9acdaca5515591dd9e2a"
},
{
"path": "docs/investmoz/simulations/source-archive/Lei-19-2013-BR76.pdf",
"sha256": "bba96d645cc935ffc3d1a79b9f0f73ac1c693d6459c2ad41f56cd2841accae76"
},
{
"path": "docs/investmoz/simulations/source-archive/Lei-19-2017-IRPS-BR-OCAM.pdf",
"sha256": "c3a0186cb9ad090d7a9533006295dc9bb6a62ad0d6150173cbd49dba4ee4b52d"
},
{
"path": "docs/investmoz/simulations/source-archive/Lei-2-2011-BR-1-4-suplemento-MCNet.pdf",
"sha256": "a7543a68cc3287a46a83c39a04821b40f649dd06c4e9b5dc51ff0b47fba08899"
},
{
"path": "docs/investmoz/simulations/source-archive/Lei-22-2022-IVA-republicada-BR-250-2-suplemento.pdf",
"sha256": "ea1b0dc6f5cf6e595f6d9a19e7042f398999e0087caef1785f6e8fac571b5f53"
},
{
"path": "docs/investmoz/simulations/source-archive/Lei-34-2007-BR52.pdf",
"sha256": "cdbef875ad7ccc1517b12faf5de0e893a70ed2ed81e33913cd6dc4a674474e6e"
},
{
"path": "docs/investmoz/simulations/source-archive/Lei-4-1994-BR37-3-suplemento.pdf",
"sha256": "210df9db8980d0a5690cd187a6eeec7e37a900d743e39062c16f4e0d12215c7e"
},
{
"path": "docs/investmoz/simulations/source-archive/Lei-4-2012-BR3.pdf",
"sha256": "b8845cef41dcbd9ba50c43dbfa743a56030dd3d348242a107e1ae30279eec121"
},
{
"path": "docs/investmoz/simulations/source-archive/Lei-7-1991-Partidos-IFES.pdf",
"sha256": "6b783a0bb3330688ef10660a58060d9724aa2da6a873d234fe902f0eaaeda42d"
},
{
"path": "docs/investmoz/simulations/source-archive/PT-MZ-CDT-1992.pdf",
"sha256": "d504e1024b28aee5c6acf4592acb96ec9c915a8414f67a61ab6984d6acf9df9d"
},
{
"path": "docs/investmoz/simulations/source-archive/PT-MZ-Protocolo-2009.pdf",
"sha256": "f353773dcf41f09f4daa6cf5ba5a5f7994f3e02615867c2dcffc61c3d901bf9b"
}
],
"new_official_source": {
"path": "docs/investmoz/simulations/source-archive/BM-Mercado-Monetario-MAIBOR-20261025.html",
"sha256": "00e8a7e0d3a1db4ca3beb56d5361b2084a0f1684f3d40fc1c203702a2190e063",
"url": "https://bancomoc.mz/pt/areas-de-actuacao/mercados/mercado-monetario/",
"review": "Official page expressly documentsdiscontinuedMAIBOR publication;MIMO/Prime not transported into fiscalrule",
"publication_import_activation": "Private source archive only;no library publication or fiscal activation;no scan claimed"
},
"source_mapping": [
"AT MRFTA19/39PDF1–2,visuallyreviewed;fields1–7,reserved8/9,singleperiod",
"Decreto9/2008 arts25/35 PDF6/8,p121/123;reusedacceptedofficialoriginal,notoldrevoked2002code"
],
"implementation_choices": [
"Completeness and absence of other tax rubrics are recorded facts to avoid treating this context subtotal as the entire declaration;not new approvers or organizational permissions",
"Manual CER/assessment references record applicable documents;no generic legal authenticity revalidation"
],
"active_changes": false,
"schema_migrations_configuration_permissions_changed": false,
"L5_integrally_completed": false,
"proofs": {
"outputs/irpc-mrfta/api.txt": "63e401f6422cdea294b4ae58bf1ff5c25e7c5dd87eccf81c249319bffbb06dd3",
"outputs/irpc-mrfta/AT-MRFTA-19-39.pdf1.png": "d0f4118a696ab940d2fd9a9cb792656dcba860b9b3a300c78334445d43d46a72",
"outputs/irpc-mrfta/AT-MRFTA-19-39.pdf2.png": "449ce541e08d886e7851e04c6b53ee44694d89508143da3c497d16db1a3f6a59",
"outputs/irpc-mrfta/build.txt": "ede4498073edeeadcbda9b1868b4be5574ad31a36d78664a1ebf8fb27ee89c86",
"outputs/irpc-mrfta/calculated.json": "b41a7dec0802a573d67f8e9b0ba42e54c4a20e935e844febd538752605d3446f",
"outputs/irpc-mrfta/Decreto-9-2008-BR16.pdf6.png": "ae8f0d02c2c01fafd1f390492ecf442defa094edd5612923c5f34b458f6c6392",
"outputs/irpc-mrfta/Decreto-9-2008-BR16.pdf8.png": "6097b3137f22a59c55cf1017643e0be4c0d6ba571e1d3c84331940988a4967b7",
"outputs/irpc-mrfta/dossier.html": "743a1960b2b70dfd4e5231f37bbf0799184f8f9d39c58c7569f66852ad1e3db5",
"outputs/irpc-mrfta/dossier.json": "864a2c9da690b086a2aa7c10ae18549d7d462f43a6f87826e6b1f57d8751e5c0",
"outputs/irpc-mrfta/dossier.txt": "a0473de355821758c4d320eea8e7af76a0d196a9cee652c71af723d6f69832f0",
"outputs/irpc-mrfta/frontend.txt": "707ef1a9c37fbeb53997027c1aff5c3a85c3358e40b3ed63512ddf93ded74b7a",
"outputs/irpc-mrfta/index.html": "6bb76e37ac20a73cb9ffcda14f1ac9db701aa01bc2ef2f62fc5adaaffb640ca8",
"outputs/irpc-mrfta/preparation.html": "c8855ab0bcbaa85e95149ca36d1b035b59a21760d3336c088b552c147eafc330",
"outputs/irpc-mrfta/recovered.json": "f7ef8221ad1ffba237f683eb2333e4bde8b076e00a459c131f8c81c96c3048f5",
"outputs/irpc-mrfta/types.txt": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"outputs/irpc-mrfta/ui-export.html": "c8855ab0bcbaa85e95149ca36d1b035b59a21760d3336c088b552c147eafc330",
"outputs/irpc-mrfta/ui-proof.json": "9d41f8a7f48e0a1f611bd0916e58e9dc6ffaa9760b281f056edbe2ccde115777",
"outputs/irpc-mrfta/ui.png": "ee9e6f5240d1f0b58569609bdb6c0c38a3cd11373db7b6ad0008b15fb229afad",
"outputs/irpc-mrfta/ui.txt": "59df712c7a7873532c4eab94fb582fd1e470803a5da1bfd68c884f8e8ad2386b"
}
}
@@ -0,0 +1,521 @@
{
"status": "installed_scoped_IRPC_reliefs_delta_verified",
"candidate_installed": "ce746d889d5711f23f4e0ac8d9802997b5e106f9",
"previous_installed": "2b33a1c343d192f80fb24aac2a27b2d375af5601",
"previous_release_preserved": "/var/www/investmoz-homologacao/releases/2b33a1c343d192f80fb24aac2a27b2d375af5601-23fd8416",
"authorization": "Tempo: disponibilizar entrega conjunta via Git,backup/restauro fresco e conferência do delta;preservar2b33a1c3,L3,ISPC,câmbio;L5 parcial.",
"fresh_backup": {
"stage": "/var/backups/investmoz-irpc-reliefs-delivery-20261025/backup",
"created_at": "2026-10-25T18:00:39Z",
"target": {
"database": "teste_investidor",
"port": 5432
},
"tables": 174,
"files_verified": 369,
"dump_sha256": "3dc0eeea326beff9a7780f3a11ef1ed23a58dbaab8382db975ddfb356d6206c8",
"files_sha256": "d0dc27540dd41131c0fbf923b15b05fefc81760d80bfb0bd8ce04bf80ec9eb50",
"restore_verified": false
},
"isolated_restore": {
"restore_verified": true,
"tables_verified": 174,
"container_network": "none",
"database": "irpc_l5_restore_20261025",
"port": 55441,
"source_dump_sha256": "3dc0eeea326beff9a7780f3a11ef1ed23a58dbaab8382db975ddfb356d6206c8",
"active_database_writes": 0
},
"preflight": {
"free_bytes": 6596661248,
"existing_pipeline_minimum_bytes": 5368709120,
"space_guard_satisfied": true,
"installed": {
"status": "installed",
"commit": "2b33a1c343d192f80fb24aac2a27b2d375af5601",
"release": "/var/www/investmoz-homologacao/releases/2b33a1c343d192f80fb24aac2a27b2d375af5601-23fd8416",
"previous_release": "/var/www/investmoz-homologacao/releases/5c0cb25cea7826811c389b55a64bcdf5e43b937f-32c4fd00",
"database_migrations": 0,
"content_imports": 0
},
"current_link": "/var/www/investmoz-homologacao/releases/2b33a1c343d192f80fb24aac2a27b2d375af5601-23fd8416",
"release_revision": {
"commit": "2b33a1c343d192f80fb24aac2a27b2d375af5601",
"branch": "main",
"built_at": "2026-10-25T17:27:10.176918+00:00",
"manifest_sha256": "75b06330161a813a10e3eb3a044e2ba4a924537dae381f97d0c8d4ee7295295a"
},
"service": "ExecMainStatus=0\nActiveState=inactive\nSubState=dead\n",
"configuration_keys": [
"repository",
"branch",
"target_url",
"legacy_root",
"state_root",
"release_root",
"current_link",
"shared_assets",
"php",
"schema_contract",
"ci_environment",
"postgis_image",
"test_database",
"qa_port",
"retention",
"webhook_secret_file"
],
"safe_paths": {
"repository": "nala/Investidor-V2",
"release_root": "/var/www/investmoz-homologacao/releases",
"postgis_image": "postgis/postgis@sha256:01a6a70e41e6c4467c8f55f6063555ed72db2d6662cd0d571040d42eadaeb6f6"
},
"pipeline_sha256": "adec1c17654f3a8658b83b134f1e75f95391b235738552721906c3236e388604"
},
"protected_delta": {
"previous_release_preserved": "/var/www/investmoz-homologacao/releases/2b33a1c343d192f80fb24aac2a27b2d375af5601-23fd8416",
"installed": {
"status": "installed",
"commit": "ce746d889d5711f23f4e0ac8d9802997b5e106f9",
"release": "/var/www/investmoz-homologacao/releases/ce746d889d5711f23f4e0ac8d9802997b5e106f9-5fdb1b51",
"previous_release": "/var/www/investmoz-homologacao/releases/2b33a1c343d192f80fb24aac2a27b2d375af5601-23fd8416",
"database_migrations": 0,
"content_imports": 0
},
"runtime_files_verified": 4,
"preserved_L3_ISPC_FX_runtime_files_verified": 28,
"documentary_files_verified": 29,
"persistent_files_and_env_verified": 369,
"tables_compared": 174,
"changed_tables": {
"cache": {
"before": {
"rows": 106,
"checksum": "df13c9623faded047b71df595d350ec7"
},
"after": {
"rows": 106,
"checksum": "cf7d9fdcfb64c81360cce81a5fa8c45d"
}
},
"sessions": {
"before": {
"rows": 15,
"checksum": "2c17940b66d6fd97d011edabbbf9ec47"
},
"after": {
"rows": 16,
"checksum": "4b7ab676493d6d1892d9933b3fde1780"
}
}
},
"preserved_counts": {
"published_fiches": 29,
"relations": 28,
"geometries": 408
},
"migration_count": 0,
"content_imports": 0,
"comparison_role": "postgres_before_and_after;sameexportedsnapshotbaseline;noapplicationRLSvisibilityinference",
"writes_by_verification": 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
},
"operational_delta": {
"status": "operational_delta_reconciled",
"comparison_identity": "postgres before/after;isolated restored baseline;no RLS visibility inference",
"cache": "queue:restart value/expiry and request-throttle counter/timer pair;no deleted cache rows",
"sessions": "anonymous sessions created by exact delivery verification user-agent",
"delta": {
"cache": {
"before_rows": 106,
"after_rows": 106,
"changes": [
{
"key_sha256": "8d6ea77eb4b34d86d207362d583cfa8bf2880b907becd9b00398964482c6b4bd",
"kind": "updated",
"fields": [
"expiration"
],
"key_pattern": "investemoz-cache-{hash}",
"is_queue_restart_key": false,
"expiration_unchanged": false,
"throttle_timer_pair": true
},
{
"key_sha256": "2f251473865d14b953236ab95b1521764754d76abd79b58ca7d38c96dfe2fab1",
"kind": "updated",
"fields": [
"value",
"expiration"
],
"key_pattern": "investemoz-cache-{hash}:timer",
"is_queue_restart_key": false,
"expiration_unchanged": false,
"throttle_timer_pair": true
},
{
"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
}
]
},
"sessions": {
"before_rows": 15,
"after_rows": 18,
"changes": [
{
"key_sha256": "4dd9d761c194f0c736e2b800add6792a790734c947d6de06bb7d91dc8778ec3b",
"kind": "added",
"fields": [
"id",
"ip_address",
"user_agent",
"payload",
"last_activity"
],
"anonymous_user": true,
"delivery_user_agent": true
},
{
"key_sha256": "93dba7d9de797b0301f4395afa891df1c9803a7abbd2f5c083ebb4a01569f75e",
"kind": "added",
"fields": [
"id",
"ip_address",
"user_agent",
"payload",
"last_activity"
],
"anonymous_user": true,
"delivery_user_agent": true
},
{
"key_sha256": "0208243bb27c67eee43773c1d4f3df5a1df5a24af6402a68de18bb518ce27beb",
"kind": "added",
"fields": [
"id",
"ip_address",
"user_agent",
"payload",
"last_activity"
],
"anonymous_user": true,
"delivery_user_agent": true
}
]
}
},
"business_tables_to_confirm_separately": 172,
"active_data_writes_by_comparison": 0,
"restore_network": "none"
},
"public_delta": {
"status": "installed_delta_verified",
"served_revision": {
"commit": "ce746d889d5711f23f4e0ac8d9802997b5e106f9",
"branch": "main",
"built_at": "2026-10-25T18:06:03.871601+00:00",
"manifest_sha256": "feb2dce3fc8845c6307c982323b3e3523b333a018189c47488f9fcba25490555"
},
"manifest_hash_matches": true,
"compiled_delta_markers": {
"Calcular e guardar retenções IRPC": [
"assets/services-rCdKRO1R.js"
],
"Dispensa de retenção": [
"assets/project-dossier-I7GMfANo.js",
"assets/services-rCdKRO1R.js"
],
"Limite convencional": [
"assets/project-dossier-I7GMfANo.js"
],
"Portugal–Moçambique — royalties, artigo 12": [
"assets/project-dossier-I7GMfANo.js"
],
"Informação insuficiente: documente residência convencional": [
"assets/project-dossier-I7GMfANo.js"
],
"Calcular e guardar apuramento trimestral ISPC": [
"assets/services-rCdKRO1R.js"
],
"Calcular e guardar restituição E": [
"assets/services-rCdKRO1R.js"
],
"Reutilizar selecção cambial": [
"assets/services-rCdKRO1R.js"
]
},
"assets": [
{
"file": "assets/simulation-form-BqVD91Ek.js",
"status": 200,
"sha256": "85cbd533c5bc92af5a448db71da006d4346d2d0adb09dde825db0aa19ad5048c"
},
{
"file": "assets/project-dossier-document-CR2W33oB.js",
"status": 200,
"sha256": "d5e23e7b8da99bdf4368991f54c6d4d5a12c206a18eeef754f0bc0f68183865c"
},
{
"file": "assets/project-dossier-I7GMfANo.js",
"status": 200,
"sha256": "2e5134e2163dbd23a83374d9e06e2ed68b3e37009f3ed07e97af7aa34f97e8ca"
},
{
"file": "assets/services-rCdKRO1R.js",
"status": 200,
"sha256": "ecd38dd17a4b371f2bc55167887206a98db8f066e6ab3f66b7a0b1938fdcf49d"
}
],
"simulator_status": 200,
"simulator_entry": "assets/app-GOBc44N3.js",
"existing_fiscal_availability": {
"data": {
"enabled": true,
"coverage_start": "2026-01-01",
"coverage_end": "2026-12-31",
"message": "IVA geral sobre base líquida disponível; enquadramento declarado."
}
},
"synthetic_private_writes": 0,
"method": "Read-only installedrevision/manifest/newcompiledcatalogueandreaders/simulatorentry;acceptedQAproofsreused"
},
"runtime_delta": [
{
"path": "resources/js/lib/irpc-relief-preparation.ts",
"sha256_LF": "e41a389ad0b5d3f33985e375b0c488eed8c2d185257eec1cf5f7d40fccd365d4"
},
{
"path": "resources/js/lib/irpc-withholdings.ts",
"sha256_LF": "75bf1c5940595e90d7a27355aeae6f9fdca4841f3e9ce3cb02103376bbd4d91b"
},
{
"path": "resources/js/components/simulation-irpc-withholdings.tsx",
"sha256_LF": "28dd35e40c5d8440adad01131714d324f0c7fcdfeb3f6ddd9d27750d563b5e4f"
},
{
"path": "resources/js/lib/project-dossier-report.ts",
"sha256_LF": "4523365e2d4a2fcd014e13393deb33a5ec4b9396ff580d8a237aa8c4f02265e4"
}
],
"preserved_runtime": [
{
"path": "resources/js/components/acquisition-beverage-restitution.tsx",
"sha256": "6f44897b8b12569314551df0022c26da57a165634087942a724293e66837558d"
},
{
"path": "resources/js/components/fiscal-calculation-gaps.tsx",
"sha256": "1dab6ceb73756d4e023096b3b2ba767aaa843ff6cc69046ea630ab449a00b0dd"
},
{
"path": "resources/js/components/fiscal-l3-preparation.tsx",
"sha256": "e84a7b1ccadaa1b5bd78d778a3287f96ccaf530855eef404786cd7aa3d321b73"
},
{
"path": "resources/js/components/simulation-acquisitions.tsx",
"sha256": "c206d4cfe575d6ba007aa2b425b5f898efa97e4162b8710e358b6ecfd9458542"
},
{
"path": "resources/js/components/simulation-operations.tsx",
"sha256": "a7e55972fa790063dcf11945055de2af46a77db9e509900af22ede7ec47bb4cf"
},
{
"path": "resources/js/lib/iva-acquisition-bc.ts",
"sha256": "b0ef55e0917bdb79f14b53ba9e95d73c08153befeb7d4ce73f81e6fb13992225"
},
{
"path": "resources/js/lib/iva-beverage-allocation.ts",
"sha256": "32968d198c7fe0c53ca89641c22d5749be6636be2c79127b867e4e3afd0dd40d"
},
{
"path": "resources/js/lib/iva-beverage-restitution.ts",
"sha256": "5c650cbd9469d5f99001f56fc4ff99ac973eb368036d4e96c6d171f1b220bd9e"
},
{
"path": "resources/js/lib/iva-calculation-gaps.ts",
"sha256": "2e7d84a0c196bd5b56fd67cc1a389d43d5041894184e675680a4de3cd10b31a2"
},
{
"path": "resources/js/lib/iva-operations.ts",
"sha256": "ec37b949f1233c5f5c14a87450c421ed29fabf1ed5b73fb32564564a2bc31f54"
},
{
"path": "resources/js/lib/l3-form-preparation.ts",
"sha256": "fc47f5fa5ec1420fbd847d0d081f00d99ebbb524164eef785b20877e0562f704"
},
{
"path": "resources/js/lib/ispc-residuals.ts",
"sha256": "40d5751c06c7c155ab4b7a17ee2d1653da2fbbf075807fbb8561cdbe19820955"
},
{
"path": "resources/js/components/simulation-ispc-residuals.tsx",
"sha256": "a9219d6f41ccbd0ce45659d4f6aa0fa1c169a685840f5cb1e18daeca377d5e18"
},
{
"path": "resources/js/lib/ispc-eligibility.ts",
"sha256": "44675c5efacdc1ae19505667611ba3b522d3043cebacc8754597c3a68ca89d25"
},
{
"path": "resources/js/components/simulation-ispc-quarter.tsx",
"sha256": "2c2555309b32d4261f14e7164f39314c9188129c2990357fc54090a2468dd2b4"
},
{
"path": "resources/js/components/simulation-ispc-withholding.tsx",
"sha256": "e0445d015b72859e48726959aa52686f79b1cf5c2ade29751efa83257068b878"
},
{
"path": "resources/js/lib/ispc-quarter.ts",
"sha256": "f66bd52e9cdfe1ddaa6b00a931465862b3d890b9be216b7a056740f027f24129"
},
{
"path": "resources/js/lib/ispc-withholding.ts",
"sha256": "6c31b240299edd3262efa28f7f3d5aee21123f94e30ef4ab90e26c35777cb6e4"
},
{
"path": "resources/js/components/simulation-ispc-eligibility.tsx",
"sha256": "f48dd4a8f87a994eda0447f81a463589905e5873b2e1044debac9d10825a880c"
},
{
"path": "resources/js/lib/iva-acquisitions.ts",
"sha256": "cce283b1d0a07fb383fcb527225f4dd5bec829010a662cbaae1d60e595221977"
},
{
"path": "resources/js/lib/iva-modelo-a.ts",
"sha256": "c641619a05613c1620385baad0e9ffbf810dd970aafb30e12b3a7f9a3bf11846"
},
{
"path": "resources/js/lib/iva-fx-selection.ts",
"sha256": "9aa07ad443bc777b80510a3b5bd6aff9ddb9814d2450f3c690e531e74d71d998"
},
{
"path": "resources/js/components/simulation-fx-selection.tsx",
"sha256": "39bb6d87d404745b0b66349bc11626545a3672ae27321ce9b4658416592059bf"
},
{
"path": "resources/js/lib/business-financial-plan.ts",
"sha256": "c20274722d1ffcd0360f041a6cacb5d868fe0621cb27a1aca6e06826612c3ebb"
},
{
"path": "resources/js/lib/iva-currency-events.ts",
"sha256": "cc9f2045d302d7987310da4006cc250fc5c99a27397918d166d649bfc32fab08"
},
{
"path": "resources/js/lib/irpc-fx.ts",
"sha256": "5c4a7a477429f8d775acf6db3e117008468f5554e5731dec81f60fbb75e948fb"
},
{
"path": "resources/js/components/simulation-form.tsx",
"sha256": "01d20f4761933a3e4dd7c776bd4adcf9610ac0435d918a1998ae9f0c3ce3f234"
},
{
"path": "resources/js/lib/project-dossier.ts",
"sha256": "df8b224b9472449711d0b69a873fa75f9cc9f4d84dce1a314fc576fae9c6eae2"
}
],
"accepted_QA_proofs_reused": {
"routes": "normal Wayfinder generate --with-form through guarded QA wrapper",
"types": "tsc --noEmit passed",
"build": "complete QA build passed; existing optional-font/chunk-size notices only",
"frontend": "9 new tests passed",
"compatibility": "5 directed IRPC cases passed;no repeated L3/ISPC/FX suites",
"api": "1test/17assertions;private endpoints;55441/simulator_fiscal_delta_20261021_test;DatabaseTransactions rollback",
"report": "retentions preparation and financial dossier from actual API recovered sample",
"ui": {
"calculation": true,
"localSaveRecovery": true,
"privateApiProof": "IrpcReliefsDraftTest, separate",
"report": true,
"insufficientInformation": true,
"errors": []
}
},
"installer_report": {
"status": "installed",
"commit": "ce746d889d5711f23f4e0ac8d9802997b5e106f9",
"release": "/var/www/investmoz-homologacao/releases/ce746d889d5711f23f4e0ac8d9802997b5e106f9-5fdb1b51",
"previous_release": "/var/www/investmoz-homologacao/releases/2b33a1c343d192f80fb24aac2a27b2d375af5601-23fd8416",
"database_migrations": 0,
"content_imports": 0,
"requested_commits": [
"ce746d889d5711f23f4e0ac8d9802997b5e106f9"
],
"finished_at": "2026-10-25T18:06:07.393002+00:00"
},
"space": {
"status": "dependencies_archived_and_verified",
"versions": 5,
"free_before": 5281300480,
"free_after": 6596743168,
"receipt": "/var/backups/investmoz-build-dependencies-irpc-reliefs-20261025/receipt.json",
"protected": [
"/var/www/investmoz-homologacao/releases/2b33a1c343d192f80fb24aac2a27b2d375af5601-23fd8416",
"/var/www/investmoz-homologacao/releases/5c0cb25cea7826811c389b55a64bcdf5e43b937f-32c4fd00"
],
"active_or_previous_release_modified": false,
"source_data_backups_deleted": 0
},
"remaining": [
"Other68paragraphs and digital/resident-specific cases",
"Other countries,treaties,EP-related and special-relationship excess cases",
"Dividends,interest-specific treaty branches",
"Late-payment interest,rectifications,aggregation,MRFTA/CER/form filling",
"External treaty claiming formalities are not declared satisfied by this calculation"
],
"database_migrations": 0,
"content_imports": 0,
"L5_integrally_completed": false,
"scope": "Only demonstrated68d/68f and qualifiedPT-MZ12royalties/equipment;nextinterest/MRFTAincrementstaysQA",
"evidence": {
"backup.txt": "53019c43c14005ca429e747a38d909b175218490add7fa416d7b3c387211db3b",
"compact-dependencies.txt": "4f087279b4760e71de0621da342d2dea14dbf38272816e938d5b9e6c171afe1d",
"installed-protected.txt": "0dff5ae05c6e73542c99fa14f0c06fcf87e7c096d29ff80b1517d21b57404504",
"installed-public.txt": "ae35a04719d1f05478b4936d4d563f9050c47f1db7d9b69610322e7653878858",
"integration.txt": "1c4d1be9b3a758a39529c69ebf87eda0b2dea36aef7622ba550ca7021dc41b6f",
"operational-delta.txt": "711f26fd0e56b8b52cef90d6245bbf32f7ec1758f5af8fcb8112241a919dbd94",
"preflight.txt": "90411a15339db664782a58139072c0fec52331d5a336348d260d6e336f2d299e",
"restore.txt": "a199e138c500d1f94da75ab38022932aa37232585ca2a6914eaf1dea91accf1a",
"schema-permissions-after.txt": "76b5a4d8af11e4898a205062766cfb7ab6ec0783200e77e60a1927aa1ca18597",
"schema-permissions-before.txt": "0ab7c964792acdd8917bce256c472e55e034b103500a58b49f020b5084823ed7",
"status1.txt": "595260a11ec30b4e7ef3e2a2aad11a178077283300f3572270a8d104307fe3ad",
"status2.txt": "4de7380a350e99585cbeea3646331759892b469aae5fd121effdb4f18fa39922",
"status3.txt": "49c26c68447722bfafed48930cd7cf0da62a088d2f66c05b3ef0ed90c3dc32b8",
"status4.txt": "7cbb15d32c6b6455b60df644f045880854c7acc4e21dfce0dd77e0df88c2a97e",
"status5.txt": "7cbb15d32c6b6455b60df644f045880854c7acc4e21dfce0dd77e0df88c2a97e",
"status6.txt": "a1224e56d81b8fbed83555dd413084cb9fff74a6b4f88960ea83ee6faec09f2f",
"status7.txt": "e2b53e3916f2a77a530f663d971d66fe495aefd1159d37b4e7c595dd93f3bf0c"
}
}
@@ -156,3 +156,10 @@ Novos originais privados preservados, sem publicação/importação automáticas
- docs/investmoz/simulations/source-archive/PT-MZ-Protocolo-2009.pdf;SHA256`f353773dcf41f09f4daa6cf5ba5a5f7994f3e02615867c2dcffc61c3d901bf9b`;origem:https://files.dre.pt/1s/2009/05/08900/0275202756.pdf.
Referências oficiais adicionais: https://diariodarepublica.pt/dr/detalhe/resolucao-assembleia-republica/36-1992-408879 ; https://diariodarepublica.pt/dr/detalhe/resolucao-assembleia-republica/36-2009-607572 ; https://dcjri.ministeriopublico.pt/instrumento/protocolo-entre-republica-portuguesa-e-republica-de-mocambique-que-reve-convencao-para . A aceitação das fontes oficiais permanece, sem revalidação genérica.
## 25/10/2026 — MRFTA e juros compensatórios: correspondência concreta
Reutilizados os29 originais aceites,sem perdas por hash. MRFTA19/39AT,PDF1–2,semAcroForms:quadros1–7 e instruções conferidos visualmente,8/9 reservados. O incremento preenche um mapaHTML correspondente,sem alterar o original ou alegar submissão. Rubricas normais de capitais/prediais e liberatórias IRPC intelectuais/equipamento/capitais/serviços correspondidas pelo nome e natureza;digital/especiais não presumidos. CER permanece dado transcrito com referência,não número de campo da guia antiga.
Decreto9/2008,PDF6/p121,art25(1–3),ePDF8/p123,art35:juros compensatórios e distinção da natureza da retenção. A página oficial BM do Mercado Monetário explica a descontinuidade da MAIBOR em consequência do acordo de17/05/2017. Snapshot integral preservado em `source-archive/BM-Mercado-Monetario-MAIBOR-20261025.html`,SHA256`00e8a7e0d3a1db4ca3beb56d5361b2084a0f1684f3d40fc1c203702a2190e063`. Origem:https://bancomoc.mz/pt/areas-de-actuacao/mercados/mercado-monetario/ . Não usar MIMO ou Prime como sucessor fiscal por inferência. Nenhuma taxa2026 genérica activada;liquidação documentada pode ser transcrita com período/data/prova. Sem nova validação de credibilidade oficial,sem publicação/importação automáticas,sem scan declarado. Inventário completo e hashes no recibo `irpc-juros-mrfta-qa-20261025.json`.
@@ -5887,3 +5887,25 @@ Reutilizados contraparte, residência/EP, documento, qualificação, montante e
**Saldo na mesma matriz:** restantes alíneas68, categorias digitais/residentes não correspondidas, outras convenções/Estados e casos com EP, dividendos/juros ou excesso por relações especiais mantêm correspondência própria. Juros de mora, rectificações, agregação, rubricas/CER e preenchimento do MRFTA permanecem discriminados. O cálculo convencional não declara cumpridas as formalidades de accionamento, recepção ou aceitação AT. Não são transportados formulários portugueses21-RFI para obrigações moçambicanas. Pendências condicionam apenas os casos dependentes. L5, IRPC e IRPS não estão integralmente concluídos. L3/ISPC/câmbio não foram retestados; as suas provas são reutilizadas. Entrega conjunta preparada apenas para os recortes demonstrados. Instalação futura segue o procedimento Git e os controlos de destino/backup/restauro já existentes.
Recibo: `docs/investmoz/simulations/irpc-l5-reliefs-qa-20261025.json`; provas locais em `outputs/irpc-reliefs/`.
## 25/10/2026 — dispensas IRPC e convenção PT–MZ instaladas pelo Git
Instalada `ce746d88`, com `2b33a1c3` preservada como release anterior. Backup fresco2026-10-25T18:00:39Z:174 tabelas e369 ficheiros/configuração;restauro isolado em contentor sem rede/55441 passou. Quatro ficheiros de execução conferidos,28 protegidos L3/ISPC/câmbio e29 originais preservados. Schema,permissões e configuração iguais;zero migrações/importações. As172 tabelas de negócio mantêm checksums;apenas cache operacional e sessões anónimas das verificações foram reconciliadas. O mínimo de espaço do instalador foi reposto pelo arquivo e verificação de dependências de5 releases antigas,sem tocar na actual/anterior nem apagar dados/backups.
Manifesto público/revisão servida,8marcadores compilados e simuladorHTTP200 conferidos. Provas QA aceites reutilizadas;CI habitual do procedimento Git passou. Sem ampliar a cobertura:68(d/f) e royalties/uso de equipamento PT–MZ nos casos efectivamente demonstrados. Juros,MRFTA e restantes casos continuam na mesma matriz;L5 permanece parcial. Seguimento em QA separado,sem instalar novos cálculos. Recibo:`irpc-l5-reliefs-delivery-20261025.json`.
## 25/10/2026 — juros e preenchimento MRFTA: incremento QA após ce746d88
O portal mantém a entrega instalada `ce746d88`, com `2b33a1c3` preservada;recibo de instalação `irpc-l5-reliefs-delivery-20261025.json`. Seguimento isolado em `codex/irpc-juros-mrfta-qa-20261025`,sem nova instalação. Reutilizadas as provas aceites;29 originais e29 ficheiros protegidos conferidos por hash. Sem delta de schema,configuração ou permissões.
**Preenchimento:** quadros1–7 do MRFTA de preparação,um mês/ano;identidade fiscal reutilizável entre livros sem sobrescrever edições nem misturar NUIT distintos. Base,taxa,imposto,documentos e entregas vêm do cálculo IRPC guardado. Agrupamento apenas da mesma rubrica/taxa/CER. Retenções sofridas,dispensas e outros períodos ficam separados. CER requer referência aplicável;os números de campos do M/39 antigo não são convertidos em códigos de receita. Rubricas digitais/especiais desconhecidas e múltiplas taxas na mesma rubrica permanecem discriminadas. Quadros8/9 não são assinados/preenchidos pelo portal. Exportação HTML de preparação,não alteração do PDF oficial nem declaração submetida.
**Juros:** Decreto9/2008,25/35,PDF6/8,p121/123 identifica MAIBOR12meses+2p.p.;BM documenta a descontinuidade da publicação. Não foi obtido parâmetro fiscal sucessor aplicável2026;MIMO/Prime não o substituem. Implementada transcrição de juros compensatórios de liquidação documentada,com período,data,montante e referência;ou ausência de atraso quando entrega integral e datas/prazo documentados coincidem. Informação insuficiente não vira zero. Juros de mora continuam por implementar. O subtotal do principal permanece disponível;total do formulário fica indeterminado enquanto faltarem juros,CER,completude ou situação de outras rubricas (IRPS/autónoma),sem isenções ou créditos presumidos. A completude é um dado da preparação,não novo aprovador organizacional.
**Prova do delta:**12 testes novos+1 de apresentação IRPC;TypeScript e compilação QA passaram. API privada1teste/14verificações com rollback no55441;preparação→gravação→recuperação→relatório/dossier. UI real em exemplo sintético:preparação,gravação/recuperação local,informação insuficiente e download HTML;prova API separada. CER999 é fictício de teste,não taxa/código oficial activado. Exemplo:principal20000MZN,entrega3000,juros documentados500,total17500;alterar provas conserva histórico e desactualiza a preparação. Não foram repetidas manualmente suites L3/ISPC/câmbio.
**Saldo:**cálculo automático dos juros e taxa aplicável2026,juros de mora,catálogo CER integral,rubricas digitais/especiais,mistura de taxas e obrigações IRPS/autónomas fora do recorte. Limita apenas as parcelas dependentes;L5/IRPC/IRPS continuam parciais. Recibo `irpc-juros-mrfta-qa-20261025.json`;provas locais `outputs/irpc-mrfta/`.
Reprodução QA: `php tools/irpc-mrfta-qa.php routes`; `node --test tests/frontend/irpc-mrfta.test.mjs`; `php tools/irpc-mrfta-qa.php test`. PHP8.5,Node24,dependências existentes;alvo e identidadeQA guardados antes de fornecedores/testes.
File diff suppressed because one or more lines are too long
@@ -0,0 +1,26 @@
import {useState} from 'react';
import {irpcCurrent,type IrpcBook} from '@/lib/irpc-withholdings';
import {newMrftaInput,recordMrfta,mrftaHtml,mrftaReportRows,type MrftaInput} from '@/lib/irpc-mrfta';
export function SimulationIrpcMrfta({draft,book,save}:{draft:Record<string,unknown>;book:IrpcBook;save:(book:IrpcBook)=>void}){
const [error,setError]=useState(''),i=book.mrfta??newMrftaInput();
const change=(patch:Partial<MrftaInput>)=>save({...book,mrfta:{...i,...patch}});
const interest=(patch:Partial<MrftaInput['interest']>)=>change({interest:{...i.interest,...patch}});
const field=(key:'period'|'nuit'|'name'|'commercialName'|'taxArea'|'address'|'contact'|'completenessRef'|'otherRubricsRef',label:string,type='text')=><label className="block">{label}<input className="block rounded border p-2" type={type} value={i[key]} onChange={e=>change({[key]:e.target.value})}/></label>;
const select=(key:'declaration'|'timing'|'completeness'|'otherRubrics',label:string,options:Array<[string,string]>)=><label className="block">{label}<select value={i[key]} onChange={e=>change({[key]:e.target.value})}>{options.map(([v,l])=><option key={v} value={v}>{l}</option>)}</select></label>;
const periods=[...new Set(book.result?.rows.map(r=>r.period).filter(Boolean)??[])];
const identities=Object.entries((JSON.parse(String(draft.modelo_a_snapshot||'{}')).irpcWithholdings??{}) as Record<string,IrpcBook>).filter(([,b])=>b.mrfta?.nuit&&b.mrfta?.name&&(!i.nuit.trim()||i.nuit===b.mrfta.nuit)&&(!i.name.trim()||i.name===b.mrfta.name)&&(['nuit','name','commercialName','taxArea','address','contact'] as const).some(key=>!i[key].trim()&&b.mrfta![key].trim()));
return <fieldset aria-label="Preparação MRFTA e juros" className="space-y-3 rounded border p-4"><legend>MRFTA — preenchimento e juros compensatórios</legend><p>Reutiliza o cálculo guardado, documentos e entregas IRPC. Um mês/ano por declaração. A retenção sofrida e as dispensas ficam separadas. Quadros 8/9 reservados à assinatura e aos serviços; não há submissão ou aceitação AT.</p>
{identities.map(([scope,b])=><button key={scope} type="button" onClick={()=>{const p:Partial<MrftaInput>={};for(const key of ['nuit','name','commercialName','taxArea','address','contact']as const)if(!i[key].trim())p[key]=b.mrfta![key];change(p);}}>Reutilizar identidade fiscal {b.mrfta!.name} ({scope}) nos campos vazios</button>)}
<div className="grid gap-3 md:grid-cols-2">{field('period','Quadro 2 — mês/ano','month')}<label>Período existente<select value="" onChange={e=>{if(e.target.value)change({period:e.target.value});}}><option value="">Seleccione</option>{periods.map(p=><option key={p} value={p}>{p}</option>)}</select></label>{select('declaration','Quadro 1 — retenção na fonte',[['','Por identificar'],['initial','Inicial'],['replacement','Substituição']])}{select('timing','Quadro 2 — situação do prazo',[['','Por esclarecer'],['within','Dentro do prazo'],['late','Fora do prazo']])}{field('nuit','Quadro 3 — NUIT')}{field('taxArea','Quadro 3 — área fiscal')}{field('name','Quadro 4 — nome do sujeito passivo')}{field('commercialName','Quadro 5 — nome comercial')}{field('address','Quadro 6 — domicílio fiscal')}{field('contact','Quadro 6 — contacto')}{select('completeness','Retenções do sujeito passivo neste período conferidas, incluindo demais livros',[['','Por esclarecer'],['yes','Sim'],['no','Ainda não']])}{field('completenessRef','Referência da reconciliação do período')}{select('otherRubrics','IRPS, tributação autónoma e outras rubricas fora deste recorte',[['','Por esclarecer'],['none','Ausência de outros valores devidos documentada'],['pending','Existem ou estão por apurar']])}{field('otherRubricsRef','Documento da situação das outras rubricas')}</div>
<h5>Quadro 7 — CER das rubricas correspondidas</h5><p>Base, taxa e imposto vêm do cálculo guardado. O CER é transcrito da referência aplicável; os números de campos da guia antiga não são automaticamente códigos de receita.</p>
{book.result?.inputs.filter(r=>r.direction==='paid'&&book.result?.rows.some(v=>v.id===r.id&&v.period===i.period&&v.status==='calculated'&&v.treatment!=='dispensed')).map(r=>{const code=i.codes.find(c=>c.rowId===r.id)??{rowId:r.id,cer:'',reference:''};const update=(p:Partial<typeof code>)=>change({codes:[...i.codes.filter(c=>c.rowId!==r.id),{...code,...p}]});return <div key={r.id} className="grid gap-2 md:grid-cols-3"><span>{r.documentRef} — {r.counterparty}</span><label>CER<input value={code.cer} onChange={e=>update({cer:e.target.value})}/></label><label>Documento / referência do CER<input value={code.reference} onChange={e=>update({reference:e.target.value})}/></label></div>;})}
<h5>Juros compensatórios — separados do principal</h5><p>Regulamento IRPC, artigos 25/35: a taxa refere MAIBOR de 12 meses + 2 pontos. O Banco de Moçambique documenta a descontinuidade da MAIBOR. O portal não adopta MIMO ou Prime como substituto nem calcula juros de mora.</p>
<label>Tratamento dos juros<select value={i.interest.mode} onChange={e=>interest({mode:e.target.value as MrftaInput['interest']['mode']})}><option value="">Informação insuficiente / cálculo automático por resolver</option><option value="assessment">Transcrever liquidação documentada de juros compensatórios</option><option value="timely">Conferir entrega integral dentro do prazo documentado</option></select></label>
{i.interest.mode&&<><label>Período documentado dos juros / entregas<input type="month" value={i.interest.period} onChange={e=>interest({period:e.target.value})}/></label><label>Documento da liquidação / reconciliação<input value={i.interest.reference} onChange={e=>interest({reference:e.target.value})}/></label></>}
{i.interest.mode==='assessment'&&<><label>Montante dos juros em MZN<input value={i.interest.amount} onChange={e=>interest({amount:e.target.value})}/></label><label>Data da liquidação<input type="date" value={i.interest.date} onChange={e=>interest({date:e.target.value})}/></label></>}
{i.interest.mode==='timely'&&<><label>Prazo aplicável documentado<input type="date" value={i.interest.dueDate} onChange={e=>interest({dueDate:e.target.value})}/></label><label>Fonte do prazo, incluindo eventual ajuste de calendário<input value={i.interest.dueReference} onChange={e=>interest({dueReference:e.target.value})}/></label></>}
<button type="button" className="rounded border p-2" onClick={()=>{try{save(recordMrfta(book,irpcCurrent(draft,book)));setError('');}catch(e){setError(String(e));}}}>Preparar e guardar MRFTA</button><button type="button" className="rounded border p-2" onClick={()=>{try{const html=mrftaHtml(book,irpcCurrent(draft,book)),u=URL.createObjectURL(new Blob([html],{type:'text/html;charset=utf-8'})),a=document.createElement('a');a.href=u;a.download='MRFTA-'+(i.period||'periodo')+'-preparacao.html';a.click();URL.revokeObjectURL(u);setError('');}catch(e){setError(String(e));}}}>Exportar preenchimento MRFTA</button>
{error&&<p role="alert">{error}</p>}{mrftaReportRows(book,irpcCurrent(draft,book)).map((r,n)=><p key={n}><strong>{r[1]}</strong>: {r[2]}<small>{r[3]}</small></p>)}
</fieldset>;
}
@@ -1,3 +1,4 @@
import {SimulationIrpcMrfta} from '@/components/simulation-irpc-mrfta';
import {irpcReliefRules,newIrpcReliefPreparation,prepareIrpcRelief} from '@/lib/irpc-relief-preparation';
import {SimulationFxSelection} from '@/components/simulation-fx-selection';
import {irpcFxSources,irpcFxRow,updateIrpcFx,type IrpcFxAmount} from '@/lib/irpc-fx';
@@ -27,6 +28,7 @@ export function SimulationIrpcWithholdings({draft,onChange}:{draft:Record<string
</fieldset>)}
<h5>Pagamentos e comprovativos reutilizáveis neste livro</h5><p>Uma referência é registada uma vez e repartida entre rendimentos sem exceder o montante. Pagamento ao fornecedor não é entrega à AT; pagamentos de IVA conservam o seu vínculo anterior e não são automaticamente usados em IRPC.</p>{book.payments.map(p=><fieldset key={p.id} className="grid gap-2 rounded border p-2 md:grid-cols-3"><label>Natureza<select value={p.kind} onChange={e=>paymentChange(p.id,{kind:e.target.value as IrpcPayment['kind']})}><option value="remittance">Entrega IRPC à AT</option><option value="withholding_proof">Comprovativo de retenção sofrida</option><option value="beneficiary">Pagamento ao beneficiário</option></select></label>{(['reference','date','amount','currency']as const).map(k=><label key={k}>{({reference:'Referência',date:'Data',amount:'Montante',currency:'Moeda'})[k]}<input type={k==='date'?'date':'text'} value={p[k]} onChange={e=>paymentChange(p.id,{[k]:e.target.value})}/></label>)}{p.currency!=='MZN'&&(p.kind==='withholding_proof'?renderFx(p.id,p,patch=>paymentChange(p.id,patch)):<p>A entrega IRPC à AT requer o montante MZN do comprovativo; converter o pagamento ao beneficiário não prova entrega do imposto.</p>)}</fieldset>)}<button type="button" onClick={()=>save({...book,payments:[...book.payments,{id:uid(),kind:'remittance',reference:'',date:'',amount:'',currency:'MZN'}]})}>Registar pagamento/prova para reutilização</button>
<div className="space-x-3"><button type="button" className="rounded border p-2" onClick={()=>{try{save(recordIrpc(draft,book));}catch(e){setError(String(e));}}}>Calcular e guardar retenções IRPC</button><button type="button" className="rounded border p-2" onClick={()=>{try{const html=irpcPreparationHtml(draft),u=URL.createObjectURL(new Blob([html],{type:'text/html;charset=utf-8'})),a=document.createElement('a');a.href=u;a.download='IRPC-L5-preparacao.html';a.click();URL.revokeObjectURL(u);setError('');}catch(e){setError(String(e));}}}>Exportar preparação IRPC</button></div>
{irpcReportRows(draft).map((r,n)=><p key={n}><strong>{r[1]}</strong>: {r[2]}<small className="block">{r[3]}</small></p>)}<p>Formalidades externas:MRFTA19/39 e guia M/39 preservados. CER/rubricas digitais e remissão67(7)→62(6) por corresponder. Exportar não submete nem obtém aceitação AT. L5 continua aberto;ISPC e IRPS conservam os seus limites anteriores.</p></section>;
<SimulationIrpcMrfta draft={draft} book={book} save={save}/>
{irpcReportRows(draft).slice(0,book.result?.rows.length??0).map((r,n)=><p key={n}><strong>{r[1]}</strong>: {r[2]}<small className="block">{r[3]}</small></p>)}<p>Formalidades externas:MRFTA19/39 e guia M/39 preservados. CER/rubricas digitais e remissão67(7)→62(6) por corresponder. Exportar não submete nem obtém aceitação AT. L5 continua aberto;ISPC e IRPS conservam os seus limites anteriores.</p></section>;
}catch(e){return<p role="alert">{String(e)}</p>;}
}
+88
View File
@@ -0,0 +1,88 @@
import type {IrpcBook, IrpcResult, IrpcRow} from './irpc-withholdings.ts';
import {addFractions,decimalFraction,fraction,fractionPresentation,subtractFractions,type FiscalFraction} from './fiscal-rational.ts';
export type MrftaInput = {
version:1; period:string; declaration:''|'initial'|'replacement'; timing:''|'within'|'late';
nuit:string; name:string; commercialName:string; taxArea:string; address:string; contact:string;
completeness:''|'yes'|'no'; completenessRef:string;
otherRubrics:''|'none'|'pending'; otherRubricsRef:string;
codes:Array<{rowId:string;cer:string;reference:string}>;
interest:{mode:''|'assessment'|'timely';period:string;amount:string;reference:string;date:string;dueDate:string;dueReference:string};
};
type MrftaLine={ids:string[];documents:string[];references:string[];rubric:string;cer:string;rate:number;base:FiscalFraction;tax:FiscalFraction;paid:FiscalFraction|null};
export type MrftaModel={period:string;input:MrftaInput;lines:MrftaLine[];excluded:Array<{id:string;reason:string}>;pending:string[];principal:FiscalFraction;paid:FiscalFraction|null;interest:FiscalFraction|null;total:FiscalFraction|null;interestReason:string;source:string};
export type MrftaRecord={key:string;createdAt:string;model:MrftaModel};
export type MrftaStore={mrfta?:MrftaInput;mrftaResult?:MrftaRecord;mrftaHistory?:MrftaRecord[]};
export const newMrftaInput=():MrftaInput=>({version:1,period:'',declaration:'',timing:'',nuit:'',name:'',commercialName:'',taxArea:'',address:'',contact:'',completeness:'',completenessRef:'',otherRubrics:'',otherRubricsRef:'',codes:[],interest:{mode:'',period:'',amount:'',reference:'',date:'',dueDate:'',dueReference:''}});
export const mrftaSource='AT MRFTA19/39,PDF1–2:quadros1–7;um mês/ano por declaração. Decreto9/2008 arts25/35,PDF6/8,p121/123:juros compensatórios separados. MAIBOR12meses+2p.p.na data da liquidação;publicação MAIBOR descontinuada segundo BM. Sem substituir por MIMO/Prime nem presumir zero. CER exige referência específica;remissões antigas do M/39 não renumeradas no original.';
const date=(s:string)=>/^20\d{2}-\d{2}-\d{2}$/.test(s)&&Number.isFinite(Date.parse(s))&&new Date(s).toISOString().slice(0,10)===s;
const zero=()=>fraction(0n,1n);
const isZero=(v:FiscalFraction)=>BigInt(v.numerator)===0n;
const negative=(v:FiscalFraction)=>BigInt(v.numerator)<0n;
const labels:Record<string,string>={period:'mês/ano',declaration:'tipo de declaração',timing:'dentro/fora do prazo',nuit:'NUIT',name:'nome do sujeito passivo',taxArea:'área fiscal',address:'domicílio fiscal'};
export function validateMrfta(b:MrftaStore){
const i=b.mrfta;
if(i&&(!['','none','pending'].includes(i.otherRubrics)||typeof i.otherRubricsRef!=='string'))throw Error('Âmbito de outras rubricas MRFTA não recuperável.');
if(i&&(i.version!==1||!['','initial','replacement'].includes(i.declaration)||!['','within','late'].includes(i.timing)||!['','yes','no'].includes(i.completeness)||['period','nuit','name','commercialName','taxArea','address','contact','completenessRef'].some(k=>typeof i[k as keyof MrftaInput]!=='string')||!Array.isArray(i.codes)||i.codes.some(c=>!c||['rowId','cer','reference'].some(k=>typeof c[k as keyof typeof c]!=='string'))||new Set(i.codes.map(c=>c.rowId)).size!==i.codes.length||!i.interest||!['','assessment','timely'].includes(i.interest.mode)||['period','amount','reference','date','dueDate','dueReference'].some(k=>typeof i.interest[k as keyof typeof i.interest]!=='string')))throw Error('Preparação MRFTA não recuperável;originais conservados.');
if(b.mrftaResult&&(!b.mrftaResult.model||typeof b.mrftaResult.key!=='string'||!Array.isArray(b.mrftaResult.model.lines)||!Array.isArray(b.mrftaResult.model.pending))||b.mrftaHistory!==undefined&&!Array.isArray(b.mrftaHistory))throw Error('Histórico MRFTA não recuperável.');
}
function rubric(r:IrpcRow,v:IrpcResult){
if(v.nature==='on_account')return r.kind==='rent'?'Taxa normal — Rendimentos prediais':r.kind==='capital'?'Taxa normal — Rendimentos de capitais e mais valias':null;
if(v.nature!=='definitive')return null;
return ({royalty:'IRPC liberatório — Actividade intelectual ou industrial / prestação de informações',equipment:'IRPC liberatório — Uso ou concessão do uso de equipamento',capital:'IRPC liberatório — Aplicação de capitais e rendimentos prediais',services:'IRPC liberatório — Comissões por intermediação de contratos ou prestação de serviços'} as Record<string,string>)[r.kind]??null;
}
function key(b:IrpcBook&MrftaStore,current:boolean){return JSON.stringify(['MRFTA-L5/1',b.result,b.mrfta,current]);}
export function mrftaCurrent(b:IrpcBook&MrftaStore,current:boolean){return !!b.mrftaResult&&b.mrftaResult.key===key(b,current);}
export function prepareMrfta(b:IrpcBook&MrftaStore,current:boolean):MrftaModel{
validateMrfta(b);const i=b.mrfta??newMrftaInput(),pending:string[]=[],excluded:MrftaModel['excluded']=[],lines:MrftaLine[]=[];
for(const k of Object.keys(labels))if(!String(i[k as keyof MrftaInput]).trim())pending.push('Falta '+labels[k]+'.');
if(!/^2026-(0[1-9]|1[0-2])$/.test(i.period))pending.push('Mês/ano inválido ou fora do recorte2026.');
if(i.nuit&&!/^\d{9}$/.test(i.nuit))pending.push('NUIT deve ter nove algarismos;confira a identidade fiscal.');
if(i.completeness!=='yes'||!i.completenessRef.trim())pending.push('Conferir completude das retenções deste sujeito passivo/período, incluindo os demais livros;este contexto não é automaticamente a declaração completa.');
if(i.otherRubrics!=='none'||!i.otherRubricsRef.trim())pending.push('IRPS,tributação autónoma e outras rubricas fora deste recorte não são zero presumido;documente a ausência de outros valores devidos ou mantenha o total indeterminado.');
if(!current||!b.result)pending.push('Cálculo das retenções desactualizado ou inexistente;guarde o cálculo actual antes da preparação.');
for(const v of b.result?.rows??[]){
const r=b.result!.inputs.find(r=>r.id===v.id);
if(!r){pending.push('Entrada do resultado ausente: '+v.id);continue;}
if(r.direction==='received'){excluded.push({id:r.id,reason:'Retenção sofrida; não é imposto a entregar pelo titular.'});continue;}
if(v.period&&v.period!==i.period){excluded.push({id:r.id,reason:'Outro período: '+v.period});continue;}
if(v.treatment==='dispensed'&&v.status==='calculated'){excluded.push({id:r.id,reason:'Dispensa documentada;sem imposto a transcrever.'});continue;}
if(v.status!=='calculated'||!v.tax||!v.base||v.rate===null||!v.period){pending.push(`${r.documentRef}: rendimento indeterminado;${v.reason}`);continue;}
const name=rubric(r,v);if(!name){pending.push(`${r.documentRef}: rubrica MRFTA por corresponder;categoria digital/especial não copiada para serviços comuns.`);continue;}
const code=i.codes.find(c=>c.rowId===r.id);if(!code?.cer.trim()||!/^\d+$/.test(code.cer.trim())||!code.reference.trim())pending.push(`${r.documentRef}: CER numérico e referência aplicável por identificar;sem código presumido.`);
const cer=code?.cer.trim()??'',line=lines.find(l=>l.rubric===name&&l.rate===v.rate&&l.cer===cer);
const reference=`CER:${code?.reference??''};contraparte:${r.counterparty};qualificação:${r.qualificationRef};benefício:${r.reliefPreparation?.evidence??r.reliefRef};residência fiscal:${r.reliefPreparation?.residenceDocument??''}`;
if(line){line.ids.push(r.id);line.documents.push(r.documentRef);line.references.push(reference);line.base=addFractions([line.base,v.base]);line.tax=addFractions([line.tax,v.tax]);line.paid=line.paid&&v.settled?addFractions([line.paid,v.settled]):null;}
else lines.push({ids:[r.id],documents:[r.documentRef],references:[reference],rubric:name,cer,rate:v.rate,base:v.base,tax:v.tax,paid:v.settled});
}
for(const line of lines)if(lines.some(other=>other!==line&&other.rubric===line.rubric))pending.push(line.rubric+': taxas ou CER distintos na mesma rubrica;conferir transcrição sem fundir taxas nem perder parcelas.');
const principal=addFractions(lines.map(l=>l.tax)),paid=lines.every(l=>l.paid!==null)?addFractions(lines.map(l=>l.paid!)):null;
if(paid===null)pending.push('Valor já pago não reconciliado;pagamento ao fornecedor não é entrega à AT.');
let interest:FiscalFraction|null=null,interestReason='Juros compensatórios indeterminados: taxa aplicável, âmbito e período por resolver;sem MAIBOR2026 nem substituição automática por MIMO/Prime. Juros de mora e tributação autónoma não apurados.';
if(i.interest.mode==='assessment'){
try{const a=decimalFraction(i.interest.amount);if(negative(a)||i.interest.period!==i.period||!i.interest.reference.trim()||!date(i.interest.date)||i.interest.date.slice(0,7)<i.period)throw Error();interest=a;interestReason=`Juros compensatórios transcritos de liquidação documentada:${i.interest.reference};data:${i.interest.date};período:${i.interest.period};montante original em MZN:${i.interest.amount}. Não são juros calculados pelo portal.`;}catch{interestReason='Liquidação de juros incompleta,negativa ou incoerente;identifique montante,data e documento do mesmo período.';}
}else if(i.interest.mode==='timely'){
const ids=lines.flatMap(l=>l.ids),inputs=(b.result?.inputs??[]).filter(r=>ids.includes(r.id));
const dates=inputs.flatMap(r=>r.allocations.map(a=>b.result?.payments.find(p=>p.id===a.paymentId)?.date??''));
if(i.interest.period===i.period&&date(i.interest.dueDate)&&i.interest.dueReference.trim()&&i.interest.reference.trim()&&paid!==null&&isZero(subtractFractions(principal,paid))&&inputs.length>0&&inputs.every(r=>r.settlement==='complete'&&r.allocations.length>0)&&dates.every(d=>date(d)&&d<=i.interest.dueDate)&&i.interest.dueDate>=i.period+'-01'){
interest=zero();interestReason=`Entregas integralmente reconciliadas até ao prazo documentado:${i.interest.dueDate};${i.interest.dueReference};${i.interest.reference}. Sem atraso neste recorte;sem declarar cumprimento global de obrigações.`;
}else interestReason='Ausência de atraso não demonstrada: confira prazo aplicável, comprovativos completos, datas e montante entregue. Sem zero de juros presumido.';
}
if(!interest)pending.push(interestReason);
let total:FiscalFraction|null=null;
if(pending.length===0&&interest&&paid){const balance=subtractFractions(addFractions([principal,interest]),paid);if(negative(balance))pending.push('Valor já pago excede imposto e juros;reconcilie sem crédito/reembolso automático.');else total=balance;}
return {period:i.period,input:structuredClone(i),lines,excluded,pending:[...new Set(pending)],principal,paid,interest,total,interestReason,source:mrftaSource};
}
export function recordMrfta(b:IrpcBook&MrftaStore,current:boolean,now=new Date().toISOString()):IrpcBook&MrftaStore{
const model=prepareMrfta(b,current);return{...b,mrftaHistory:b.mrftaResult?[...(b.mrftaHistory??[]),structuredClone(b.mrftaResult)]:b.mrftaHistory??[],mrftaResult:{key:key(b,current),createdAt:now,model}};
}
const money=(f:FiscalFraction|null)=>{if(!f)return'Indeterminado';const p=fractionPresentation(f);return(p.approximate?'≈':'')+p.decimal+' MZN';};
export function mrftaReportRows(b:IrpcBook&MrftaStore,current:boolean):Array<[string,string,string,string]>{
const r=b.mrftaResult;if(!r)return[];const m=r.model;return[['IRPC — retenções','MRFTA — '+m.period,`Subtotal das retenções correspondidas:${money(m.principal)};juros:${money(m.interest)};valor já pago:${money(m.paid)};total a pagar:${money(m.total)};${m.interestReason};pendências:${m.pending.join(' | ')}`,`${mrftaCurrent(b,current)?'Preparação correspondente':'Histórico;preparação desactualizada'};${r.createdAt};completude:${m.input.completenessRef};outras rubricas:${m.input.otherRubricsRef};${m.source}`],...m.lines.map(l=>['IRPC — retenções',l.rubric,`${l.documents.join(';')};base:${money(l.base)};taxa:${l.rate}%;imposto:${money(l.tax)};CER:${l.cer||'por corresponder'}`,`Período:${m.period};apenas rendimento pago;sem assinatura ou uso dos serviços;documentos:${l.ids.join(',')};suporte:${l.references.join(' | ')}`] as [string,string,string,string])];
}
export function mrftaHtml(b:IrpcBook&MrftaStore,current:boolean):string{
if(!current||!mrftaCurrent(b,current))throw Error('Calcule as retenções e prepare o MRFTA com as entradas actuais antes de exportar.');
const m=b.mrftaResult!.model,esc=(v:unknown)=>String(v).replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]!));
return `<!doctype html><html lang="pt"><meta charset="utf-8"><title>MRFTA — preparação</title><body><h1>MRFTA — preenchimento de preparação</h1><p>Quadro1:retenção na fonte;${esc(m.input.declaration==='initial'?'Inicial':m.input.declaration==='replacement'?'Substituição':'Tipo por identificar')}. Quadro2:${esc(m.period)};${esc(m.input.timing==='within'?'Dentro do prazo':m.input.timing==='late'?'Fora do prazo':'Prazo por identificar')}.</p><p>Quadro3 NUIT:${esc(m.input.nuit)};área fiscal:${esc(m.input.taxArea)}. Quadro4:${esc(m.input.name)}. Quadro5:${esc(m.input.commercialName)}. Quadro6:${esc(m.input.address)};${esc(m.input.contact)}.</p><p>Conferência do período:${esc(m.input.completenessRef)}. Outras rubricas:${esc(m.input.otherRubricsRef)}.</p><h2>Quadro7</h2><table><thead><tr><th>CER</th><th>Rubrica</th><th>Base</th><th>Taxa</th><th>Imposto</th><th>Documentos</th></tr></thead><tbody>${m.lines.map(l=>`<tr><td>${esc(l.cer||'Por corresponder')}</td><td>${esc(l.rubric)}</td><td>${esc(money(l.base))}</td><td>${l.rate}%</td><td>${esc(money(l.tax))}</td><td>${esc(l.documents.join(';')+';'+l.references.join(' | '))}</td></tr>`).join('')}</tbody></table><p>Subtotal correspondido:${esc(money(m.principal))};JUROS COMPENSATÓRIOS:${esc(money(m.interest))};VALOR JÁ PAGO:${esc(money(m.paid))};TOTAL A PAGAR:${esc(money(m.total))}.</p><p>${esc(m.interestReason)}</p><h2>Casos pendentes</h2><ul>${m.pending.map(p=>'<li>'+esc(p)+'</li>').join('')}</ul><h2>Casos separados</h2><ul>${m.excluded.map(p=>'<li>'+esc(p.id+':'+p.reason)+'</li>').join('')}</ul><p>Quadros8/9 reservados,sem assinatura,carimbo,número de entrada ou aceitação AT. Tributação autónoma e juros de mora não apurados. Este documento de preparação não substitui o original oficial nem submete uma declaração.</p><small>${esc(m.source)}</small></body></html>`;
}
+4 -2
View File
@@ -1,3 +1,4 @@
import {validateMrfta,mrftaReportRows,type MrftaStore} from './irpc-mrfta.ts';
import {irpcReliefVersion,evaluateIrpcRelief,irpcReliefEvidenceLabel,type IrpcReliefPreparation} from './irpc-relief-preparation.ts';
import {convertIrpcAmount,irpcFxDependency,irpcFxEvidenceLabel,type IrpcFxAmount,type IrpcFxEvidence} from './irpc-fx.ts';
import {acquisitionBook,acquisitionScope} from './iva-acquisitions.ts';
@@ -16,12 +17,13 @@ export type IrpcOrdinaryFacts={nonDigital:Choice;capitalType:''|'unlisted_intere
export const emptyIrpcOrdinaryFacts=():IrpcOrdinaryFacts=>({nonDigital:'',capitalType:'',rentOwnPropertyManagement:'',specialFacts:'',capitalSeatAndManagement:'',reference:''});
export type IrpcRow=IrpcFxAmount&{reliefPreparation?:IrpcReliefPreparation;ordinary?:IrpcOrdinaryFacts;id:string;direction:'paid'|'received';counterparty:string;documentRef:string;description:string;base:string;currency:string;origin:'projected'|'effective';kind:IrpcKind;recipientIrpc:Choice;payerIrpc:Choice;residence:''|'resident'|'nonresident';permanentEstablishment:Choice;attributable:Choice;mozambique:Choice;qualificationRef:string;specialRelief:''|'none'|'claimed';reliefRef:string;eventsComplete:Choice;events:IrpcEvent[];settlement:''|'none'|'complete';settlementRef:string;allocations:Array<{paymentId:string;amount:string}>;source?:{id:string;key:string;fingerprint:string};};
export type IrpcResult={treatment?:'dispensed'|'treaty';reliefRule?:string;id:string;status:'calculated'|'indeterminate'|'outside_scope';reason:string;period:string;eventDate:string;nature:'definitive'|'on_account'|null;rate:number|null;base:FiscalFraction|null;tax:FiscalFraction|null;settled:FiscalFraction|null;outstanding:FiscalFraction|null;direction:IrpcRow['direction'];conversion?:IrpcFxEvidence|null};
export type IrpcBook={version:1;rows:IrpcRow[];payments:IrpcPayment[];result?:{version:string;key:string;calculatedAt:string;inputs:IrpcRow[];payments:IrpcPayment[];rows:IrpcResult[];paymentConversions?:Record<string,IrpcFxEvidence|null>};history?:Array<NonNullable<IrpcBook['result']>>};
export type IrpcBook=MrftaStore&{version:1;rows:IrpcRow[];payments:IrpcPayment[];result?:{version:string;key:string;calculatedAt:string;inputs:IrpcRow[];payments:IrpcPayment[];rows:IrpcResult[];paymentConversions?:Record<string,IrpcFxEvidence|null>};history?:Array<NonNullable<IrpcBook['result']>>};
export const newIrpcRow=(id:string):IrpcRow=>({id,direction:'paid',counterparty:'',documentRef:'',description:'',base:'',currency:'MZN',origin:'projected',kind:'',recipientIrpc:'',payerIrpc:'',residence:'',permanentEstablishment:'',attributable:'',mozambique:'',qualificationRef:'',specialRelief:'',reliefRef:'',eventsComplete:'',events:[],settlement:'',settlementRef:'',allocations:[]});
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 preservados.');return s;}
function map(d:Record<string,unknown>):Record<string,IrpcBook>{const m=store(d).irpcWithholdings;if(m===undefined)return{};if(!m||typeof m!=='object'||Array.isArray(m))throw Error('Mapa IRPC não recuperável;originais preservados.');return m;}
function validate(b:IrpcBook){
if(!b||b.version!==1||!Array.isArray(b.rows)||!Array.isArray(b.payments)||b.history!==undefined&&!Array.isArray(b.history))throw Error('Livro IRPC não recuperável.');
validateMrfta(b);
for(const r of b.rows){const template=newIrpcRow('');if(!r||Object.keys(template).filter(k=>typeof template[k as keyof IrpcRow]==='string').some(k=>typeof r[k as keyof IrpcRow]!=='string')||!['paid','received'].includes(r.direction)||!['projected','effective'].includes(r.origin)||!irpcKinds.some(([k])=>k===r.kind)||!['','resident','nonresident'].includes(r.residence)||!['','none','claimed'].includes(r.specialRelief)||!['','none','complete'].includes(r.settlement)||['recipientIrpc','payerIrpc','permanentEstablishment','attributable','mozambique','eventsComplete'].some(k=>!['','yes','no'].includes(r[k as keyof IrpcRow]as string))||!Array.isArray(r.events)||!Array.isArray(r.allocations)||r.events.some(e=>!e||!['cost','payment','maturity','availability','liquidation','quantification'].includes(e.kind)||typeof e.date!=='string'||typeof e.reference!=='string')||r.allocations.some(a=>!a||typeof a.paymentId!=='string'||typeof a.amount!=='string')||r.source!==undefined&&(!r.source||!['id','key','fingerprint'].every(k=>typeof r.source![k as keyof NonNullable<IrpcRow['source']>]==='string')))throw Error('Entradas IRPC não recuperáveis;sem apagar ou repor vazios.');}
for(const r of b.rows)if(r.reliefPreparation&&(r.reliefPreparation.rule!==undefined&&!['','cirpc68d','cirpc68f','ptMz12'].includes(r.reliefPreparation.rule)||r.reliefPreparation.facts!==undefined&&(!r.reliefPreparation.facts||typeof r.reliefPreparation.facts!=='object'||Array.isArray(r.reliefPreparation.facts)||Object.values(r.reliefPreparation.facts).some(v=>!['','yes','no'].includes(v)))))throw Error('Condições da dispensa/convenção não recuperáveis.');
for(const r of b.rows)if(r.reliefPreparation&&(!['dispensation','exemption','treaty'].includes(r.reliefPreparation.kind)||['instrument','article','country','residenceDocument','validFrom','validTo','conditions','evidence'].some(k=>typeof r.reliefPreparation![k as keyof IrpcReliefPreparation]!=='string')))throw Error('Preparação de dispensa/convenção não recuperável;original preservado.');
@@ -116,5 +118,5 @@ export function recordIrpc(d:Record<string,unknown>,b:IrpcBook,now=new Date().to
return{...b,history:b.result?[...(b.history??[]),structuredClone(b.result)]:b.history,result:{version:irpcWithholdingVersion,key:key(d,b),calculatedAt:now,inputs:structuredClone(b.rows),payments:structuredClone(b.payments),rows,paymentConversions}};
}
const amount=(v:FiscalFraction|null)=>{if(!v)return'Indeterminado';const p=fractionPresentation(v);return(p.approximate?'≈':'')+p.decimal+' MZN';};
export function irpcReportRows(d:Record<string,unknown>):Array<[string,string,string,string]>{try{const b=irpcBook(d),r=b.result;if(!r)return[];return r.rows.map(v=>{const i=r.inputs.find(i=>i.id===v.id)!;return['IRPC — retenções',`${i.documentRef} — ${i.counterparty}`,`${({calculated:'Calculado',indeterminate:'Indeterminado',outside_scope:'Fora deste âmbito'})[v.status]};tratamento:${v.treatment==='dispensed'?'Dispensa de retenção':v.treatment==='treaty'?'Limite convencional':'Retenção interna'};enquadramento: ${irpcKinds.find(([k])=>k===i.kind)?.[1]??i.kind};${i.direction==='paid'?'retida a entregar':'sofrida'};base:${amount(v.base)};taxa:${v.treatment==='dispensed'?'Dispensa (sem retenção)':(v.rate??'por corresponder')+'%'};retenção:${amount(v.tax)};entrega/prova:${amount(v.settled)};saldo:${amount(v.outstanding)};${v.reason}`,`${acquisitionScope(d)};${i.origin};${v.period};evento:${v.eventDate};${v.nature==='on_account'?'Imposto por conta (sem crédito anual automático)':v.nature==='definitive'?'Retenção definitiva':'Natureza por determinar'};${irpcCurrent(d,b)?'Entradas correspondentes':'Histórico;entradas/origem divergentes'};dispensa/convenção:${irpcReliefEvidenceLabel(i)};reconciliação:${i.settlementRef};comprovativos:${i.allocations.map(a=>{const p=r.payments.find(p=>p.id===a.paymentId);return p?`${p.reference}/${p.date}:parcela${a.amount}MZN de${p.amount}${p.currency};${r.paymentConversions?.[p.id]?irpcFxEvidenceLabel(r.paymentConversions[p.id]!):'sem conversão usada'}`:'referência por resolver';}).join(' | ')};${r.calculatedAt};${v.conversion?'Câmbio:'+irpcFxEvidenceLabel(v.conversion)+';':''}${irpcWithholdingSource}`];});}catch(e){return[['IRPC — retenções','Livro não recuperável',String(e),'Originais e histórico preservados.']];}}
export function irpcReportRows(d:Record<string,unknown>):Array<[string,string,string,string]>{try{const b=irpcBook(d),r=b.result;if(!r)return mrftaReportRows(b,false);return [...r.rows.map((v):[string,string,string,string]=>{const i=r.inputs.find(i=>i.id===v.id)!;return['IRPC — retenções',`${i.documentRef} — ${i.counterparty}`,`${({calculated:'Calculado',indeterminate:'Indeterminado',outside_scope:'Fora deste âmbito'})[v.status]};tratamento:${v.treatment==='dispensed'?'Dispensa de retenção':v.treatment==='treaty'?'Limite convencional':'Retenção interna'};enquadramento: ${irpcKinds.find(([k])=>k===i.kind)?.[1]??i.kind};${i.direction==='paid'?'retida a entregar':'sofrida'};base:${amount(v.base)};taxa:${v.treatment==='dispensed'?'Dispensa (sem retenção)':(v.rate??'por corresponder')+'%'};retenção:${amount(v.tax)};entrega/prova:${amount(v.settled)};saldo:${amount(v.outstanding)};${v.reason}`,`${acquisitionScope(d)};${i.origin};${v.period};evento:${v.eventDate};${v.nature==='on_account'?'Imposto por conta (sem crédito anual automático)':v.nature==='definitive'?'Retenção definitiva':'Natureza por determinar'};${irpcCurrent(d,b)?'Entradas correspondentes':'Histórico;entradas/origem divergentes'};dispensa/convenção:${irpcReliefEvidenceLabel(i)};reconciliação:${i.settlementRef};comprovativos:${i.allocations.map(a=>{const p=r.payments.find(p=>p.id===a.paymentId);return p?`${p.reference}/${p.date}:parcela${a.amount}MZN de${p.amount}${p.currency};${r.paymentConversions?.[p.id]?irpcFxEvidenceLabel(r.paymentConversions[p.id]!):'sem conversão usada'}`:'referência por resolver';}).join(' | ')};${r.calculatedAt};${v.conversion?'Câmbio:'+irpcFxEvidenceLabel(v.conversion)+';':''}${irpcWithholdingSource}`];}),...mrftaReportRows(b,irpcCurrent(d,b))];}catch(e){return[['IRPC — retenções','Livro não recuperável',String(e),'Originais e histórico preservados.']];}}
export function irpcPreparationHtml(d:Record<string,unknown>):string{const b=irpcBook(d);if(!b.result||!irpcCurrent(d,b))throw Error('Calcule e guarde as entradas actuais antes de preparar.');const esc=(v:unknown)=>String(v).replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]!));return'<!doctype html><html lang="pt"><meta charset="utf-8"><title>IRPC — preparação das retenções</title><body><h1>Preparação IRPC — L5</h1><p>Mapa de trabalho;sem submissão,recepção ou aceitação AT. MRFTA19/39:quadro1 retenção;quadro2 mês/ano único por declaração;quadro7 base/taxa/imposto. CER e correspondência das rubricas digitais/moeda electrónica por esclarecer;67(7)remete62(6),mas taxa nova em62(5). Não preencher assinatura ou uso dos serviços. Não consolidar períodos diferentes nem transformar retenções sofridas em imposto a entregar.</p>'+irpcReportRows(d).map(r=>'<section><h2>'+esc(r[1])+'</h2><p>'+esc(r[2])+'</p><small>'+esc(r[3])+'</small></section>').join('')+'<p>Instruções do exemplar:prazo nominal dia20 do mês seguinte à dedução,sem ajuste de calendário/prorrogação ou procedimento2026 presumidos. Juros não calculados. GuiaM/39 anterior preservada,sem actualizar remissões do original.</p></body></html>';}
+41
View File
@@ -0,0 +1,41 @@
<?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 IrpcMrftaDraftTest extends TestCase
{
use DatabaseTransactions;
public function test_mrfta_and_documented_interest_recover_from_private_api_without_losing_history(): void
{
$target = DB::selectOne('select current_database() as name,inet_server_port() as port');
$this->assertSame('simulator_fiscal_delta_20261021_test', $target->name);
$this->assertSame(55441, (int) $target->port);
config(['investor-path.enabled'=>true, 'suitecrm.enabled'=>false]);
$this->actingAs(User::factory()->create(['email_verified_at'=>now()]));
$draft = json_decode(file_get_contents(base_path('outputs/irpc-mrfta/calculated.json')), true, flags: JSON_THROW_ON_ERROR);
$personal = ['person_type'=>$draft['person_type']];
unset($draft['person_type']);
$id = $this->postJson('/api/v1/simulation-drafts', ['personal_snapshot'=>$personal, 'economic_snapshot'=>$draft], ['Idempotency-Key'=>(string) Str::uuid()])->assertCreated()->json('data.id');
$recovered = $this->getJson('/api/v1/simulation-drafts/'.$id)->assertOk()->json('data.economic_snapshot');
$this->assertSame($draft['modelo_a_snapshot'], $recovered['modelo_a_snapshot']);
$store = json_decode($recovered['modelo_a_snapshot'], true, flags: JSON_THROW_ON_ERROR);
$book = $store['irpcWithholdings'][$draft['context_opportunity']];
$model = $book['mrftaResult']['model'];
$this->assertSame('2026-10', $model['period']);
$this->assertSame('20000', $model['principal']['numerator']);
$this->assertSame('3000', $model['paid']['numerator']);
$this->assertSame('500', $model['interest']['numerator']);
$this->assertSame('17500', $model['total']['numerator']);
$this->assertSame([], $model['pending']);
$this->assertSame('999', $model['lines'][0]['cer']);
$this->assertStringContainsString('fictício', $model['input']['codes'][0]['reference']);
$this->assertNotEmpty($book['history']);
file_put_contents(base_path('outputs/irpc-mrfta/recovered.json'), json_encode($recovered, JSON_THROW_ON_ERROR|JSON_UNESCAPED_UNICODE));
}
}
+54
View File
@@ -0,0 +1,54 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import {newIrpcRow,emptyIrpcOrdinaryFacts,recordIrpc,saveIrpcBook,irpcBook,irpcCurrent,irpcReportRows} from '../../resources/js/lib/irpc-withholdings.ts';
import {newMrftaInput,prepareMrfta,recordMrfta,mrftaCurrent,mrftaHtml} from '../../resources/js/lib/irpc-mrfta.ts';
import {createProjectDossierReport} from '../../resources/js/lib/project-dossier-report.ts';
const draft=()=>JSON.parse(fs.readFileSync(new URL('./fixtures/irpc-grouped-preserved.json',import.meta.url),'utf8'));
const row=(id='principal')=>({...newIrpcRow(id),counterparty:'QA contraparte',documentRef:'QA-'+id,base:'100000',kind:'royalty',recipientIrpc:'yes',payerIrpc:'yes',residence:'nonresident',permanentEstablishment:'no',mozambique:'yes',qualificationRef:'QA contrato e residência',specialRelief:'none',eventsComplete:'yes',events:[{kind:'cost',date:'2026-10-02',reference:'QA custo'}],settlement:'complete',settlementRef:'QA reconciliação',allocations:[{paymentId:'p',amount:'3000'}],ordinary:{...emptyIrpcOrdinaryFacts(),nonDigital:'yes',reference:'QA royalties não digitais'}});
const input=()=>({...newMrftaInput(),period:'2026-10',declaration:'initial',timing:'late',nuit:'999999999',name:'QA sujeito passivo',commercialName:'QA',taxArea:'QA área fiscal',address:'QA domicílio',contact:'qa@example.invalid',completeness:'yes',completenessRef:'QA conferência do período e livros',otherRubrics:'none',otherRubricsRef:'QA ausência documentada de IRPS/tributação autónoma/outros valores',codes:[{rowId:'principal',cer:'999',reference:'QA CER fictício, apenas teste'}],interest:{...newMrftaInput().interest,mode:'assessment',period:'2026-10',amount:'500',date:'2026-11-25',reference:'QA liquidação de juros compensatórios'}});
function book(rows=[row()]){return {...recordIrpc(draft(),{version:1,rows,payments:[{id:'p',kind:'remittance',reference:'QA entrega IRPC',date:'2026-11-15',amount:'3000',currency:'MZN'}]}),mrfta:input()};}
test('MRFTA fills one period with principal, documented interest, payment and balance separately',()=>{
const b=book(),m=prepareMrfta(b,true);assert.deepEqual(m.pending,[]);assert.equal(m.principal.numerator,'20000');assert.equal(m.paid.numerator,'3000');assert.equal(m.interest.numerator,'500');assert.equal(m.total.numerator,'17500');assert.equal(m.lines[0].rate,20);assert.equal(m.lines[0].cer,'999');assert.match(m.interestReason,/transcritos/);
});
test('unknown MAIBOR2026 never becomes zero or MIMO; independent principal remains calculated',()=>{
const b=book();b.mrfta.interest.mode='';const m=prepareMrfta(b,true);assert.equal(m.interest,null);assert.equal(m.total,null);assert.equal(m.principal.numerator,'20000');assert.equal(b.result.rows[0].status,'calculated');assert.match(m.pending.join(' '),/MAIBOR2026/);
});
test('insufficient CER or contradictory rate per rubric blocks total without losing rows',()=>{
const b=book();b.mrfta.codes=[];let m=prepareMrfta(b,true);assert.equal(m.total,null);assert.equal(m.lines.length,1);assert.match(m.pending.join(' '),/CER/);
const other=structuredClone(b.result.rows[0]);other.id='other';other.rate=10;other.tax={numerator:'10000',denominator:'1'};b.result.rows.push(other);b.result.inputs.push({...row('other'),allocations:[]});b.mrfta.codes=[{rowId:'principal',cer:'999',reference:'QA ref'},{rowId:'other',cer:'999',reference:'QA ref'}];m=prepareMrfta(b,true);assert.equal(m.lines.length,2);assert.match(m.pending.join(' '),/taxas ou CER distintos/);assert.equal(m.total,null);
});
test('other months,suffered withholdings and documented dispensas stay outside payable rows',()=>{
const b=book();for(const[id,patch,original]of [['other-month',{period:'2026-11'},{}],['suffered',{}, {direction:'received'}],['dispensed',{treatment:'dispensed',tax:{numerator:'0',denominator:'1'}},{}]]){b.result.rows.push({...b.result.rows[0],id,...patch});b.result.inputs.push({...row(id),...original});}
const m=prepareMrfta(b,true);assert.equal(m.lines.length,1);assert.equal(m.excluded.length,3);assert.equal(m.principal.numerator,'20000');assert.equal(m.total.numerator,'17500');
});
test('unsupported digital and indeterminate cases retain explicit gaps without guessed rubric',()=>{
const b=book();b.result.inputs[0].kind='digital_services';let m=prepareMrfta(b,true);assert.equal(m.lines.length,0);assert.equal(m.total,null);assert.match(m.pending.join(' '),/digital/);b.result.inputs[0].kind='royalty';b.result.rows[0].status='indeterminate';b.result.rows[0].tax=null;m=prepareMrfta(b,true);assert.equal(m.total,null);assert.match(m.pending.join(' '),/indeterminado/);
});
test('timely remittance only yields zero interest with complete proof and explicit applicable deadline',()=>{
const r=row();r.base='15000';const b=book([r]);b.mrfta.interest={...b.mrfta.interest,mode:'timely',period:'2026-10',dueDate:'2026-11-20',dueReference:'QA prazo documentado e calendário'};assert.equal(prepareMrfta(b,true).interest.numerator,'0');assert.equal(prepareMrfta(b,true).total.numerator,'0');
b.mrfta.interest.dueDate='2026-11-10';assert.equal(prepareMrfta(b,true).interest,null);b.mrfta.interest.dueDate='2026-11-20';b.result.rows[0].settled=null;assert.equal(prepareMrfta(b,true).interest,null);
});
test('assessment cannot leak into another period or accept negative amount and missing reference',()=>{
const b=book();for(const patch of [{period:'2026-11'},{amount:'-10'},{reference:''},{date:'2026-02-30'}]){const copy=structuredClone(b);Object.assign(copy.mrfta.interest,patch);assert.equal(prepareMrfta(copy,true).interest,null);assert.equal(prepareMrfta(copy,true).total,null);}
});
test('MRFTA save,recover,history and dossier preserve existing tax maps and freeze evidence',()=>{
const d=draft(),prior=irpcBook(d),original=JSON.parse(d.modelo_a_snapshot);let b=book();b.history=[prior.result];b=recordMrfta(b,true);d.modelo_a_snapshot=saveIrpcBook(d,b);const recovered=JSON.parse(JSON.stringify(d)),saved=irpcBook(recovered);assert.deepEqual(saved,b);assert.equal(irpcCurrent(recovered,saved),true);assert.equal(mrftaCurrent(saved,true),true);
for(const[k,v]of Object.entries(original))if(k!=='irpcWithholdings')assert.deepEqual(JSON.parse(recovered.modelo_a_snapshot)[k],v,k);
const report=irpcReportRows(recovered);assert.match(JSON.stringify(report),/17500/);assert.match(JSON.stringify(createProjectDossierReport(recovered,{selectedSections:new Set(['finance'])})),/MRFTA/);
assert.match(mrftaHtml(saved,true),/Quadro7/);assert.match(mrftaHtml(saved,true),/QA CER fictício/);const before=structuredClone(saved.mrftaResult);saved.mrfta.interest.amount='600';assert.equal(mrftaCurrent(saved,true),false);assert.throws(()=>mrftaHtml(saved,true));const next=recordMrfta(saved,true);assert.deepEqual(next.mrftaHistory.at(-1),before);assert.equal(next.mrftaResult.model.total.numerator,'17600');
fs.mkdirSync('outputs/irpc-mrfta',{recursive:true});fs.writeFileSync('outputs/irpc-mrfta/calculated.json',JSON.stringify(recovered));fs.writeFileSync('outputs/irpc-mrfta/preparation.html',mrftaHtml(b,true));
});
test('malformed persisted MRFTA rejects recovery without replacing originals',()=>{
const b=book();b.mrfta.codes.push({...b.mrfta.codes[0]});assert.throws(()=>saveIrpcBook(draft(),b));b.mrfta.codes=[];b.mrfta.interest.amount=5;assert.throws(()=>saveIrpcBook(draft(),b));
});
test('export escapes identities,documents and assessment references',()=>{
const b=book();b.mrfta.name='<script>QA</script>';b.mrfta.interest.reference='<img src=x onerror=QA>';const html=mrftaHtml(recordMrfta(b,true),true);assert.ok(!html.includes('<script>QA'));assert.ok(html.includes('&lt;script&gt;'));assert.ok(html.includes('&lt;img'));
});
test('other tax rubrics cannot silently become zero and partial rows remain available',()=>{
const b=book();b.mrfta.otherRubrics='pending';const m=prepareMrfta(b,true);assert.equal(m.total,null);assert.equal(m.principal.numerator,'20000');assert.match(m.pending.join(' '),/IRPS,tributação autónoma/);assert.throws(()=>mrftaHtml(recordMrfta(b,false),false));
});
test('same rubric rate and documented CER aggregate without losing document references',()=>{
const other={...row('other'),allocations:[],settlement:'none'},b=book([row(),other]);b.mrfta.codes.push({rowId:'other',cer:'999',reference:'QA segundo documento de CER fictício'});const m=prepareMrfta(b,true);assert.equal(m.lines.length,1);assert.equal(m.lines[0].base.numerator,'200000');assert.equal(m.principal.numerator,'40000');assert.equal(m.total.numerator,'37500');assert.equal(m.lines[0].references.length,2);assert.equal(m.lines[0].documents.length,2);
});
+21
View File
@@ -0,0 +1,21 @@
<?php
$root=dirname(__DIR__);
$operation=$argv[1]??'';
if(!in_array($operation,['routes','test'],true)||basename($root)!=='portal-investidor-irpc-mrfta-qa-20261025')throw new RuntimeException('Explicit reliefs QA workspace and routes/test operation required.');
foreach(['storage/framework/views','storage/framework/cache','storage/framework/sessions','bootstrap/cache','storage/logs','outputs/irpc-mrfta']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'=>'simulator_fiscal_delta_20261021_test','DB_USERNAME'=>'postgres','DB_PASSWORD'=>'','DB_URL'=>'','CACHE_STORE'=>'array','SESSION_DRIVER'=>'array','QUEUE_CONNECTION'=>'sync','MAIL_MAILER'=>'array'] as $k=>$v){putenv($k.'='.$v);$_ENV[$k]=$_SERVER[$k]=$v;}
$loader=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']!=='simulator_fiscal_delta_20261021_test'||!empty($c['url']))throw new RuntimeException('QA guard');
$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('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/IrpcMrftaDraftTest.php'];$argc=count($argv);$_SERVER['argv']=$argv;$_SERVER['argc']=$argc;require $root.'/vendor/phpunit/phpunit/phpunit';