Start scoped IRPC L5 withholding workflow with document and payment reuse
This commit is contained in:
@@ -27412,6 +27412,73 @@
|
||||
"item": "Submissão e pagamento",
|
||||
"needed": "actos externos e comprovativos reais;nenhuma operaçãoATfoiexecutadaemQA"
|
||||
}
|
||||
]
|
||||
],
|
||||
"installation_followup": "ispc_residuals_delivered_20261025"
|
||||
},
|
||||
"ispc_residuals_delivered_20261025": {
|
||||
"status": "installed_grouped_ISPC_residual_delta_verified",
|
||||
"commit": "5c0cb25cea7826811c389b55a64bcdf5e43b937f",
|
||||
"receipt": "ispc-residual-delivery-20261025.json",
|
||||
"backup_restore_verified": true,
|
||||
"ISPC_integrally_completed": false,
|
||||
"IRPS_integrally_completed": false
|
||||
},
|
||||
"irpc_l5_first_scoped_QA_20261025": {
|
||||
"status": "QA_first_scoped_increment_not_installed",
|
||||
"receipt": "irpc-l5-withholdings-qa-20261025.json",
|
||||
"verification": {
|
||||
"new_frontend_distinct": 17,
|
||||
"frontend": "frontend.txt:16pass;directed-cross-context.txt:1pass",
|
||||
"API": "1test/23assertions;guardedloopback55441/simulator_fiscal_delta_20261021_test;DatabaseTransactionsrollback;allpreviousL3/ISPCmapsidentical",
|
||||
"types": "types-final.txt:exit0",
|
||||
"build": "build-final.txt:exit0",
|
||||
"UI": "Actualcomponent calculation/save/recover/report andexport;localfileharness distinguishedfromprivateAPI;reuse existingacquisition at373 demonstrated",
|
||||
"previous_functional_suites_repeated": false
|
||||
},
|
||||
"remaining": [
|
||||
{
|
||||
"item": "Taxas normais e especiais dos demais rendimentos",
|
||||
"needed": "Corresponder62(1–4)/67 por rendimento e período,com alterações20/2009,4/2012,19/2013 e20/2022. Ficheiro local BR252/2022 contém apenas3.ºsuplemento/Decreto74/2022,semLei20/2022;INM identifica esta na p2731. Texto exacto em falta não é revalidação de autenticidade.",
|
||||
"resolution": "Implementar ramo dirigido após correspondência do texto aplicável;não usar2002 ou taxa genérica."
|
||||
},
|
||||
{
|
||||
"item": "Residentes/EP e dispensa68",
|
||||
"needed": "Corresponder62(5) novo com dispensas68,em especial68(d),titular/rendimento imputável aEP e factos de cada dispensa;sem confundir taxa com obrigação de reter.",
|
||||
"resolution": "Ramo próprio por natureza/dispensa;sem presumirretenção10%ouzero."
|
||||
},
|
||||
{
|
||||
"item": "Convenções/benefícios",
|
||||
"needed": "Convenção invocada,artigo,residência,beneficiário,condições,período e documento aplicável",
|
||||
"resolution": "Só o caso dependente permanece indeterminado;nenhuma taxa reduzida sem esses elementos."
|
||||
},
|
||||
{
|
||||
"item": "Conversão cambial",
|
||||
"needed": "Moeda,montante,data,taxa efonte aplicáveis à operação",
|
||||
"resolution": "Conversão rastreável;sem taxa actual nem relabelagemMZN."
|
||||
},
|
||||
{
|
||||
"item": "Juros/regularizações/guia agregada",
|
||||
"needed": "Regra/taxa/base/período próprios;guias/declarações anteriores e pagamentos para substituições",
|
||||
"resolution": "Não substituir juros porzero;preparação actual não é declaração rectificativa nem liquidação anualIRPC."
|
||||
}
|
||||
],
|
||||
"external": [
|
||||
{
|
||||
"item": "MRFTA19/39:rubricas eCER digitais/moeda electrónica",
|
||||
"needed": "Correspondência da nova categoria62(5)a/b às rubricas/CERdo quadro7;remissão67(7)para62(6)existe no original,masnova taxa aparece62(5).",
|
||||
"resolution": "Preservar divergência e original;não renumerar,adivinharCERou bloquear cálculo positivo independente."
|
||||
},
|
||||
{
|
||||
"item": "GuiaM39 anterior eMRFTAactual",
|
||||
"needed": "Manter exemplares;instruçõesMRFTAidentificamperíodoúnico e prazonominaldia20mêsseguinte àdedução;calendário/prorrogação eprocedimento2026específico nãoinferidos",
|
||||
"resolution": "Mapa de preparação/exportação;sem preenchimentofinalCER,assinaturaouusodosserviços."
|
||||
},
|
||||
{
|
||||
"item": "ActosAT",
|
||||
"needed": "Submissão,recepção,aceitação eeventualcomprovativo externo",
|
||||
"resolution": "Externos aoportal;exportar nãoexecuta nemcertifica estes actos."
|
||||
}
|
||||
],
|
||||
"active_changes": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
{
|
||||
"status": "QA_first_scoped_increment_not_installed",
|
||||
"base_installed": "5c0cb25cea7826811c389b55a64bcdf5e43b937f",
|
||||
"branch": "codex/irpc-l5-withholdings-qa-20261025",
|
||||
"L5_integrally_completed": false,
|
||||
"IRPC_integrally_completed": false,
|
||||
"IRPS_integrally_completed": false,
|
||||
"scope": "Shared counterpart/document reuse;2026event chronology;10% digitalgoods/services/emoneycommission,nonresident nonattributabletoEP with explicit IRPCpayer/recipient/Mozambiquesource and no invokedspecialrelief;withheld/remitted/proven amounts separate;private draft persistence/recovery/report/preparationexport.",
|
||||
"sources": [
|
||||
{
|
||||
"file": "Lei-34-2007-BR52.pdf",
|
||||
"url": "https://www.dlapiperafrica.com/export/sites/africa/mozambique/insights/legislation-series/tax/downloads/Lei_34_2007.pdf_2063068952.pdf",
|
||||
"sha256": "cdbef875ad7ccc1517b12faf5de0e893a70ed2ed81e33913cd6dc4a674474e6e",
|
||||
"pages": 22,
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-34-2007-BR52.pdf",
|
||||
"usage": "Art67(3)/68 visually reviewed onPDF19;other text identified"
|
||||
},
|
||||
{
|
||||
"file": "Lei-4-2012-BR3.pdf",
|
||||
"url": "https://www.dlapiperafrica.com/export/sites/africa/mozambique/insights/legislation-series/tax/downloads/Lei-n-4.2012-de-23-de-Janeiro-de-2012.pdf_2063068952.pdf",
|
||||
"sha256": "b8845cef41dcbd9ba50c43dbfa743a56030dd3d348242a107e1ae30279eec121",
|
||||
"pages": 3,
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-4-2012-BR3.pdf",
|
||||
"usage": "Original inventoried/preserved;full article mapping not yet complete,no rate activated from this copy"
|
||||
},
|
||||
{
|
||||
"file": "Lei-19-2013-BR76.pdf",
|
||||
"url": "https://www.dlapiperafrica.com/export/sites/africa/mozambique/insights/legislation-series/tax/downloads/Lei-n_19.2013.pdf_2063068952.pdf",
|
||||
"sha256": "bba96d645cc935ffc3d1a79b9f0f73ac1c693d6459c2ad41f56cd2841accae76",
|
||||
"pages": 2,
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-19-2013-BR76.pdf",
|
||||
"usage": "Original inventoried/preserved;full article mapping not yet complete,no rate activated from this copy"
|
||||
},
|
||||
{
|
||||
"file": "Decreto-9-2008-BR16.pdf",
|
||||
"url": "https://www.dlapiperafrica.com/export/sites/africa/mozambique/insights/legislation-series/tax/downloads/Decreto_9_2008.pdf_2063068952.pdf",
|
||||
"sha256": "96fccdecbfdc11c9eafb90fbc6c7bc259f88bbd4c7507d1ac3af1fa03f960697",
|
||||
"pages": 11,
|
||||
"path": "docs/investmoz/simulations/source-archive/Decreto-9-2008-BR16.pdf",
|
||||
"usage": "Original inventoried/preserved;full article mapping not yet complete,no rate activated from this copy"
|
||||
},
|
||||
{
|
||||
"file": "AT-MRFTA-19-39.pdf",
|
||||
"url": "https://www.at.gov.mz/api/webdav?path=%2FFORMULARIOS%2FIMPOSTOS%20FISCAIS%2FIRPS%2FIRPS-IRPC%20MRFTA%20-%2019-39.pdf",
|
||||
"pages": 2,
|
||||
"sha256": "8ea62c53066b482f87078a7a7b1ecf15ca98a12a122e8d7abca63acfc993186b",
|
||||
"path": "docs/investmoz/simulations/source-archive/AT-MRFTA-19-39.pdf",
|
||||
"usage": "MRFTA p1–2 visually reviewed;consultation/preparation,not submission"
|
||||
}
|
||||
],
|
||||
"existing_2026_source": {
|
||||
"path": "docs/investmoz/simulations/source-archive/BR-248-I-Suplemento-1-2025.pdf",
|
||||
"sha256": "ccf2a8deeca820f2dd076b4b2141b1efe3f6a806568c3a587ca59a8e92f4c251",
|
||||
"citation": "Lei12/2025 PDF18–20;p2180(18–20);PDF19visually reviewed via identical text in received4pageexemplar;62(5),67(5/7)"
|
||||
},
|
||||
"excluded_legacy_sources": [
|
||||
{
|
||||
"file": "IRPColectivas.pdf",
|
||||
"sha256": "06b1ac1310e973c2cc0406396177f9182922988b2ce4f95786f128c8310f907f",
|
||||
"reason": "Decreto21/2002 revogadoLei34/2007 art3;consultation only,not2026calculation"
|
||||
},
|
||||
{
|
||||
"file": "BR252-2022.pdf",
|
||||
"sha256": "3339f1a11099466b01bf7b9024ff36266d056e6a914d1fa67f6b60f0e9f12f2b",
|
||||
"reason": "Filename identifiesdate/issue butactual3rdsupplementDecree74/2022 tourism;notLei20/2022"
|
||||
}
|
||||
],
|
||||
"inventories_reused": [
|
||||
"8143localaccessiblefiles",
|
||||
"39ATforms and189ATlegislation:all inventoriedbeforeselection",
|
||||
"existingModelosinventory andIRPC_IRPSM39structure;no new workbook formulas trusted"
|
||||
],
|
||||
"runtime_delta": [
|
||||
{
|
||||
"path": "resources/js/lib/irpc-withholdings.ts",
|
||||
"sha256_LF": "7a2f92695d5732c3c3525f70f23598bbfd9ab0346429e2997c9e07e324106eb5"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-irpc-withholdings.tsx",
|
||||
"sha256_LF": "e513e9ee9318d71fb4cc40b8b132e6bb5fc6cf88746061fff8c537cb5e88291a"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-form.tsx",
|
||||
"sha256_LF": "01d20f4761933a3e4dd7c776bd4adcf9610ac0435d918a1998ae9f0c3ce3f234"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/project-dossier.ts",
|
||||
"sha256_LF": "df8b224b9472449711d0b69a873fa75f9cc9f4d84dce1a314fc576fae9c6eae2"
|
||||
}
|
||||
],
|
||||
"existing_integrations_additions_only": {
|
||||
"resources/js/components/simulation-form.tsx": 2,
|
||||
"resources/js/lib/project-dossier.ts": 2
|
||||
},
|
||||
"unchanged_previous_resources": 438,
|
||||
"previous_original_PDFs_preserved": 21,
|
||||
"new_PDFs_preserved": 5,
|
||||
"verification": {
|
||||
"new_frontend_distinct": 17,
|
||||
"frontend": "frontend.txt:16pass;directed-cross-context.txt:1pass",
|
||||
"API": "1test/23assertions;guardedloopback55441/simulator_fiscal_delta_20261021_test;DatabaseTransactionsrollback;allpreviousL3/ISPCmapsidentical",
|
||||
"types": "types-final.txt:exit0",
|
||||
"build": "build-final.txt:exit0",
|
||||
"UI": "Actualcomponent calculation/save/recover/report andexport;localfileharness distinguishedfromprivateAPI;reuse existingacquisition at373 demonstrated;UI proof reused for unchanged positive route after final shared-payment-key refinement;directed test proves only that delta",
|
||||
"previous_functional_suites_repeated": false
|
||||
},
|
||||
"examples": {
|
||||
"retained": "base100000;10%;tax10000;remittance6000;outstanding4000;cost2026-10-02beforepayment2026-11-03=>period2026-10",
|
||||
"suffered": "base20000;tax2000;documentedwithholding1500;difference500notannualcredit"
|
||||
},
|
||||
"remaining_calculations_and_mapping": [
|
||||
{
|
||||
"item": "Taxas normais e especiais dos demais rendimentos",
|
||||
"needed": "Corresponder62(1–4)/67 por rendimento e período,com alterações20/2009,4/2012,19/2013 e20/2022. Ficheiro local BR252/2022 contém apenas3.ºsuplemento/Decreto74/2022,semLei20/2022;INM identifica esta na p2731. Texto exacto em falta não é revalidação de autenticidade.",
|
||||
"resolution": "Implementar ramo dirigido após correspondência do texto aplicável;não usar2002 ou taxa genérica."
|
||||
},
|
||||
{
|
||||
"item": "Residentes/EP e dispensa68",
|
||||
"needed": "Corresponder62(5) novo com dispensas68,em especial68(d),titular/rendimento imputável aEP e factos de cada dispensa;sem confundir taxa com obrigação de reter.",
|
||||
"resolution": "Ramo próprio por natureza/dispensa;sem presumirretenção10%ouzero."
|
||||
},
|
||||
{
|
||||
"item": "Convenções/benefícios",
|
||||
"needed": "Convenção invocada,artigo,residência,beneficiário,condições,período e documento aplicável",
|
||||
"resolution": "Só o caso dependente permanece indeterminado;nenhuma taxa reduzida sem esses elementos."
|
||||
},
|
||||
{
|
||||
"item": "Conversão cambial",
|
||||
"needed": "Moeda,montante,data,taxa efonte aplicáveis à operação",
|
||||
"resolution": "Conversão rastreável;sem taxa actual nem relabelagemMZN."
|
||||
},
|
||||
{
|
||||
"item": "Juros/regularizações/guia agregada",
|
||||
"needed": "Regra/taxa/base/período próprios;guias/declarações anteriores e pagamentos para substituições",
|
||||
"resolution": "Não substituir juros porzero;preparação actual não é declaração rectificativa nem liquidação anualIRPC."
|
||||
}
|
||||
],
|
||||
"external_formalities": [
|
||||
{
|
||||
"item": "MRFTA19/39:rubricas eCER digitais/moeda electrónica",
|
||||
"needed": "Correspondência da nova categoria62(5)a/b às rubricas/CERdo quadro7;remissão67(7)para62(6)existe no original,masnova taxa aparece62(5).",
|
||||
"resolution": "Preservar divergência e original;não renumerar,adivinharCERou bloquear cálculo positivo independente."
|
||||
},
|
||||
{
|
||||
"item": "GuiaM39 anterior eMRFTAactual",
|
||||
"needed": "Manter exemplares;instruçõesMRFTAidentificamperíodoúnico e prazonominaldia20mêsseguinte àdedução;calendário/prorrogação eprocedimento2026específico nãoinferidos",
|
||||
"resolution": "Mapa de preparação/exportação;sem preenchimentofinalCER,assinaturaouusodosserviços."
|
||||
},
|
||||
{
|
||||
"item": "ActosAT",
|
||||
"needed": "Submissão,recepção,aceitação eeventualcomprovativo externo",
|
||||
"resolution": "Externos aoportal;exportar nãoexecuta nemcertifica estes actos."
|
||||
}
|
||||
],
|
||||
"preservation": "L3/ISPCbooks unchanged;131L1closures preserved;P001/P058nonblocking;P084/P085closed;statistics/AIdeferred. ISPCinterest/otherexcess/forminstructionsremaininexistingmatrix;IRPSlimitedpriorcase.",
|
||||
"technical_choices": "Snapshotversion1 extension,exactfractions,source fingerprints andsharedpaymentdependencies areimplementationchoices;notnewTempoapprovals. ExistingIVApayments remainboundtoIVA;newIRPCproofregisteredonce/reusedwithoutoverallocation;noautomaticannualcredit.",
|
||||
"schema_backend_configuration_dependency_changes": false,
|
||||
"active_changes": false,
|
||||
"proof_hashes": {
|
||||
"outputs/irpc-l5-20261025/preservation-before-tests.json": "bd4ac1ee0e0d892b204f30baeed8842d00787d4b1f20656479e11ccafaca279f",
|
||||
"outputs/irpc-l5-20261025/frontend.txt": "40bfc0c12fbf5b1f690a7a12570fbdc390f039b9ac845bab4bbddca622bbbcee",
|
||||
"outputs/irpc-l5-20261025/directed-cross-context.txt": "5303e16fb702f8e022ac694965345d2388779af278844a7262d66bcbc630fd16",
|
||||
"outputs/irpc-l5-20261025/api.txt": "f2ec740d0df65c8336abcbea48bf49eecb8bfcc1c70440ba7a8b27d24707d9f3",
|
||||
"outputs/irpc-l5-20261025/types-final.txt": "5346ac8a6566c1c38cb0bbda6a029579df58cd78af78e2c4845d9c82b5d1d76c",
|
||||
"outputs/irpc-l5-20261025/build-final.txt": "28b7601fefa8d992c9ff7dcf95465a84358381d073dc5f537e37d9a48ae36d9e",
|
||||
"outputs/irpc-l5-20261025/ui-report.txt": "545f5fdaabd96e5dad996e6992e0721a8f39ed4dd473d769450ea35bbf39c311",
|
||||
"outputs/irpc-l5-20261025/ui-reuse.txt": "26fc3ae1fbb7c6a5afbc1f57181e93566415bd17d418aceebe7f0d579f99c532",
|
||||
"outputs/irpc-l5-20261025/preparation.html": "dce6700bffbffb9885f80c777ba0d85c61b5303778f6993dd3f9d70eee3d9fde",
|
||||
"outputs/irpc-l5-20261025/active-baseline.json": "2f2d1a9d3e311f12099d2372d0c1a01eb5ff3918a39218c73b668dfbe51c66f8"
|
||||
},
|
||||
"compiled_markers": {
|
||||
"Calcular e guardar retenções IRPC": [
|
||||
"assets/services-D_pyIjFg.js"
|
||||
],
|
||||
"Exportar preparação IRPC": [
|
||||
"assets/services-D_pyIjFg.js"
|
||||
],
|
||||
"Reutilizar contrapartes e documentos existentes": [
|
||||
"assets/services-D_pyIjFg.js"
|
||||
],
|
||||
"Calcular e guardar apuramento trimestral ISPC": [
|
||||
"assets/services-D_pyIjFg.js"
|
||||
],
|
||||
"Calcular e guardar restituição E": [
|
||||
"assets/services-D_pyIjFg.js"
|
||||
]
|
||||
},
|
||||
"active_revision_readonly": {
|
||||
"commit": "5c0cb25cea7826811c389b55a64bcdf5e43b937f",
|
||||
"branch": "main",
|
||||
"built_at": "2026-10-25T16:24:27.707113+00:00",
|
||||
"manifest_sha256": "385a289f9ba26a632aa0164fbba3913cc8dc2fb891641881fc8d2dcda1951626"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,479 @@
|
||||
{
|
||||
"schema_permissions_comparison": {
|
||||
"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
|
||||
},
|
||||
"status": "installed_grouped_ISPC_residual_delta_verified",
|
||||
"authorization": "Tempo: deliver grouped residual delta through Git with fresh backup/restore and delta verification; preserve habitual6af and L3; not full IRPS/ISPC completion.",
|
||||
"candidate_installed": "5c0cb25cea7826811c389b55a64bcdf5e43b937f",
|
||||
"previous_installed": "6af7092e83b9b3932712aa7a9f527393fcec4da8",
|
||||
"fresh_backup": {
|
||||
"stage": "/var/backups/investmoz-ispc-residual-delivery-20261025/backup",
|
||||
"created_at": "2026-10-25T16:20:55Z",
|
||||
"target": {
|
||||
"database": "teste_investidor",
|
||||
"port": 5432
|
||||
},
|
||||
"tables": 174,
|
||||
"files_verified": 369,
|
||||
"dump_sha256": "027d5dfdd06b317c9b480b1673b1b55b91cfb222f2720d039d62801733773cfe",
|
||||
"files_sha256": "1857e41142879c97b0166277546dc01880326dbe832e5520a871cfbf19796d41",
|
||||
"restore_verified": false
|
||||
},
|
||||
"isolated_restore": {
|
||||
"restore_verified": true,
|
||||
"tables_verified": 174,
|
||||
"container_network": "none",
|
||||
"database": "ispc_residual_restore_20261025",
|
||||
"port": 55441,
|
||||
"source_dump_sha256": "027d5dfdd06b317c9b480b1673b1b55b91cfb222f2720d039d62801733773cfe",
|
||||
"active_database_writes": 0
|
||||
},
|
||||
"authenticated_preflight": {
|
||||
"free_bytes": 6490124288,
|
||||
"existing_pipeline_minimum_bytes": 5368709120,
|
||||
"space_guard_satisfied": true,
|
||||
"installed": {
|
||||
"status": "installed",
|
||||
"commit": "6af7092e83b9b3932712aa7a9f527393fcec4da8",
|
||||
"release": "/var/www/investmoz-homologacao/releases/6af7092e83b9b3932712aa7a9f527393fcec4da8-647c8e2e",
|
||||
"previous_release": "/var/www/investmoz-homologacao/releases/fa32a2b9e127da9c4d764dd99ef58ee43b770aa6-ad33eeab",
|
||||
"database_migrations": 0,
|
||||
"content_imports": 0
|
||||
},
|
||||
"current_link": "/var/www/investmoz-homologacao/releases/6af7092e83b9b3932712aa7a9f527393fcec4da8-647c8e2e",
|
||||
"release_revision": {
|
||||
"commit": "6af7092e83b9b3932712aa7a9f527393fcec4da8",
|
||||
"branch": "main",
|
||||
"built_at": "2026-10-25T15:29:39.962545+00:00",
|
||||
"manifest_sha256": "0761702f0aa0f54dd4e7dac14efadf290830c52a5762a97733d82f5332fb44a8"
|
||||
},
|
||||
"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"
|
||||
},
|
||||
"installed_protected_delta": {
|
||||
"installed": {
|
||||
"status": "installed",
|
||||
"commit": "5c0cb25cea7826811c389b55a64bcdf5e43b937f",
|
||||
"release": "/var/www/investmoz-homologacao/releases/5c0cb25cea7826811c389b55a64bcdf5e43b937f-32c4fd00",
|
||||
"previous_release": "/var/www/investmoz-homologacao/releases/6af7092e83b9b3932712aa7a9f527393fcec4da8-647c8e2e",
|
||||
"database_migrations": 0,
|
||||
"content_imports": 0
|
||||
},
|
||||
"runtime_files_verified": 7,
|
||||
"L3_unchanged_runtime_files_verified": 16,
|
||||
"documentary_files_verified": 21,
|
||||
"persistent_files_and_env_verified": 369,
|
||||
"tables_compared": 174,
|
||||
"changed_tables": {
|
||||
"cache": {
|
||||
"before": {
|
||||
"rows": 106,
|
||||
"checksum": "22eb03056dae006c1c92badee23e6109"
|
||||
},
|
||||
"after": {
|
||||
"rows": 106,
|
||||
"checksum": "eec58e1179f9023ab578cd1401bca5de"
|
||||
}
|
||||
},
|
||||
"sessions": {
|
||||
"before": {
|
||||
"rows": 9,
|
||||
"checksum": "cc0d72daaa3994d422a41cc7c84a5f60"
|
||||
},
|
||||
"after": {
|
||||
"rows": 10,
|
||||
"checksum": "98394985102c0779844faa9d537a7b53"
|
||||
}
|
||||
}
|
||||
},
|
||||
"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
|
||||
},
|
||||
"installed_public": {
|
||||
"status": "installed_delta_verified",
|
||||
"served_revision": {
|
||||
"commit": "5c0cb25cea7826811c389b55a64bcdf5e43b937f",
|
||||
"branch": "main",
|
||||
"built_at": "2026-10-25T16:24:27.707113+00:00",
|
||||
"manifest_sha256": "385a289f9ba26a632aa0164fbba3913cc8dc2fb891641881fc8d2dcda1951626"
|
||||
},
|
||||
"manifest_hash_matches": true,
|
||||
"compiled_delta_markers": {
|
||||
"Calcular e guardar operações ocasionais": [
|
||||
"assets/services-Cvwv29Mo.js"
|
||||
],
|
||||
"Calcular e guardar parcela excedente": [
|
||||
"assets/services-Cvwv29Mo.js"
|
||||
],
|
||||
"Preparar e guardar campos B–Geral": [
|
||||
"assets/services-Cvwv29Mo.js"
|
||||
],
|
||||
"Descarregar preparação B–Geral": [
|
||||
"assets/services-Cvwv29Mo.js"
|
||||
],
|
||||
"Calcular e guardar apuramento trimestral ISPC": [
|
||||
"assets/services-Cvwv29Mo.js"
|
||||
],
|
||||
"Calcular e guardar restituição E": [
|
||||
"assets/services-Cvwv29Mo.js"
|
||||
],
|
||||
"Calcular e guardar acompanhamento D": [
|
||||
"assets/services-Cvwv29Mo.js"
|
||||
],
|
||||
"Calcular e guardar substituições C": [
|
||||
"assets/services-Cvwv29Mo.js"
|
||||
]
|
||||
},
|
||||
"assets": [
|
||||
{
|
||||
"file": "assets/services-Cvwv29Mo.js",
|
||||
"status": 200,
|
||||
"sha256": "aa51139b6509bd66927a578e88cd115f497f80f9ae6406d6fb84db7df858340c"
|
||||
},
|
||||
{
|
||||
"file": "assets/project-dossier-BJrlh6zA.js",
|
||||
"status": 200,
|
||||
"sha256": "e800e5bd5bcbb18cb417db0796c9c3e85600fd65c79b9808577148c4805eb3dc"
|
||||
},
|
||||
{
|
||||
"file": "assets/simulation-form-BqVD91Ek.js",
|
||||
"status": 200,
|
||||
"sha256": "85cbd533c5bc92af5a448db71da006d4346d2d0adb09dde825db0aa19ad5048c"
|
||||
},
|
||||
{
|
||||
"file": "assets/project-dossier-document-kUHwyL01.js",
|
||||
"status": 200,
|
||||
"sha256": "575c47d1ad13193cabc84b43fe555551fba677fb617f57c0f7445dc2d0409f98"
|
||||
}
|
||||
],
|
||||
"simulator_status": 200,
|
||||
"simulator_entry": "assets/app-DmFRsc4U.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"
|
||||
},
|
||||
"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": [
|
||||
"value",
|
||||
"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": 9,
|
||||
"after_rows": 12,
|
||||
"changes": [
|
||||
{
|
||||
"key_sha256": "b7fa07a8ea67586994a66f15ffcc4d1a0df81fbe07dc8ade44bfdba20db30651",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "eb22f458a6354ff34d4e80ede2a4c9f3a81c4fa1be35f34fae1dc8d9f20dc294",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "4a0a44f3b0a78e66a5aac0587d8474f21e26cc91018361267b795afcbea203a2",
|
||||
"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"
|
||||
},
|
||||
"pipeline_status": {
|
||||
"service": "ExecMainStatus=0\nActiveState=inactive\nSubState=dead\n",
|
||||
"installed": {
|
||||
"status": "installed",
|
||||
"commit": "5c0cb25cea7826811c389b55a64bcdf5e43b937f",
|
||||
"release": "/var/www/investmoz-homologacao/releases/5c0cb25cea7826811c389b55a64bcdf5e43b937f-32c4fd00",
|
||||
"previous_release": "/var/www/investmoz-homologacao/releases/6af7092e83b9b3932712aa7a9f527393fcec4da8-647c8e2e",
|
||||
"database_migrations": 0,
|
||||
"content_imports": 0
|
||||
},
|
||||
"latest_report": {
|
||||
"status": "installed",
|
||||
"commit": "5c0cb25cea7826811c389b55a64bcdf5e43b937f",
|
||||
"release": "/var/www/investmoz-homologacao/releases/5c0cb25cea7826811c389b55a64bcdf5e43b937f-32c4fd00",
|
||||
"previous_release": "/var/www/investmoz-homologacao/releases/6af7092e83b9b3932712aa7a9f527393fcec4da8-647c8e2e",
|
||||
"database_migrations": 0,
|
||||
"content_imports": 0,
|
||||
"requested_commits": [
|
||||
"5c0cb25cea7826811c389b55a64bcdf5e43b937f"
|
||||
],
|
||||
"finished_at": "2026-10-25T16:24:30.738299+00:00"
|
||||
},
|
||||
"log_file": "1792945281843706519.log",
|
||||
"summaries": [
|
||||
"OK (1 test, 3106 assertions)",
|
||||
"built in 29.13s",
|
||||
"OK (59 tests, 317 assertions)",
|
||||
"# tests 801",
|
||||
"# pass 801",
|
||||
"# fail 0"
|
||||
],
|
||||
"log_bytes": 231911
|
||||
},
|
||||
"runtime_delta": [
|
||||
{
|
||||
"path": "resources/js/lib/ispc-residuals.ts",
|
||||
"sha256_LF": "40d5751c06c7c155ab4b7a17ee2d1653da2fbbf075807fbb8561cdbe19820955"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-ispc-residuals.tsx",
|
||||
"sha256_LF": "a9219d6f41ccbd0ce45659d4f6aa0fa1c169a685840f5cb1e18daeca377d5e18"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-form.tsx",
|
||||
"sha256_LF": "07f7d0d73993ffc2924f1b9b2d21cbc53fb7c95aab959c0820fd7dbc35a1fffa"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/project-dossier.ts",
|
||||
"sha256_LF": "beca4eebd3959afa520061f231de25d064723537bd98deb541712ab448eba369"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/ispc-eligibility.ts",
|
||||
"sha256_LF": "44675c5efacdc1ae19505667611ba3b522d3043cebacc8754597c3a68ca89d25"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-ispc-quarter.tsx",
|
||||
"sha256_LF": "2c2555309b32d4261f14e7164f39314c9188129c2990357fc54090a2468dd2b4"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-ispc-withholding.tsx",
|
||||
"sha256_LF": "e0445d015b72859e48726959aa52686f79b1cf5c2ade29751efa83257068b878"
|
||||
}
|
||||
],
|
||||
"accepted_QA_proofs_reused": {
|
||||
"preservation_before_tests": "outputs/ispc-residuals-20261025/preservation-before-tests.json",
|
||||
"new_frontend_distinct": 19,
|
||||
"frontend_proofs": [
|
||||
"frontend-final.txt:14passed",
|
||||
"directed-final.txt:1additionalpass",
|
||||
"directed-retention-form.txt:1additionalpass",
|
||||
"directed-known-exclusions.txt:1additionalpass",
|
||||
"directed-dispensation-form.txt:1additionalpass",
|
||||
"directed-liberatory-history.txt:1additionalpass"
|
||||
],
|
||||
"API": "1test/22assertionspassed;QAloopback55441/simulator_fiscal_delta_20261021_test;DatabaseTransactionsrollback;APP_BASE_PATHcandidateexplicit",
|
||||
"UI": "Vendedor376/adquirente375separados;3000/5000/20000/25000;guardar→recuperar→relatório;B–Geralpreparadodescarregado;visibilidadeapenasdascondiçõesafectadasverificada",
|
||||
"UI_proofs": [
|
||||
"ui-own-report.txt",
|
||||
"ui-acquisition-report.txt",
|
||||
"ui-occasional-applicability.txt",
|
||||
"ui-regular-applicability.txt",
|
||||
"preparation.html"
|
||||
],
|
||||
"types": "exit0;types-final.txt",
|
||||
"build": "exit0;build-final.txt",
|
||||
"habitual_re_evaluation": "not repeated;arithmetic engines unchanged;24000snapshotpreservedexactly;onlyconditionalvisibilitydeltaexamined"
|
||||
},
|
||||
"manual_functional_tests_repeated": false,
|
||||
"pipeline_checks": "Mandatory existing Git pipeline checks only.",
|
||||
"available": [
|
||||
"Operações ocasionais próprias no âmbito demonstrado, sem segunda prestação sobre retenção liberatória documentada.",
|
||||
"Parcela excedente no caso IRPS qualificado demonstrado; sem taxa automática para outros enquadramentos.",
|
||||
"Preparação/exportação B–Geral com documentos e histórico."
|
||||
],
|
||||
"remaining_calculations": [
|
||||
{
|
||||
"item": "Juros compensatórios",
|
||||
"state": "montante documentado pode ser conservado;fórmula ainda não implementada",
|
||||
"needed": "regra/taxa/base/período/dias efectivamente aplicáveis,no documento/instrução correspondente;sem zero ou taxa presumidos"
|
||||
},
|
||||
{
|
||||
"item": "Outros enquadramentos da parcela excedente",
|
||||
"state": "sem fórmula genérica;casos diferentes do acto isolado qualificado não recebem taxa automática",
|
||||
"needed": "natureza,regime do fornecedor,opção aplicável,base e suporte concretos;encaminhar para o cálculo próprio quando for outro regime,não bloquear o caso positivo implementado"
|
||||
}
|
||||
],
|
||||
"remaining_formal_correspondences_instructions": [
|
||||
{
|
||||
"item": "Quadro2doB–Geral",
|
||||
"needed": "correspondência concreta do mês/ano a inscrever para o trimestre;comunicado fornece meses de pagamento,que não são copiados automaticamente para período"
|
||||
},
|
||||
{
|
||||
"item": "Dispensa inferior500MZN noB–Geral",
|
||||
"needed": "Representação declarativa do imposto apurado com prestação dispensada10-A;sem preencher montanteapurado comozero porpresunção. O cálculo imposto/prestação jáestá implementado."
|
||||
},
|
||||
{
|
||||
"item": "Entrega de retenções",
|
||||
"needed": "modelo/código/instrução próprios12(3)ISPCe57(2k)IRPS;B–Geraldeapoio nãoéapresentadocomocorrespondênciacompletadestasretenções"
|
||||
},
|
||||
{
|
||||
"item": "Regulamentação2026/instruções de envio de facturas",
|
||||
"needed": "texto/exemplar/instrução concreta quando disponibilizado;comunicado transitório já implementado,não se mantém pesquisa genérica como aprovação adicional"
|
||||
},
|
||||
{
|
||||
"item": "Submissão e pagamento",
|
||||
"needed": "actos externos e comprovativos reais;nenhuma operaçãoATfoiexecutadaemQA"
|
||||
}
|
||||
],
|
||||
"external_to_portal": "Actos da AT: exportação não equivale a submissão, recepção, aceitação ou pagamento.",
|
||||
"ISPC_integrally_completed": false,
|
||||
"IRPS_integrally_completed": false,
|
||||
"schema_migrations": 0,
|
||||
"content_imports": 0,
|
||||
"preserved_L3_amounts_MZN": {
|
||||
"documentary": 16,
|
||||
"deduction_this_route": 0,
|
||||
"recognised": 12,
|
||||
"received": 7,
|
||||
"awaiting": 5
|
||||
},
|
||||
"residuals": "L3residuals in existing matrix;P001/P058 nonblocking;P084/P085 closed;statistics/AI deferred.",
|
||||
"proof_hashes": {
|
||||
"outputs/ispc-residual-delivery-20261025/backup.txt": "2c5838cda547495dee6ac3acc1971457b4d6a34399766c142e13501ac16fac61",
|
||||
"outputs/ispc-residual-delivery-20261025/installed-protected.txt": "95a376d4970744f3a0f02757870968f989d87b3549f6aa841676ddf38df4bddf",
|
||||
"outputs/ispc-residual-delivery-20261025/installed-public-delta.json": "f092efaeb3091d3fc345ab2979a5f3fa3ee8ed644d05175e45f393fb78910ac7",
|
||||
"outputs/ispc-residual-delivery-20261025/installed-public.txt": "88facda241ab71035c481c9cc2d4a21c0c2fe5eb4d703d88ffa8557c7602dcb3",
|
||||
"outputs/ispc-residual-delivery-20261025/integration.txt": "52d05eed26351310f897d3b647c605e6fdb0b7d6a570487ff6f7b8a3c11287ed",
|
||||
"outputs/ispc-residual-delivery-20261025/manifest-served.json": "385a289f9ba26a632aa0164fbba3913cc8dc2fb891641881fc8d2dcda1951626",
|
||||
"outputs/ispc-residual-delivery-20261025/operational-delta.txt": "7ba0c56bd9395003f7b384683ee106d00dfa500d98ce61e45d5057548e9451dc",
|
||||
"outputs/ispc-residual-delivery-20261025/preflight.txt": "f05ba4c7c5e636da470a6aeb63332d5a673807d2493213df98ef016acef0770d",
|
||||
"outputs/ispc-residual-delivery-20261025/preparation.json": "49e3ade28b69afddb94cf3076964c8bf61197ae201d4eecb9b1619962c9f8a2d",
|
||||
"outputs/ispc-residual-delivery-20261025/progress.txt": "db8b637be9106ed97abbca2ccef38729509a47f2ee006784e2f6f0a0a485bd87",
|
||||
"outputs/ispc-residual-delivery-20261025/restore.txt": "093b66c345788125f01f02aa93867812a3587107515b5592f7aa999cf3f437cc",
|
||||
"outputs/ispc-residual-delivery-20261025/schema-permissions-after.txt": "76b5a4d8af11e4898a205062766cfb7ab6ec0783200e77e60a1927aa1ca18597",
|
||||
"outputs/ispc-residual-delivery-20261025/schema-permissions-before.txt": "0ab7c964792acdd8917bce256c472e55e034b103500a58b49f020b5084823ed7",
|
||||
"outputs/ispc-residual-delivery-20261025/status-final.txt": "3f0db035a0d53507176ff8af6f9361c813fe0f64975c61f91ea48a6b53ab5ea5",
|
||||
"outputs/ispc-residual-delivery-20261025/status1.txt": "33f1595c6ab8b9f6e6231e2c54ae78c171dbb560e24dc305180061d8a04a5425",
|
||||
"outputs/ispc-residual-delivery-20261025/status2.txt": "7d9f4cb894d96915b622734a2b39f70df8ef0832337f92136e010f51a5abda6d",
|
||||
"outputs/ispc-residual-delivery-20261025/status3.txt": "8414250023b01144b3d2cdabe21e7ad35c89aee6883b938950b50f6642ba0203",
|
||||
"outputs/ispc-residual-delivery-20261025/status4.txt": "0d8002ce39bc890aade37606652c68c7b10bc05db3372da770aab879cce1742d",
|
||||
"outputs/ispc-residual-delivery-20261025/status5.txt": "0d8002ce39bc890aade37606652c68c7b10bc05db3372da770aab879cce1742d",
|
||||
"outputs/ispc-residual-delivery-20261025/status6.txt": "0d8002ce39bc890aade37606652c68c7b10bc05db3372da770aab879cce1742d",
|
||||
"outputs/ispc-residual-delivery-20261025/status7.txt": "3f0db035a0d53507176ff8af6f9361c813fe0f64975c61f91ea48a6b53ab5ea5"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"status": "QA_grouped_compatible_delta_not_installed",
|
||||
"status": "installed_grouped_scoped_residual_delta",
|
||||
"matrix_anchor": "AUD-SIM-INT02",
|
||||
"base_installed": "6af7092e83b9b3932712aa7a9f527393fcec4da8",
|
||||
"branch": "codex/ispc-residuals-qa-20261025",
|
||||
@@ -249,5 +249,12 @@
|
||||
"eligibility_regular_path": "byte-identical except message for occasional route",
|
||||
"source_scope_choices": "Separate books,concrete factinputs and exact cap intervals are implementation choices,not new organisational approvals;no generic legal/provenance revalidation."
|
||||
},
|
||||
"liberatory_history_guard": "Same operation reference/date/quarter/amount with documented prior liberatory withholding cannot generate another own payment without explicit correction reference; history preserved."
|
||||
"liberatory_history_guard": "Same operation reference/date/quarter/amount with documented prior liberatory withholding cannot generate another own payment without explicit correction reference; history preserved.",
|
||||
"QA_status_before_delivery": "QA_grouped_compatible_delta_not_installed",
|
||||
"installation_followup": {
|
||||
"commit": "5c0cb25cea7826811c389b55a64bcdf5e43b937f",
|
||||
"receipt": "ispc-residual-delivery-20261025.json",
|
||||
"ISPC_integrally_completed": false,
|
||||
"IRPS_integrally_completed": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,3 +117,19 @@ Inventários oficiais AT guardados antes da selecção:39formulários,189documen
|
||||
- `Lei-19-2017-IRPS-BR-OCAM.pdf` — SHA256 `c3a0186cb9ad090d7a9533006295dc9bb6a62ad0d6150173cbd49dba4ee4b52d`;original preservado emsource-archive,sem publicação/activação automática.
|
||||
|
||||
Uso:comunicadoATindicaB–Geraltransitório;campos correspondidos por quadro. Modelo30aindacontém75.000/3%antigos epermaneceexcluído dos cálculos2026. IRPS57(2k,4)suporta20%sobre rendimentoilíquido de acto isolado qualificado,nãotaxagenérica de compras excedentes. Citações,URLs ehashes completos no recibo ispc-residuals-qa-20261025.json. Scan/vigência global não foram certificados por esta importação privada.
|
||||
|
||||
## 25/10/2026 — L5 IRPC:fontes e limites concretos
|
||||
|
||||
Reutilizados os inventários de8143ficheiros locais e39formulários/189documentosAT. Código localIRPColectivas.pdféDecreto21/2002,revogadoLei34/2007,nãoaplicado2026. BR252/2022localé3.ºsuplemento turístico;Lei20/2022,p2731identificadaINM,nãoconstadesseexemplar. Sem repetirpesquisa genérica nemexigiroutraaprovaçãodafonte. Lei12/2025 jáaceite fornece62(5)/67(5),PDF19,p2180(19),conferidos visualmente;remissão67(7)→62(6)preservada,nãocorrigidanooriginal. MRFTA19/39AT,p1–2,conferidovisualmenteincluindoinstruções;fichas/códigos/CERnovosficamdelimitados.
|
||||
|
||||
- Lei-34-2007-BR52.pdf;SHA256`cdbef875ad7ccc1517b12faf5de0e893a70ed2ed81e33913cd6dc4a674474e6e`;https://www.dlapiperafrica.com/export/sites/africa/mozambique/insights/legislation-series/tax/downloads/Lei_34_2007.pdf_2063068952.pdf;Art67(3)/68 visually reviewed onPDF19;other text identified.
|
||||
|
||||
- Lei-4-2012-BR3.pdf;SHA256`b8845cef41dcbd9ba50c43dbfa743a56030dd3d348242a107e1ae30279eec121`;https://www.dlapiperafrica.com/export/sites/africa/mozambique/insights/legislation-series/tax/downloads/Lei-n-4.2012-de-23-de-Janeiro-de-2012.pdf_2063068952.pdf;Original inventoried/preserved;full article mapping not yet complete,no rate activated from this copy.
|
||||
|
||||
- Lei-19-2013-BR76.pdf;SHA256`bba96d645cc935ffc3d1a79b9f0f73ac1c693d6459c2ad41f56cd2841accae76`;https://www.dlapiperafrica.com/export/sites/africa/mozambique/insights/legislation-series/tax/downloads/Lei-n_19.2013.pdf_2063068952.pdf;Original inventoried/preserved;full article mapping not yet complete,no rate activated from this copy.
|
||||
|
||||
- Decreto-9-2008-BR16.pdf;SHA256`96fccdecbfdc11c9eafb90fbc6c7bc259f88bbd4c7507d1ac3af1fa03f960697`;https://www.dlapiperafrica.com/export/sites/africa/mozambique/insights/legislation-series/tax/downloads/Decreto_9_2008.pdf_2063068952.pdf;Original inventoried/preserved;full article mapping not yet complete,no rate activated from this copy.
|
||||
|
||||
- AT-MRFTA-19-39.pdf;SHA256`8ea62c53066b482f87078a7a7b1ecf15ca98a12a122e8d7abca63acfc993186b`;https://www.at.gov.mz/api/webdav?path=%2FFORMULARIOS%2FIMPOSTOS%20FISCAIS%2FIRPS%2FIRPS-IRPC%20MRFTA%20-%2019-39.pdf;MRFTA p1–2 visually reviewed;consultation/preparation,not submission.
|
||||
|
||||
Originaisprivadospreservados;sempublicação/activaçãoautomática,semdeclararscanoucertificaçãoefectuados. Lei4/2012,19/2013eRegulamento9/2008foramrecuperados/inventariados;correspondênciaintegralpendente,nãoforamusadosparaactivartaxasantigas. Recibo:irpc-l5-withholdings-qa-20261025.json.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -5822,3 +5822,29 @@ Provas do delta:19testes novos distintos, APIprivadaQA1/22 comrollback, tipos e
|
||||
Na representação do B–Geral, a dispensa inferior a500MZN permanece uma correspondência formal específica: imposto calculado e prestação zero são mostrados separadamente, sem preencher o montante apurado como zero. O código não inventa a instrução de preenchimento. Este ponto não altera nem bloqueia o cálculo10-Ajá demonstrado.
|
||||
|
||||
Uma retenção liberatória documentada na mesma operação permanece no histórico e impede nova prestação própria sem referência concreta de correcção; mudar apenas a opção de liquidação não apaga o benefício anterior. Caso dirigido adicional aceite.
|
||||
|
||||
## 25/10/2026 — entrega conjunta dos residuais ISPC demonstrados
|
||||
|
||||
Instalado `5c0cb25c` pelo Git sobre `6af7092e`, com backup/restauro fresco isolado, conferência dos sete ficheiros afectados, dos ficheiros de preservação habitual/L3, de21originais, dos ficheiros persistentes/configuração e das tabelas com identidade equivalente. As provas funcionais aceites foram reutilizadas; apenas o processo Git existente executou os seus controlos obrigatórios. Sem migrações ou importações. Diferenças operacionais de cache/sessões estão delimitadas no recibo `ispc-residual-delivery-20261025.json`.
|
||||
|
||||
Disponível: operações ocasionais próprias no âmbito demonstrado, parcela excedente no caso IRPS qualificado e preparação/exportação B–Geral. A retenção liberatória anterior não é apagada ao mudar o percurso. Ainda por concluir: fórmula de juros, outros enquadramentos do excedente e correspondências/instruções de preenchimento2026 identificadas no recibo. Actos da AT permanecem externos: exportar não submete nem obtém aceitação. Este incremento não conclui todo o ISPC nem o IRPS. O habitual e L3 permanecem preservados, incluindo16documentais/0dedução nesta via/12reconhecidos/7recebidos/5por receber. P001/P058não bloqueiam;P084/P085fechados;estatísticas/IAadiadas.
|
||||
|
||||
## 25/10/2026 — inventário anterior a L5
|
||||
|
||||
Recuperadas as provas aceites de L3, ISPC instalado5c0cb25c e131fechosL1. Sem perdas confirmadas. IRPS continua limitado ao caso demonstrado; residuaisISPC não bloqueiam retençõesIRPC independentes. Acervo anterior inventariado é reutilizado antes da selecção; contrapartes/documentos/pagamentos serão ligados aos livros existentes sem os duplicar ou apagar. Não houve testes novos nem alteração activa neste inventário.
|
||||
|
||||
## 25/10/2026 — L5: primeiro incremento executável em QA
|
||||
|
||||
Preparado sobre o instalado `5c0cb25c`, com livros de retenções por contexto. Reutiliza fornecedores, documentos e linhas de aquisições e operações de venda existentes. As vendas não têm um cadastro próprio de contraparte: esse dado permanece por completar, sem ser inferido da localização. As edições deliberadas são conservadas; uma alteração na origem mostra os valores divergentes e permite reconciliar o vínculo sem sobrescrever essas edições.
|
||||
|
||||
O cálculo implementado corresponde aos 10% da Lei 12/2025 para bens e serviços digitais e comissões de agentes de moeda electrónica, no recorte não residente e não imputável a estabelecimento estável. Exige a qualificação do titular e devedor como sujeitos a IRPC, a incidência moçambicana e os factos ou pressupostos aplicáveis, sem convenção ou dispensa invocada. A data resulta dos eventos aplicáveis do artigo 67(5), incluindo o reconhecimento do custo; um pagamento posterior não desloca o evento anterior para outro período. A base do rendimento fica separada do IVA.
|
||||
|
||||
No exemplo sintético de rendimento pago, 100.000 MZN produzem retenção de 10.000 MZN; a entrega associada é de 6.000 MZN e o saldo por reconciliar é de 4.000 MZN. No exemplo de retenção sofrida, o cálculo é de 2.000 MZN, com comprovativo de 1.500 MZN e diferença de 500 MZN, sem crédito anual automático. Um comprovativo pode ser repartido entre operações e contextos, sem exceder o montante disponível. Alterações em contextos sem pagamentos partilhados não desactualizam o resultado. Pagamentos de IVA mantêm os vínculos anteriores.
|
||||
|
||||
O MRFTA 19/39 da AT foi conferido nas duas páginas, incluindo as instruções. O quadro 1 identifica retenção, o quadro 2 corresponde a um mês/ano único e o quadro 7 contém base, taxa e imposto. O CER e a correspondência das novas categorias digitais permanecem por esclarecer. A Lei 12/2025 contém uma remissão concreta do artigo 67(7) para 62(6), enquanto a nova taxa aparece em 62(5): essa diferença é preservada, sem renumerar o original ou bloquear o cálculo independente. As instruções do exemplar indicam o prazo nominal de dia 20 do mês seguinte à dedução; não foram presumidos ajustes de calendário, prorrogações ou instruções específicas de 2026. A exportação é um mapa de preparação, sem assinatura, submissão, recepção ou aceitação AT.
|
||||
|
||||
**Saldo de L5:** correspondência das taxas normais e especiais dos artigos 62(1–4), dispensas para residentes/EP, convenções invocadas, conversão cambial, juros, rectificações e guia agregada. A Lei 20/2022 está identificada pela INM na página 2731 do BR 252, mas o exemplar local com essa data/número contém apenas o 3.º suplemento turístico. A ausência desse texto específico não é um pedido de revalidação da sua credibilidade. O código local de 2002 foi identificado como revogado e não foi usado em cálculos de 2026. Cada lacuna afecta apenas o ramo dependente, sem taxa ou zero genéricos.
|
||||
|
||||
Provas do delta: 17 testes novos distintos; API privada em QA, 1 teste e 23 verificações com rollback; tipos e compilação; componente real com cálculo, gravação, recuperação, relatório, exportação e reutilização de uma aquisição anterior. A demonstração visual local em ficheiro é distinguida da prova da API privada. O controlo adicional de pagamentos partilhados foi conferido por caso dirigido, sem repetir as provas funcionais anteriores.
|
||||
|
||||
L3 e ISPC permanecem preservados, assim como os 21 originais anteriores e os 131 fechos de L1. Os residuais ISPC ficam na mesma matriz; o IRPS conserva apenas o caso anteriormente demonstrado. P001/P058 não bloqueiam e P084/P085 permanecem fechados. Estatísticas e IA continuam adiadas. L5, IRPC e IRPS não estão integralmente concluídos. Não houve instalação, migração, importação ou alteração activa. Recibo: `irpc-l5-withholdings-qa-20261025.json`.
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,3 +1,4 @@
|
||||
import {SimulationIrpcWithholdings} from '@/components/simulation-irpc-withholdings';
|
||||
import {SimulationIspcResiduals} from '@/components/simulation-ispc-residuals';
|
||||
import {SimulationIspcWithholding} from '@/components/simulation-ispc-withholding';
|
||||
import {SimulationIspcQuarter} from '@/components/simulation-ispc-quarter';
|
||||
@@ -3509,6 +3510,7 @@ export function SimulationForm() {
|
||||
{(current.id === 'financial' || current.id === 'tax') && <SimulationIspcQuarter draft={draft} onChange={change} />}
|
||||
{(current.id === 'financial' || current.id === 'tax') && <SimulationIspcWithholding draft={draft} onChange={change} />}
|
||||
{(current.id === 'financial' || current.id === 'tax') && <SimulationIspcResiduals draft={draft} onChange={change} />}
|
||||
{(current.id === 'financial' || current.id === 'tax') && <SimulationIrpcWithholdings draft={draft} onChange={change} />}
|
||||
{(current.id === 'financial' || current.id === 'tax') && <SimulationMonthlyIva draft={draft} busy={isCalculating} onCalculate={runSimulation} onChange={change} />}
|
||||
{current.id === 'financial' &&
|
||||
(draft.detail_level === 'advanced' ||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import {useState} from 'react';
|
||||
import {irpcBook,saveIrpcBook,reuseIrpcSources,irpcSources,newIrpcRow,recordIrpc,irpcKinds,irpcReportRows,irpcPreparationHtml,type IrpcBook,type IrpcRow,type IrpcEvent,type IrpcPayment} from '@/lib/irpc-withholdings';
|
||||
export function SimulationIrpcWithholdings({draft,onChange}:{draft:Record<string,unknown>;onChange:(field:string,value:string)=>void}){
|
||||
const[error,setError]=useState('');if(draft.fiscal_component!=='irpc'&&draft.fiscal_component!=='withholding')return null;
|
||||
try{const book=irpcBook(draft),sources=irpcSources(draft),save=(b:IrpcBook)=>{try{onChange('modelo_a_snapshot',saveIrpcBook(draft,b));setError('');}catch(e){setError(String(e));}},change=(id:string,p:Partial<IrpcRow>)=>save({...book,rows:book.rows.map(r=>r.id===id?{...r,...p}:r)}),uid=()=>crypto.randomUUID();
|
||||
const field=(r:IrpcRow,k:keyof IrpcRow,label:string,type='text')=><label className="block">{label}<input className="block rounded border p-2" type={type} value={String(r[k]??'')} onChange={e=>change(r.id,{[k]:e.target.value})}/></label>;
|
||||
const select=(r:IrpcRow,k:keyof IrpcRow,label:string,options:Array<[string,string]>)=><label className="block">{label}<select className="block rounded border p-2" value={String(r[k]??'')} onChange={e=>change(r.id,{[k]:e.target.value})}>{options.map(([v,l])=><option key={v} value={v}>{l}</option>)}</select></label>;
|
||||
const yn:Array<[string,string]>=[['','Por esclarecer'],['yes','Sim'],['no','Não']];
|
||||
const eventLabels:Array<[IrpcEvent['kind'],string]>=[['cost','Reconhecimento do custo'],['payment','Pagamento do rendimento'],['maturity','Vencimento, ainda que presumido'],['availability','Colocação à disposição'],['liquidation','Liquidação'],['quantification','Apuramento do quantitativo']];
|
||||
const paymentChange=(id:string,p:Partial<IrpcPayment>)=>save({...book,payments:book.payments.map(v=>v.id===id?{...v,...p}:v)});
|
||||
return<section aria-label="Retenções de IRPC" className="space-y-4 rounded border p-4"><h4>IRPC — retenções e preparação documental</h4><p>Reutilize documentos e contrapartes existentes. Classifique o rendimento e o evento aplicável:facturar,pagar e reconhecer o custo são factos distintos. Este incremento calcula10% nos casos digitais/moeda electrónica não residentes,não imputáveis aEP,documentados abaixo. Outros enquadramentos ficam visíveis e indeterminados,sem bloquear estes casos.</p>{error&&<p role="alert">{error}</p>}
|
||||
<button type="button" className="rounded border p-2" onClick={()=>{try{save(reuseIrpcSources(draft,book));}catch(e){setError(String(e));}}}>Reutilizar contrapartes e documentos existentes</button><button type="button" className="rounded border p-2" onClick={()=>save({...book,rows:[...book.rows,newIrpcRow(uid())]})}>Acrescentar rendimento IRPC</button>
|
||||
{book.rows.map(r=><fieldset key={r.id} className="space-y-3 rounded border p-3"><legend>{r.description||r.documentRef||'Rendimento por identificar'}</legend>{r.source&&<p>Origem:{r.source.id}. Entradas deliberadamente editadas são conservadas. {sources.find(s=>s.id===r.source!.id)?.key!==r.source.key&&<><strong>Origem alterada ou indisponível.</strong>{sources.find(s=>s.id===r.source!.id)&&<><span> Base actual:{r.base};base na origem:{sources.find(s=>s.id===r.source!.id)?.amount||'desconhecida'};documento actual:{r.documentRef};origem:{sources.find(s=>s.id===r.source!.id)?.documentRef||'desconhecido'}.</span><button type="button" className="rounded border p-2" onClick={()=>{const s=sources.find(s=>s.id===r.source!.id)!;if(window.confirm('Confirma a reconciliação da origem indicada, conservando as entradas deliberadas e os resultados anteriores no histórico?'))change(r.id,{source:{...r.source!,key:s.key}});}}>Conservar edições e reconciliar origem</button></>}</>}</p>}
|
||||
<div className="grid gap-3 md:grid-cols-2">{select(r,'direction','Perspectiva',[['paid','Rendimento pago — retenção a entregar'],['received','Rendimento recebido — retenção sofrida']])}{field(r,'counterparty','Contraparte')}{field(r,'documentRef','Documento / operação / linha distinta')}{field(r,'description','Descrição')}{select(r,'origin','Origem',[['projected','Projecção com pressupostos'],['effective','Factos documentados']])}{select(r,'kind','Natureza do rendimento',irpcKinds)}{field(r,'base','Base do rendimento, separada do IVA')}{field(r,'currency','Moeda da base')}{select(r,'recipientIrpc','Titular sujeito a IRPC neste rendimento',yn)}{select(r,'payerIrpc','Devedor sujeito a IRPC',yn)}{select(r,'residence','Residência do titular',[['','Por esclarecer'],['resident','Residente'],['nonresident','Não residente']])}{r.residence==='nonresident'&&<>{select(r,'permanentEstablishment','Existe estabelecimento estável em Moçambique',yn)}{r.permanentEstablishment==='yes'&&select(r,'attributable','Rendimento imputável ao estabelecimento',yn)}</>}{select(r,'mozambique','Rendimento obtido em Moçambique, conforme qualificação legal',yn)}{field(r,'qualificationRef','Suporte / pressuposto da qualificação, residência e incidência')}{select(r,'specialRelief','Convenção, isenção ou dispensa aplicável',[['','Por esclarecer'],['none','Não invocada neste caso qualificado'],['claimed','Invocada — correspondência específica necessária']])}{r.specialRelief==='claimed'&&field(r,'reliefRef','Artigo, condições, período e prova invocados')}</div>
|
||||
<h5>Eventos aplicáveis — artigo67(5), versão2026</h5>{r.events.map((e,n)=><div key={n} className="grid gap-2 md:grid-cols-3"><label>Evento<select value={e.kind} onChange={ev=>change(r.id,{events:r.events.map((v,j)=>j===n?{...v,kind:ev.target.value as IrpcEvent['kind']}:v)})}>{eventLabels.map(([k,l])=><option key={k} value={k}>{l}</option>)}</select></label><label>Data<input type="date" value={e.date} onChange={ev=>change(r.id,{events:r.events.map((v,j)=>j===n?{...v,date:ev.target.value}:v)})}/></label><label>Suporte / pressuposto<input value={e.reference} onChange={ev=>change(r.id,{events:r.events.map((v,j)=>j===n?{...v,reference:ev.target.value}:v)})}/></label></div>)}<button type="button" onClick={()=>change(r.id,{events:[...r.events,{kind:'cost',date:'',reference:''}]})}>Acrescentar evento aplicável</button>{select(r,'eventsComplete','Todos os eventos aplicáveis identificados, incluindo custo quando aplicável',yn)}
|
||||
{select(r,'settlement','Reconciliação da entrega / prova de retenção sofrida',[['','Por esclarecer'],['none','Ainda sem entrega / prova'],['complete','Pagamentos / provas associados completos']])}{field(r,'settlementRef','Referência da situação declarada')}
|
||||
{r.allocations.map((a,n)=><div key={n}><label>Pagamento / prova existente<select value={a.paymentId} onChange={e=>change(r.id,{allocations:r.allocations.map((v,j)=>j===n?{...v,paymentId:e.target.value}:v)})}><option value="">Seleccione</option>{book.payments.map(p=><option key={p.id} value={p.id}>{p.reference} — {p.amount} {p.currency} — {p.kind}</option>)}</select></label><label>Parcela usada<input value={a.amount} onChange={e=>change(r.id,{allocations:r.allocations.map((v,j)=>j===n?{...v,amount:e.target.value}:v)})}/></label></div>)}{r.settlement==='complete'&&<button type="button" onClick={()=>change(r.id,{allocations:[...r.allocations,{paymentId:'',amount:''}]})}>Associar pagamento/prova existente</button>}
|
||||
</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>)}</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>;
|
||||
}catch(e){return<p role="alert">{String(e)}</p>;}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import {acquisitionBook,acquisitionScope} from './iva-acquisitions.ts';
|
||||
import {salesBook} from './iva-operations.ts';
|
||||
import {closureFingerprint} from './iva-closure.ts';
|
||||
import {addFractions,decimalFraction,fraction,fractionPresentation,subtractFractions,type FiscalFraction} from './fiscal-rational.ts';
|
||||
|
||||
export const irpcWithholdingVersion='irpc-L5/1';
|
||||
export const irpcWithholdingSource='Lei12/2025,BR248/2025,PDF19/p2180(19):arts5(5/7),20(2)c,62(5)a/b e67(5/7);CIRPC Lei34/2007 arts2,67(3),68,PDF18–19/p768(72–73). Art67(7)remete62(6),enquanto taxa nova consta62(5):divergência formal preservada,sem renumerar o original. MRFTA19/39AT,p1–2. Código2002 revogado não usado. Outros regimes/taxas dependem da correspondência específica,sem zero presumido.';
|
||||
type Choice=''|'yes'|'no';
|
||||
export type IrpcKind=''|'digital_goods'|'digital_services'|'emoney_commission'|'royalty'|'equipment'|'rent'|'capital'|'services'|'other';
|
||||
export const irpcKinds:Array<[IrpcKind,string]>=[['','Por identificar'],['digital_goods','Transmissão de bens digitais'],['digital_services','Prestação de serviços digitais'],['emoney_commission','Comissão de agente de moeda electrónica'],['royalty','Propriedade intelectual/industrial'],['equipment','Uso de equipamento'],['rent','Rendimento predial'],['capital','Aplicação de capitais'],['services','Outras prestações/intermediação'],['other','Outro enquadramento']];
|
||||
export type IrpcEvent={kind:'cost'|'payment'|'maturity'|'availability'|'liquidation'|'quantification';date:string;reference:string};
|
||||
export type IrpcPayment={id:string;kind:'beneficiary'|'remittance'|'withholding_proof';reference:string;date:string;amount:string;currency:string};
|
||||
export type IrpcRow={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={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']};
|
||||
export type IrpcBook={version:1;rows:IrpcRow[];payments:IrpcPayment[];result?:{version:string;key:string;calculatedAt:string;inputs:IrpcRow[];payments:IrpcPayment[];rows:IrpcResult[]};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.');
|
||||
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 p of b.payments)if(!p||!['id','reference','date','amount','currency'].every(k=>typeof p[k as keyof IrpcPayment]==='string')||!['beneficiary','remittance','withholding_proof'].includes(p.kind))throw Error('Pagamento IRPC inválido;originais preservados.');
|
||||
if(b.result&&(!Array.isArray(b.result.rows)||!Array.isArray(b.result.inputs)||typeof b.result.key!=='string'||b.result.version!==irpcWithholdingVersion))throw Error('Resultado IRPC não recuperável.');
|
||||
if(b.payments.some((p,i)=>p.reference.trim()&&b.payments.some((q,j)=>i!==j&&q.kind===p.kind&&q.reference.trim()===p.reference.trim()&&q.date===p.date&&q.currency===p.currency)))throw Error('Pagamento/prova já registado:reutilize a referência existente em vez de duplicar.');
|
||||
if(new Set(b.rows.map(r=>r.id)).size!==b.rows.length||new Set(b.payments.map(p=>p.id)).size!==b.payments.length)throw Error('Identificadores IRPC repetidos.');
|
||||
}
|
||||
export function irpcBook(d:Record<string,unknown>):IrpcBook{const b=map(d)[acquisitionScope(d)]??{version:1,rows:[],payments:[]};validate(b);return b;}
|
||||
export function saveIrpcBook(d:Record<string,unknown>,b:IrpcBook):string{validate(b);return JSON.stringify({...store(d),irpcWithholdings:{...map(d),[acquisitionScope(d)]:b}});}
|
||||
export function irpcSources(d:Record<string,unknown>){return[
|
||||
...acquisitionBook(d).rows.map(r=>({id:'acquisition:'+r.id,key:closureFingerprint(r),counterparty:r.supplier,documentRef:r.documentRef+(r.documentLine?' / linha '+r.documentLine:''),description:r.description,amount:r.net,currency:acquisitionBook(d).currency,direction:'paid' as const,origin:r.origin})),
|
||||
...salesBook(d).rows.map(r=>({id:'sale:'+r.id,key:closureFingerprint(r),counterparty:'',documentRef:r.evidence,description:r.description,amount:r.basis==='net'?r.amount:'',currency:r.sourceCurrency,direction:'received' as const,origin:'projected' as const})),
|
||||
];}
|
||||
export function reuseIrpcSources(d:Record<string,unknown>,b:IrpcBook):IrpcBook{validate(b);const rows=[...b.rows];for(const s of irpcSources(d))if(!rows.some(r=>r.source?.id===s.id)){const r={...newIrpcRow('irpc:'+s.id),counterparty:s.counterparty,documentRef:s.documentRef,description:s.description,base:s.amount,currency:s.currency,direction:s.direction,origin:s.origin};rows.push({...r,source:{id:s.id,key:s.key,fingerprint:closureFingerprint(r)}});}return{...b,rows};}
|
||||
function sourceIssue(d:Record<string,unknown>,r:IrpcRow){if(!r.source)return'';const s=irpcSources(d).find(s=>s.id===r.source!.id);return !s?'Operação de origem não está disponível neste contexto;vínculo e edições conservados.':s.key!==r.source.key?'Documento/operação de origem alterado;compare o histórico e reconcilie a divergência antes de calcular.':'';}
|
||||
const date=(s:string)=>/^20\d{2}-(0[1-9]|1[0-2])-\d{2}$/.test(s)&&!Number.isNaN(Date.parse(s))&&new Date(s).toISOString().slice(0,10)===s;
|
||||
const positive=(f:FiscalFraction)=>BigInt(f.numerator)>0n;
|
||||
export function evaluateIrpc(d:Record<string,unknown>,r:IrpcRow):IrpcResult{
|
||||
const v:IrpcResult={id:r.id,status:'indeterminate',reason:'Identifique o rendimento e os intervenientes.',period:'',eventDate:'',nature:null,rate:null,base:null,tax:null,settled:null,outstanding:null,direction:r.direction};
|
||||
const issue=sourceIssue(d,r);if(issue)return{...v,reason:issue};
|
||||
if(r.recipientIrpc==='no')return{...v,status:'outside_scope',reason:'Titular fora de IRPC;sem converter em IRPS ou em isenção.'};
|
||||
if(!r.counterparty.trim()||!r.documentRef.trim()||r.recipientIrpc!=='yes'||r.payerIrpc!=='yes'||!r.residence||!r.kind||!r.qualificationRef.trim())return{...v,reason:'Faltam contraparte,documento,qualificação do rendimento,titular IRPC,devedor IRPC ou residência. A via devedor IRPS exige âmbito próprio.'};
|
||||
if(r.currency!=='MZN')return{...v,reason:'Base em moeda diferente de MZN: falta conversão documentada com data,taxa e fonte;não relabelar nem usar taxa actual.'};
|
||||
if(r.mozambique!=='yes')return{...v,reason:'Falta correspondência territorial aos arts5/67;ser estrangeiro não resolve incidência nem isenção.'};
|
||||
if(r.specialRelief!=='none')return{...v,reason:'Convenção,isenção ou dispensa invocada/desconhecida:identificar artigo,período,condições e prova;sem taxa reduzida ou zero automáticos.'};
|
||||
if(!r.eventsComplete||r.eventsComplete!=='yes'||!r.events.length||r.events.some(e=>!date(e.date)||!e.reference.trim()))return{...v,reason:'Identifique os eventos aplicáveis67(5),incluindo reconhecimento do custo,e confirme a sua completude com suporte;uma factura ou pagamento isolado não fixa o momento.'};
|
||||
v.eventDate=r.events.map(e=>e.date).sort()[0];v.period=v.eventDate.slice(0,7);
|
||||
if(!v.eventDate.startsWith('2026-'))return{...v,reason:'Primeiro evento aplicável fora de2026;corresponder versão/período próprio,sem deslocar para o pagamento posterior.'};
|
||||
try{v.base=decimalFraction(r.base);}catch{return{...v,reason:'Base do rendimento necessária,em decimal MZN separado do IVA;desconhecido não é zero.'};}
|
||||
if(!['digital_goods','digital_services','emoney_commission'].includes(r.kind))return{...v,reason:'Rendimento preservado paraL5:confrontar arts62/67/68 e alterações20/2009,4/2012,19/2013,20/2022. O exemplar2002 não suporta uma taxa2026;normal/especial não presumidos.'};
|
||||
if(r.residence==='nonresident'&&r.permanentEstablishment==='no')v.nature='definitive';
|
||||
else if(r.residence==='nonresident'&&r.permanentEstablishment==='yes'&&r.attributable==='no')v.nature='definitive';
|
||||
else return{...v,reason:'Para residente ou rendimento imputável aEP,corresponder concretamente a taxa62(5) com as dispensas68,em especial68(d),antes da liquidação. EP desconhecido permanece indeterminado;90dias/serviço digital não criam ausência deEP automática.'};
|
||||
v.rate=10;v.tax=fraction(BigInt(v.base.numerator),BigInt(v.base.denominator)*10n);v.status='calculated';v.reason='Taxa10%62(5) sobre o rendimento identificado;retenção definitiva67(3) no recorte não residente/não imputável aEP. Qualificação e eventos documentados ou pressupostos expressos;procedimento67(7)/remissão62(6) e CER permanecem separados. Não é IVA nem crédito anual automático.';return v;
|
||||
}
|
||||
function sharedPayments(d:Record<string,unknown>,b:IrpcBook){const used=b.payments.filter(p=>b.rows.some(r=>r.allocations.some(a=>a.paymentId===p.id)));return Object.entries(map(d)).filter(([ctx])=>ctx!==acquisitionScope(d)).flatMap(([ctx,other])=>{validate(other);const payments=other.payments.filter(q=>used.some(p=>p.reference.trim()===q.reference.trim()&&p.date===q.date&&p.currency===q.currency&&p.kind===q.kind));return payments.length?[[ctx,payments,other.rows.flatMap(r=>r.allocations.filter(a=>payments.some(p=>p.id===a.paymentId)))]]:[];});}
|
||||
function key(d:Record<string,unknown>,b:IrpcBook){return JSON.stringify([irpcWithholdingVersion,acquisitionScope(d),b.rows,b.payments,b.rows.map(r=>sourceIssue(d,r)),sharedPayments(d,b)]);}
|
||||
export function irpcCurrent(d:Record<string,unknown>,b:IrpcBook){return !!b.result&&b.result.key===key(d,b);}
|
||||
export function recordIrpc(d:Record<string,unknown>,b:IrpcBook,now=new Date().toISOString()):IrpcBook{
|
||||
validate(b);const rows=b.rows.map(r=>evaluateIrpc(d,r));const allocations=new Map<string,FiscalFraction[]>();
|
||||
const fail=(i:number,reason:string)=>{rows[i]={...rows[i],status:'indeterminate',reason,settled:null,outstanding:null};};
|
||||
b.rows.forEach((r,i)=>{
|
||||
if(rows[i].status!=='calculated')return;
|
||||
if(r.settlement==='none'&&r.allocations.length){fail(i,'Ausência de entrega/prova contradiz pagamentos associados;conserve e reconcilie.');return;}
|
||||
if(!r.settlement||!r.settlementRef.trim()){rows[i].reason+=' Entrega à AT/retenção sofrida ainda não reconciliada;não presumir zero pago.';return;}
|
||||
const parts:FiscalFraction[]=[];const seen=new Set<string>();
|
||||
for(const a of r.allocations){const p=b.payments.find(p=>p.id===a.paymentId);try{
|
||||
if(!p||seen.has(p.id)||p.currency!=='MZN'||!date(p.date)||p.date<rows[i].eventDate||!p.reference.trim()||p.kind!==(r.direction==='paid'?'remittance':'withholding_proof'))throw Error();
|
||||
seen.add(p.id);const amount=decimalFraction(a.amount);decimalFraction(p.amount);parts.push(amount);allocations.set(p.id,[...(allocations.get(p.id)??[]),amount]);
|
||||
}catch{fail(i,'Pagamento/prova ausente,incoerente,repetido ou de outra natureza:pagamento ao fornecedor e imposto IVA não são entrega IRPC.');return;}}
|
||||
const settled=addFractions(parts);if(positive(subtractFractions(settled,rows[i].tax!))){fail(i,'Valor associado excede a retenção calculada;reconcilie excesso sem gerar crédito automático.');return;}
|
||||
rows[i].settled=settled;rows[i].outstanding=subtractFractions(rows[i].tax!,settled);
|
||||
});
|
||||
for(const [id,parts]of allocations){const p=b.payments.find(p=>p.id===id)!;let excess=false;try{const elsewhere:FiscalFraction[]=[];for(const [ctx,other]of Object.entries(map(d))){if(ctx===acquisitionScope(d))continue;validate(other);for(const q of other.payments.filter(q=>q.reference.trim()===p.reference.trim()&&q.date===p.date&&q.kind===p.kind&&q.currency===p.currency)){if(q.amount!==p.amount)throw Error('Montantes divergentes');for(const r of other.rows)for(const a of r.allocations.filter(a=>a.paymentId===q.id))elsewhere.push(decimalFraction(a.amount));}}excess=positive(subtractFractions(addFractions([...parts,...elsewhere]),decimalFraction(p.amount)));}catch{excess=true;}if(excess)b.rows.forEach((r,i)=>{if(r.allocations.some(a=>a.paymentId===id))fail(i,'Mesmo pagamento utilizado além do montante disponível;reparta sem duplicação.');});}
|
||||
// An identical source document/income cannot become a second tax just by adding a row.
|
||||
b.rows.forEach((r,i)=>{if(b.rows.some((s,j)=>j!==i&&(r.source&&s.source?.id===r.source.id||r.documentRef.trim()&&s.documentRef.trim()===r.documentRef.trim()&&s.counterparty.trim()===r.counterparty.trim()&&s.direction===r.direction))){fail(i,'Documento/rendimento repetido neste livro;identifique parcelas próprias antes de somar,sem duplicação.');}});
|
||||
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}};
|
||||
}
|
||||
const amount=(v:FiscalFraction|null)=>v?fractionPresentation(v).decimal+' MZN':'Indeterminado';
|
||||
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}`,`${v.status};${i.direction==='paid'?'retida a entregar':'sofrida'};base:${amount(v.base)};taxa:${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??'natureza por determinar'};${irpcCurrent(d,b)?'Entradas correspondentes':'Histórico;entradas/origem divergentes'};${r.calculatedAt};${irpcWithholdingSource}`];});}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=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[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>';}
|
||||
@@ -1,3 +1,4 @@
|
||||
import {irpcReportRows} from './irpc-withholdings.ts';
|
||||
import {ispcResidualReportRows} from './ispc-residuals.ts';
|
||||
import {ispcWithholdingReportRows} from './ispc-withholding.ts';
|
||||
import {ispcQuarterReportRows} from './ispc-quarter.ts';
|
||||
@@ -693,6 +694,7 @@ export function buildProjectDossierRows(
|
||||
rows.push(...ispcQuarterReportRows(draft));
|
||||
rows.push(...ispcWithholdingReportRows(draft));
|
||||
rows.push(...ispcResidualReportRows(draft));
|
||||
rows.push(...irpcReportRows(draft));
|
||||
rows.push(...fiscalCalculationGapReportRows(draft));
|
||||
rows.push(...l3FormPreparationReportRows(draft));
|
||||
const operationRows = salesReportRows(draft);
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<?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 IrpcWithholdingDraftTest extends TestCase
|
||||
{
|
||||
use DatabaseTransactions;
|
||||
public function test_irpc_recovers_private_retentions_payments_and_history_without_changing_installed_books():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()]));
|
||||
$d=json_decode(file_get_contents(base_path('tests/frontend/fixtures/irpc-l5-preserved.json')),true,flags:JSON_THROW_ON_ERROR);$personal=['person_type'=>$d['person_type']];unset($d['person_type']);
|
||||
$id=$this->postJson('/api/v1/simulation-drafts',['personal_snapshot'=>$personal,'economic_snapshot'=>$d],['Idempotency-Key'=>(string)Str::uuid()])->assertCreated()->json('data.id');$recovered=$this->getJson('/api/v1/simulation-drafts/'.$id)->assertOk()->json('data.economic_snapshot');$this->assertSame($d['modelo_a_snapshot'],$recovered['modelo_a_snapshot']);
|
||||
$s=json_decode($recovered['modelo_a_snapshot'],true,flags:JSON_THROW_ON_ERROR);$key=$d['context_opportunity'];$b=$s['irpcWithholdings'][$key];$this->assertSame('calculated',$b['result']['rows'][0]['status']);$this->assertSame(['numerator'=>'10000','denominator'=>'1'],$b['result']['rows'][0]['tax']);$this->assertSame(['numerator'=>'6000','denominator'=>'1'],$b['result']['rows'][0]['settled']);$this->assertSame(['numerator'=>'4000','denominator'=>'1'],$b['result']['rows'][0]['outstanding']);$this->assertSame('2026-10',$b['result']['rows'][0]['period']);$this->assertSame('received',$b['result']['rows'][1]['direction']);$this->assertSame(['numerator'=>'2000','denominator'=>'1'],$b['result']['rows'][1]['tax']);$this->assertSame(['numerator'=>'500','denominator'=>'1'],$b['result']['rows'][1]['outstanding']);
|
||||
$old=json_decode(file_get_contents(base_path('tests/frontend/fixtures/ispc-residual-preserved.json')),true,flags:JSON_THROW_ON_ERROR);foreach(json_decode($old['modelo_a_snapshot'],true,flags:JSON_THROW_ON_ERROR)as$k=>$v)$this->assertSame($v,$s[$k],$k.' must be preserved');
|
||||
if(is_dir(base_path('outputs/irpc-l5-20261025')))file_put_contents(base_path('outputs/irpc-l5-20261025/recovered.json'),json_encode($recovered,JSON_THROW_ON_ERROR|JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,28 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import {irpcBook,newIrpcRow,recordIrpc,saveIrpcBook,reuseIrpcSources,irpcCurrent,irpcReportRows,irpcPreparationHtml} from '../../resources/js/lib/irpc-withholdings.ts';
|
||||
import {acquisitionBook,newAcquisition,saveAcquisitionBook} from '../../resources/js/lib/iva-acquisitions.ts';
|
||||
const seed=JSON.parse(fs.readFileSync(new URL('./fixtures/ispc-residual-preserved.json',import.meta.url),'utf8'));
|
||||
const draft=()=>({...structuredClone(seed),fiscal_component:'irpc'});
|
||||
const row=(id='digital')=>({...newIrpcRow(id),counterparty:'SYNTHETIC QA fornecedor não residente',documentRef:'SYNTHETIC-'+id,base:'100000',kind:'digital_services',recipientIrpc:'yes',payerIrpc:'yes',residence:'nonresident',permanentEstablishment:'no',mozambique:'yes',qualificationRef:'SYNTHETIC QA: SaaS utilizado em Moçambique, devedor nacional,sem EP,Lei12/2025 arts5(5/7)/62(5)b',specialRelief:'none',eventsComplete:'yes',events:[{kind:'cost',date:'2026-10-02',reference:'SYNTHETIC custo'},{kind:'payment',date:'2026-11-03',reference:'SYNTHETIC pagamento do rendimento'}],settlement:'none',settlementRef:'SYNTHETIC sem entrega'});
|
||||
const book=(r=row())=>({version:1,rows:[r],payments:[]});
|
||||
test('positive digital goods/services and e-money have exact 10 percent and first applicable cost event',()=>{for(const kind of ['digital_goods','digital_services','emoney_commission']){const r=recordIrpc(draft(),book({...row(),kind})).result.rows[0];assert.equal(r.status,'calculated');assert.deepEqual(r.tax,{numerator:'10000',denominator:'1'});assert.equal(r.eventDate,'2026-10-02');assert.equal(r.nature,'definitive');assert.equal(r.period,'2026-10');}});
|
||||
test('unknown territory, recipient, payer, EP, event completeness, relief never become zero or nonapplicable',()=>{for(const patch of [{mozambique:''},{recipientIrpc:''},{payerIrpc:''},{permanentEstablishment:''},{eventsComplete:''},{specialRelief:''}]){const r=recordIrpc(draft(),book({...row(),...patch})).result.rows[0];assert.equal(r.status,'indeterminate');assert.equal(r.tax,null);}});
|
||||
test('resident and income attributable to EP preserve concrete 68d mapping gap; no 10 or20 automatic',()=>{for(const patch of [{residence:'resident'},{permanentEstablishment:'yes',attributable:'yes'}]){const r=recordIrpc(draft(),book({...row(),...patch})).result.rows[0];assert.equal(r.tax,null);assert.match(r.reason,/68/);}});
|
||||
test('nonresident with EP and explicitly nonattributable income has definitive positive branch',()=>assert.equal(recordIrpc(draft(),book({...row(),permanentEstablishment:'yes',attributable:'no'})).result.rows[0].status,'calculated'));
|
||||
test('ordinary and exceptional income need specific amended rule; no legacy2002 rates',()=>{for(const kind of ['royalty','rent','capital','equipment','services','other']){const r=recordIrpc(draft(),book({...row(),kind})).result.rows[0];assert.equal(r.tax,null);assert.match(r.reason,/20\/2022/);}});
|
||||
test('event before2026 is not moved to later payment; impossible dates rejected',()=>{for(const date of ['2025-12-31','2026-02-30']){const r=row();r.events[0].date=date;assert.equal(recordIrpc(draft(),book(r)).result.rows[0].status,'indeterminate');}});
|
||||
test('foreign amount and missing base remain indeterminate, explicit zero is distinct',()=>{for(const patch of [{currency:'USD'},{base:''},{base:'-1'}])assert.equal(recordIrpc(draft(),book({...row(),...patch})).result.rows[0].tax,null);assert.deepEqual(recordIrpc(draft(),book({...row(),base:'0'})).result.rows[0].tax,{numerator:'0',denominator:'1'});});
|
||||
test('partial remittance is separate from tax and recipient payment cannot settle tax',()=>{const r={...row(),settlement:'complete',allocations:[{paymentId:'p1',amount:'6000'}]};const b={...book(r),payments:[{id:'p1',kind:'remittance',reference:'SYNTHETIC-AT1',date:'2026-10-20',amount:'6000',currency:'MZN'}]};const out=recordIrpc(draft(),b).result.rows[0];assert.deepEqual(out.settled,{numerator:'6000',denominator:'1'});assert.deepEqual(out.outstanding,{numerator:'4000',denominator:'1'});b.payments[0].kind='beneficiary';assert.equal(recordIrpc(draft(),b).result.rows[0].status,'indeterminate');});
|
||||
test('received withholding needs matching proof; no yearly credit or buyer debt generated',()=>{const r={...row(),direction:'received',settlement:'complete',allocations:[{paymentId:'p1',amount:'1500'}],base:'20000'};const b={...book(r),payments:[{id:'p1',kind:'withholding_proof',reference:'SYNTHETIC-PROOF1',date:'2026-10-20',amount:'1500',currency:'MZN'}]};const out=recordIrpc(draft(),b).result.rows[0];assert.equal(out.direction,'received');assert.deepEqual(out.outstanding,{numerator:'500',denominator:'1'});assert.equal('annualCredit'in out,false);});
|
||||
test('unknown settlement stays null and no-payment with allocations is contradictory',()=>{let r=recordIrpc(draft(),book({...row(),settlement:''})).result.rows[0];assert.equal(r.settled,null);assert.equal(r.tax.numerator,'10000');r=recordIrpc(draft(),book({...row(),allocations:[{paymentId:'p',amount:'1'}]})).result.rows[0];assert.equal(r.status,'indeterminate');});
|
||||
test('same remittance can be split but never consumed twice, including other opportunities',()=>{const p={id:'p',kind:'remittance',reference:'SYNTHETIC-SHARED',date:'2026-10-20',amount:'10000',currency:'MZN'},a={...row('a'),settlement:'complete',allocations:[{paymentId:'p',amount:'5000'}]},b={...row('b'),settlement:'complete',allocations:[{paymentId:'p',amount:'5000'}]};let bk={version:1,rows:[a,b],payments:[p]};assert.ok(recordIrpc(draft(),bk).result.rows.every(r=>r.status==='calculated'));bk.rows[1].allocations[0].amount='6000';assert.ok(recordIrpc(draft(),bk).result.rows.every(r=>r.status==='indeterminate'));const d=draft();d.modelo_a_snapshot=saveIrpcBook(d,{version:1,rows:[a],payments:[p]});d.context_opportunity='another';assert.equal(recordIrpc(d,{version:1,rows:[{...b,allocations:[{paymentId:'p',amount:'6000'}]}],payments:[p]}).result.rows[0].status,'indeterminate');});
|
||||
test('duplicate document, duplicate proof ID or duplicate reference do not double totals',()=>{const r=row(),d=draft();assert.ok(recordIrpc(d,{version:1,rows:[r,{...r,id:'x'}],payments:[]}).result.rows.every(r=>r.status==='indeterminate'));const p={id:'p',kind:'remittance',reference:'SYNTHETIC-SAME',date:'2026-10-20',amount:'1',currency:'MZN'};assert.throws(()=>saveIrpcBook(d,{...book(),payments:[p,{...p,id:'p2'}]}),/já registado/);});
|
||||
test('reuse is idempotent, inherits supplier/document/amount, preserves deliberate edits and detects source delta',()=>{const d=draft(),ab=acquisitionBook(d),a={...newAcquisition('L5-supplier','2026-10','MZN'),supplier:'SYNTHETIC saved supplier',documentRef:'SYNTHETIC invoice',documentLine:'1',net:'100000',description:'SaaS'};d.modelo_a_snapshot=saveAcquisitionBook(d,{...ab,rows:[...ab.rows,a]});let b=reuseIrpcSources(d,irpcBook(d));assert.equal(reuseIrpcSources(d,b).rows.length,b.rows.length);let r=b.rows.find(r=>r.source?.id==='acquisition:L5-supplier');assert.equal(r.counterparty,a.supplier);assert.equal(r.documentRef,'SYNTHETIC invoice / linha 1');r.base='90000';assert.equal(reuseIrpcSources(d,b).rows.find(v=>v.id===r.id).base,'90000');b={version:1,rows:[{...row(),id:r.id,source:r.source}],payments:[]};const current=acquisitionBook(d);d.modelo_a_snapshot=saveAcquisitionBook(d,{...current,rows:current.rows.map(v=>v.id===a.id?{...v,net:'120000'}:v)});assert.match(recordIrpc(d,b).result.rows[0].reason,/origem alterado/);});
|
||||
test('save/recover preserves all preexisting maps, history, context separation and frozen report',()=>{const d=draft(),prior=JSON.parse(d.modelo_a_snapshot),b=recordIrpc(d,book());d.modelo_a_snapshot=saveIrpcBook(d,b);const now=JSON.parse(d.modelo_a_snapshot);for(const[k,v]of Object.entries(prior))assert.deepEqual(now[k],v);assert.deepEqual(irpcBook(d).result,b.result);assert.equal(irpcCurrent(d,b),true);b.rows[0].base='200000';assert.equal(irpcCurrent(d,b),false);assert.match(irpcReportRows({...d,modelo_a_snapshot:saveIrpcBook(d,b)})[0][2],/10000.00/);assert.equal(recordIrpc(d,b).history.length,1);d.context_opportunity='other';assert.equal(irpcBook(d).rows.length,0);});
|
||||
test('malformed store remains recoverable as explicit error, never overwritten blank',()=>{assert.throws(()=>irpcBook({modelo_a_snapshot:'{"version":1,"books":{},"irpcWithholdings":[]}'}));const d=draft(),b=book();b.rows[0].events=[{kind:'madeup',date:'2026-10-01',reference:'x'}];assert.throws(()=>saveIrpcBook(d,b));});
|
||||
test('export escapes private inputs, retains period/nature/gaps and excludes submission claims',()=>{const d=draft(),r={...row(),counterparty:'<script>alert(1)</script>'};d.modelo_a_snapshot=saveIrpcBook(d,recordIrpc(d,book(r)));const html=irpcPreparationHtml(d);assert.ok(!html.includes('<script>'));assert.match(html,/<script>/);assert.match(html,/sem submissão/);assert.match(html,/CER/);assert.match(html,/2026-10/);assert.match(html,/definitive/);});
|
||||
if(process.env.IRPC_FIXTURE_OUTPUT){const d=draft(),b=recordIrpc(d,{version:1,rows:[{...row(),settlement:'complete',allocations:[{paymentId:'p1',amount:'6000'}]},{...row('commission'),base:'20000',kind:'emoney_commission',direction:'received',settlement:'complete',allocations:[{paymentId:'p2',amount:'1500'}]}],payments:[{id:'p1',kind:'remittance',reference:'SYNTHETIC-AT1',date:'2026-10-20',amount:'6000',currency:'MZN'},{id:'p2',kind:'withholding_proof',reference:'SYNTHETIC-PROOF1',date:'2026-10-20',amount:'1500',currency:'MZN'}]});d.modelo_a_snapshot=saveIrpcBook(d,b);fs.writeFileSync(process.env.IRPC_FIXTURE_OUTPUT,JSON.stringify(d,null,2)+'\n');}
|
||||
|
||||
test("allocation changes in another opportunity mark previous result stale only for shared payments",()=>{const d=draft(),p={id:'p',kind:'remittance',reference:'SYNTHETIC-SHARED',date:'2026-10-20',amount:'10000',currency:'MZN'},r={...row(),settlement:'complete',allocations:[{paymentId:'p',amount:'6000'}]},b=recordIrpc(d,{...book(r),payments:[p]});d.modelo_a_snapshot=saveIrpcBook(d,b);assert.equal(irpcCurrent(d,b),true);const ctx=d.context_opportunity;d.context_opportunity='other';d.modelo_a_snapshot=saveIrpcBook(d,book(row('unrelated')));d.context_opportunity=ctx;assert.equal(irpcCurrent(d,b),true);d.context_opportunity='other';d.modelo_a_snapshot=saveIrpcBook(d,{...book({...row('shared'),settlement:'complete',allocations:[{paymentId:'p',amount:'1000'}]}),payments:[p]});d.context_opportunity=ctx;assert.equal(irpcCurrent(d,b),false);assert.match(irpcReportRows(d)[0][3],/Histórico/);});
|
||||
Reference in New Issue
Block a user