Prepare documented interest calculations and separate MRFTA working cases

This commit is contained in:
2026-10-25 20:52:21 +02:00
parent 5fa64bc0f4
commit acf8ec3cb5
14 changed files with 1217 additions and 10 deletions
@@ -392,3 +392,25 @@ O portal mantém a entrega instalada `ce746d88`, com `2b33a1c3` preservada;recib
**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.
## 25/10/2026 — entrega5fa64bc0 instalada;seguimento restrito a juros/CER em QA
Instalada pelo Git a revisão exacta `5fa64bc0`,com `ce746d88` preservada como release anterior. Backup fresco2026-10-25T18:28:38Z;174tabelas e369ficheiros/configuração;restauro isolado sem rede/55441 passou. Quatro ficheiros de execução,29protegidos e30originais conferidos. Schema,permissões e configuração iguais;zero migrações/importações. As172tabelas de negócio mantêm checksums;apenas cache operacional e sessões anónimas da instalação/verificação foram reconciliadas. Revisão/manifesto públicos,8marcadores compilados e simuladorHTTP200 passaram. Provas aceites reutilizadas,sem nova ronda manual;CI habitual do instalador passou.
Cobertura instalada:preparação por período,registo de juros documentados e relatório/exportação MRFTA. Registar não significa calcular automaticamente;CER/rubricas desconhecidos continuam identificados sem preenchimento presumido. Não houve submissão ou aceitação AT. Seguimento isolado apenas de juros automáticos eCER/rubricas;L5continua parcial,L3/ISPC/câmbio preservados. Recibo:`irpc-mrfta-delivery-20261025.json`.
## 25/10/2026 — seguimento QA restrito: juros automáticos e casos CER/rubricas
Portal instalado em `5fa64bc0`,com `ce746d88` preservada;recibo `irpc-mrfta-delivery-20261025.json`. Novo ramo `codex/irpc-interest-cer-qa-20261025`,sem instalação. Não foram reabertos outros saldos L5.
**Juros:** nova aritmética racional diária por parâmetros documentados do caso:taxa anual final,fonte/fundamento fiscal,vigência na data de liquidação,base anual,arredondamento do total e ocorrência. Nenhum valor vem pré-definido. Intervalos começam no dia posterior ao prazo documentado;entregas reduzem a base após o seu dia e o pagamento integral pára encargos. Contagem por ano/base365/366 e arredondamento só conforme convenção preenchida com referência. Definitiva e por conta retida cobertas neste recorte;por conta não retida e mora mantêm regra própria. Sem perfil completo,juros indeterminados e principal preservado. A taxa10%,base365 e convenções dos testes são fictícias,sem activação de parâmetro fiscal2026. O registo de liquidação documentada da entrega instalada mantém significado e histórico.
**CER/rubricas:** separação estável por período,rubrica,taxa e CER,com documentos e referências. Fichas de trabalhoHTML independentes conservam as pendências globais;não são declarações autónomas,anexos oficiais nem uma taxa média. Teste integrado distingue retenções20%/10% e CERfictícios999/998;juros por perfil permanecem demonstráveis,mas o total do formulário continua indeterminado pela transposição de rubricas distintas. Ausência de código ou referência continua identificada,sem preenchimento presumido. Nenhum catálogo CER oficial foi acrescentado.
**Compatibilidade/provas:** campos novos adicionais conservam os modos conhecidos pelo leitor5fa64bc0. Prova só de leitura/gravação exacta do novo payload nesse leitor:retenções actuais e nova preparação histórica,sem executar novamente cálculos antigos. Preservados31ficheiros de execução e30originais por hash. TypeScript,buildQA e13testes novos passaram;API1teste/19verificações com rollback em55441. UI:parâmetro fictício explícito,sem taxa→indeterminado,gravação/recuperação,relatório e download de casos20%/999 e10%/998. Relatório do dossier a partir da recuperação API. Provas aceites L3/ISPC/câmbio e5fa64bc0 reutilizadas;sem nova ronda manual das suites antigas.
**Saldo,apenas os mesmos dois pontos:** (1)corresponder fonte/parâmetro fiscal vigente2026 e convenções aplicáveis,além dos ramos mora/por conta não retida fora desta aritmética;(2)corresponder catálogo CER/rubricas ainda pendentes e procedimento oficial para taxas/códigos distintos. O inventário oficial AT189documentos foi consultado por metadados,não relido integralmente;nenhuma taxa/código foi inferido de rascunho,número de campo,agregado orçamental,MIMO ouPrime. L5 continua parcial. Recibo `irpc-interest-cer-qa-20261025.json`;provas em`outputs/irpc-interest-cer/`.
Reprodução do delta em QA: `php tools/irpc-interest-cer-qa.php routes`;`node --test tests/frontend/irpc-interest-cer.test.mjs`;`php tools/irpc-interest-cer-qa.php test`. Dependências existentes,PHP8.5/Node24;alvo QA guardado antes dos fornecedores/testes.
@@ -0,0 +1,391 @@
{
"status": "QA_two_point_delta_demonstrated_not_installed",
"installed_baseline": "5fa64bc0f4b067e81d8728b344eb8697415ebedf",
"branch": "codex/irpc-interest-cer-qa-20261025",
"scope_only": [
"juros automáticos por parâmetros documentados",
"separação CER/rubrica/taxa por casos de trabalho"
],
"implemented": {
"juros": "Aritmética racional diária com taxa anual final/base anual/arredondamento/fontes/versão do caso;principal após entregas parciais,paragem no pagamento integral,contagem por anos quando documentada,versão própria apenas da nova aritmética. Definitiva e por conta efectivamente retida neste recorte.",
"CER_rubricas": "Chaves estáveis por período/rubrica/taxa/CER;origens,documentos e referências conservados;fichaHTML por caso,mantendo pendências do período e sem declarar anexo/declaração autónoma.",
"compatibility": "Campos adicionais;modo legado preservado. Leitor5fa64bc0 conserva o payload exacto e o cálculo fiscal actual;novo resultado de juros é histórico nessa versão,sem recálculo."
},
"not_claimed": [
"Taxa fiscal2026 global cadastrada/activada ou substituição automática daMAIBOR",
"Convenção fiscal de base anual/arredondamento inferida sem fonte do caso",
"Juros de mora ou regra completa de imposto por conta não retido",
"CatálogoCER integral e novas correspondências oficiais digitais/especiais",
"Autorização para fundir taxas/CER ou criar declarações/anexos oficiais",
"Instalação deste novo incremento ou conclusão deL5"
],
"remaining_only_two_points": {
"juros": "Ligar parâmetro/fonte fiscal aplicáveis2026 e as convenções documentadas do caso;regra de mora e retenção por conta não efectuada continuam separadas. Sem perfil completo,a aritmética fica indeterminada;não reutilizar taxa de exemplo.",
"CER_rubricas": "Obter/corresponder os códigos/rubricas ainda pendentes e a transposição oficial de múltiplas taxas/CER;as fichas separam trabalho,mas não resolvem o catálogo ou o procedimento oficial."
},
"verification": {
"new_frontend": "13 tests passed;no old functional suites manually repeated",
"types": "tsc --noEmit exit0",
"build": "complete QA build exit0",
"private_api": "1test/19assertions;DatabaseTransactions rollback;loopback55441/simulator_fiscal_delta_20261021_test",
"dossier": "generated from actual API recovery;parameters/day segments/report preserved",
"UI": {
"documentedParameterCalculation": true,
"fictionalRateExplicit": true,
"missingRateIndeterminate": true,
"principalPreserved": true,
"localSaveRecovery": true,
"caseDownload": true,
"notOfficialDeclaration": true,
"differentRatesCodesSeparated": true,
"periodTotalIndeterminate": true,
"privateApi": "IrpcInterestCerDraftTest,separate",
"errors": []
},
"installed_reader_new_payload": {
"readerRevision": "5fa64bc0",
"readNewPayload": true,
"taxResultCurrent": true,
"newPreparationHistorical": true,
"roundTripExact": true,
"oldCalculationsReexecuted": false
},
"accepted_proofs": "Existing5fa64bc0,L3,ISPC,FX proofs reused;onlynewdelta andnew-payload-reader compatibility checked"
},
"examples": "Annual10%,365days,rounding conventions andCER999/998 are explicitly fictional QA parameters/codes,not official2026 fiscal data. Principal7300,paid3650,interest15 on5+5days,total3665 in onecase;20%/10%withholding and999/998 separate cases keepperiodtotalindeterminate.",
"runtime_delta": [
{
"path": "resources/js/lib/irpc-interest.ts",
"sha256_LF": "f8e6390b3b2c58c296f7e3fd14576e06ae248bc0cabce1a666b7094611f2a7dd"
},
{
"path": "resources/js/lib/irpc-mrfta-cases.ts",
"sha256_LF": "65133d0005051e0e5c9fbc8346478cca5198717e283e153094132a16d384a620"
},
{
"path": "resources/js/lib/irpc-mrfta.ts",
"sha256_LF": "c3132e6c1cdecff6a2d6d369ef5d8118aa6d58b59bca51516a1a3e0cb42f058e"
},
{
"path": "resources/js/components/simulation-irpc-mrfta.tsx",
"sha256_LF": "27a93e14eecfb002fe2f264e1854ec5bf852997535d61db1a8d3373a8e8470eb"
}
],
"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": "4523365e2d4a2fcd014e13393deb33a5ec4b9396ff580d8a237aa8c4f02265e4",
"sha256_LF": "4523365e2d4a2fcd014e13393deb33a5ec4b9396ff580d8a237aa8c4f02265e4"
},
{
"path": "resources/js/lib/irpc-withholdings.ts",
"sha256_LF": "8e5f3c035ce5e4d6c3fc82f933439ab580fa5519492f9c4d32cfb71b77b54803"
},
{
"path": "resources/js/components/simulation-irpc-withholdings.tsx",
"sha256_LF": "751af4fab346659bc5ccb08b8a2169769af7521ea0035050af32b41ea7f5aa76"
}
],
"originals_unchanged": [
{
"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/BM-Mercado-Monetario-MAIBOR-20261025.html",
"sha256": "00e8a7e0d3a1db4ca3beb56d5361b2084a0f1684f3d40fc1c203702a2190e063"
},
{
"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"
}
],
"source_review": {
"reuse": "AcceptedDecreto9/2008,25/35 andMRFTA/BM original sources. No new rate/CER activated from research.",
"AT_inventory": {
"url": "https://www.at.gov.mz/api/documents?root=LEGISLACAO",
"path": "outputs/irpc-interest-cer/at-legislation-inventory.json",
"sha256": "d74e764224d97fdea2e6e6e3496614c9b0f94941e8f8757eed8e7847bda9508c",
"documents": 189,
"read_scope": "Metadata/title inventory;not a fresh integral reading of189PDFs. No identified title supplies the required successor fiscal parameter or MRFTA CER crosswalk."
},
"CER_search": "Decreto68/2014 and subsequent disaggregation references identified;no applicable detailed CER crosswalk recovered for automatic mapping. Unnumbered drafts,field numbers and budget aggregates not used as revenue codes.",
"rate_search": "No applicable2026 fiscal replacement parameter obtained in this scoped check;current banking reference rates were not substituted."
},
"implementation_choices": [
"Complete parameter references are case data,not a new organizational approver. No default tax rate/day basis/rounding or source publication activates a calculation.",
"Working-case export retains period gaps;it does not decide official declaration splitting."
],
"active_changes": false,
"schema_backend_api_config_permissions_dependencies_delta": false,
"L5_integrally_completed": false,
"proofs": {
"outputs/irpc-interest-cer/api.txt": "e647e9d8daf16cfa4359e35edd373cfdbc7e35ef684b5fb8cdb1107eeb158ee7",
"outputs/irpc-interest-cer/at-legislation-inventory.json": "d74e764224d97fdea2e6e6e3496614c9b0f94941e8f8757eed8e7847bda9508c",
"outputs/irpc-interest-cer/build.txt": "3841d26e7f52f6468a337f1fd786dc7c04be122bc639bf3755842c39e1a837ad",
"outputs/irpc-interest-cer/calculated.json": "ff2d649ba269e7618a9cb9208f5e47baff5bbbc45c65176ba163cea5e69ad390",
"outputs/irpc-interest-cer/case-10-998.html": "bcfe6a84e07d3b411e28e231c797daf7c013041856dbb0aa05ba605309e8370a",
"outputs/irpc-interest-cer/case.html": "24ee8874acb770769bbe4e807af05e9d42646257142717c55c621d34259a6dc8",
"outputs/irpc-interest-cer/dossier.html": "5f69bd87ff0bcaa52c20eaa1c9feafeb6ae263d667aa4c53df15bd2abce8752d",
"outputs/irpc-interest-cer/dossier.json": "61c194509f6c2feb432819ff6f5d72647acf3ec2ff068bb299fe4c6be6a294da",
"outputs/irpc-interest-cer/dossier.txt": "a0473de355821758c4d320eea8e7af76a0d196a9cee652c71af723d6f69832f0",
"outputs/irpc-interest-cer/frontend.txt": "686362609c03b88d3f0d90951be11d0a3c65be8d79a3c50aa24c0249ab545da2",
"outputs/irpc-interest-cer/index.html": "6bb76e37ac20a73cb9ffcda14f1ac9db701aa01bc2ef2f62fc5adaaffb640ca8",
"outputs/irpc-interest-cer/installed-reader-compatibility.json": "dcac8cb281bac3d3ccace6c73087632c53dec1c27c5e4967b631a822dcb7da14",
"outputs/irpc-interest-cer/mixed.json": "82fec05e50261f4eb13160de93c78d76a6a58c79644ed2ca97fc27ae3ce41c3e",
"outputs/irpc-interest-cer/preparation.html": "8f210407ab61ed7a1f0bd3b8bce7c247bcecfc4ce2fe932924957fd3af1ff990",
"outputs/irpc-interest-cer/recovered.json": "ac9d7c1338c99190616d62f0ad5c7d11403af65d73ff6f7726a871c1693596e1",
"outputs/irpc-interest-cer/types.txt": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"outputs/irpc-interest-cer/ui-proof.json": "366a3aa989f40ea195b4d334bdc666d255ed88125eca9533a669eb0721e0267d",
"outputs/irpc-interest-cer/ui.png": "cac3bab6a576d5394b97d349903ead7ad12f3270a9c8a9f16ec2880822420d41",
"outputs/irpc-interest-cer/ui.txt": "98b34de11dd5b993510b997413d537320f291c447c6a1a9244817df99cc7cc72"
}
}
@@ -0,0 +1,535 @@
{
"status": "installed_scoped_MRFTA_documented_interest_delta_verified",
"candidate_installed": "5fa64bc0f4b067e81d8728b344eb8697415ebedf",
"previous_installed": "ce746d889d5711f23f4e0ac8d9802997b5e106f9",
"previous_release_preserved": "/var/www/investmoz-homologacao/releases/ce746d889d5711f23f4e0ac8d9802997b5e106f9-5fdb1b51",
"authorization": "Tempo:concluir entrega5fa64bc0 peloGit,backup/restauro fresco e delta;preservarce746d88,L3,ISPC,câmbio;seguir em QA apenas juros automáticos eCER/rubricas;L5parcial.",
"fresh_backup": {
"stage": "/var/backups/investmoz-irpc-mrfta-delivery-20261025/backup",
"created_at": "2026-10-25T18:28:38Z",
"target": {
"database": "teste_investidor",
"port": 5432
},
"tables": 174,
"files_verified": 369,
"dump_sha256": "4721dc593969b542c9cdbd3633e9a71bbd21b2e7a6ecfa2d4c180147a322d841",
"files_sha256": "51ba4c5d214b25bae9123489f9a432289fb41ef118142900e4048b22d5cec9d9",
"restore_verified": false
},
"isolated_restore": {
"restore_verified": true,
"tables_verified": 174,
"container_network": "none",
"database": "irpc_l5_restore_20261025",
"port": 55441,
"source_dump_sha256": "4721dc593969b542c9cdbd3633e9a71bbd21b2e7a6ecfa2d4c180147a322d841",
"active_database_writes": 0
},
"preflight": {
"free_bytes": 5446705152,
"existing_pipeline_minimum_bytes": 5368709120,
"space_guard_satisfied": true,
"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
},
"current_link": "/var/www/investmoz-homologacao/releases/ce746d889d5711f23f4e0ac8d9802997b5e106f9-5fdb1b51",
"release_revision": {
"commit": "ce746d889d5711f23f4e0ac8d9802997b5e106f9",
"branch": "main",
"built_at": "2026-10-25T18:06:03.871601+00:00",
"manifest_sha256": "feb2dce3fc8845c6307c982323b3e3523b333a018189c47488f9fcba25490555"
},
"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/ce746d889d5711f23f4e0ac8d9802997b5e106f9-5fdb1b51",
"installed": {
"status": "installed",
"commit": "5fa64bc0f4b067e81d8728b344eb8697415ebedf",
"release": "/var/www/investmoz-homologacao/releases/5fa64bc0f4b067e81d8728b344eb8697415ebedf-b08a592f",
"previous_release": "/var/www/investmoz-homologacao/releases/ce746d889d5711f23f4e0ac8d9802997b5e106f9-5fdb1b51",
"database_migrations": 0,
"content_imports": 0
},
"runtime_files_verified": 4,
"preserved_L3_ISPC_FX_runtime_files_verified": 29,
"documentary_files_verified": 30,
"persistent_files_and_env_verified": 369,
"tables_compared": 174,
"changed_tables": {
"cache": {
"before": {
"rows": 106,
"checksum": "e0ffe70896abb7ef550be154a6c755c6"
},
"after": {
"rows": 106,
"checksum": "ac75911294e9f6e3aeae2665d0d212b2"
}
},
"sessions": {
"before": {
"rows": 18,
"checksum": "0b7305e61724007da03a5cfd786f14d0"
},
"after": {
"rows": 19,
"checksum": "069206643d7e4e1f788a65e9c9fd3cd3"
}
}
},
"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": "dbb99b7e17f5b4b9bb7a8bdf9350d9b2ae368aaa4bc44593a2522d87617d8461",
"kind": "updated",
"fields": [
"value",
"expiration"
],
"key_pattern": "investemoz-cache-{hash}",
"is_queue_restart_key": false,
"expiration_unchanged": false,
"throttle_timer_pair": true
},
{
"key_sha256": "c1c7bc86ef3255c103a0be6aeb651b824eb07259e993cc5daf3665c1a6afff2d",
"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": 18,
"after_rows": 21,
"changes": [
{
"key_sha256": "78207abc124e12f093844b879b74b5688a9d83d5816a3dde5ef08a77817039dc",
"kind": "added",
"fields": [
"id",
"ip_address",
"user_agent",
"payload",
"last_activity"
],
"anonymous_user": true,
"delivery_user_agent": true
},
{
"key_sha256": "163d86a1d136564be2cd032d5f0232442e89cebba612f73b789e0da0fa932290",
"kind": "added",
"fields": [
"id",
"ip_address",
"user_agent",
"payload",
"last_activity"
],
"anonymous_user": true,
"delivery_user_agent": true
},
{
"key_sha256": "03d1ab44d145b80341139dacf4a8a1f6d9d9b2689880049244ba20926cdab2dd",
"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": "5fa64bc0f4b067e81d8728b344eb8697415ebedf",
"branch": "main",
"built_at": "2026-10-25T18:33:01.349237+00:00",
"manifest_sha256": "d1fd6fad9c48cf8c1feedcefbedae5a9b5471f0710894422ff9ae1a1633368a4"
},
"manifest_hash_matches": true,
"compiled_delta_markers": {
"Preparar e guardar MRFTA": [
"assets/services-Cp_vtFz7.js"
],
"Transcrever liquidação documentada de juros compensatórios": [
"assets/services-Cp_vtFz7.js"
],
"Não são juros calculados pelo portal.": [
"assets/project-dossier-DldGGCX7.js"
],
"CER numérico e referência aplicável por identificar;sem código presumido.": [
"assets/project-dossier-DldGGCX7.js"
],
"Dispensa de retenção": [
"assets/services-Cp_vtFz7.js",
"assets/project-dossier-DldGGCX7.js"
],
"Calcular e guardar apuramento trimestral ISPC": [
"assets/services-Cp_vtFz7.js"
],
"Calcular e guardar restituição E": [
"assets/services-Cp_vtFz7.js"
],
"Reutilizar selecção cambial": [
"assets/services-Cp_vtFz7.js"
]
},
"assets": [
{
"file": "assets/services-Cp_vtFz7.js",
"status": 200,
"sha256": "a7868d6f94fa6ead018f44d0df3900e827bdf2e9ea8cd1ee8db6d55de0db5ca9"
},
{
"file": "assets/simulation-form-BqVD91Ek.js",
"status": 200,
"sha256": "85cbd533c5bc92af5a448db71da006d4346d2d0adb09dde825db0aa19ad5048c"
},
{
"file": "assets/project-dossier-DldGGCX7.js",
"status": 200,
"sha256": "48e31ad3e2de0d0182b5f1abdea98b9db1283a20667e63c7cd910327e923ae4c"
},
{
"file": "assets/project-dossier-document-BUtxzH85.js",
"status": 200,
"sha256": "d3b7d7907a2fdf6bce247ed43a115d9d3c104882331d41e8975a894c49ccfb23"
}
],
"simulator_status": 200,
"simulator_entry": "assets/app-CfQbzHq7.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-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"
}
],
"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"
},
{
"path": "resources/js/lib/project-dossier-report.ts",
"sha256": "4523365e2d4a2fcd014e13393deb33a5ec4b9396ff580d8a237aa8c4f02265e4"
}
],
"accepted_QA_proofs_reused": {
"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"
},
"installer_report": {
"status": "installed",
"commit": "5fa64bc0f4b067e81d8728b344eb8697415ebedf",
"release": "/var/www/investmoz-homologacao/releases/5fa64bc0f4b067e81d8728b344eb8697415ebedf-b08a592f",
"previous_release": "/var/www/investmoz-homologacao/releases/ce746d889d5711f23f4e0ac8d9802997b5e106f9-5fdb1b51",
"database_migrations": 0,
"content_imports": 0,
"requested_commits": [
"5fa64bc0f4b067e81d8728b344eb8697415ebedf"
],
"finished_at": "2026-10-25T18:33:04.381640+00:00"
},
"remaining_only_two_points": {
"juros": {
"entregue": "Transcrição de liquidação de juros compensatórios, com montante/data/período/documento; ou ausência de atraso demonstrada por entrega integral e prazo documentado. Nenhuma taxa aplicada pelo motor para calcular juros.",
"resta": [
"Corresponder fonte, parâmetro/taxa e versão temporal aplicáveis ao cálculo automático dos juros da retenção; a referência MAIBOR12meses+2p.p. e a descontinuidade documentada não autorizam escolher MIMO/Prime como substituto.",
"Delimitar, na mesma operação, início/fim da contagem, natureza por conta/definitiva, base em dívida após entregas parciais, regra diária e arredondamento; distinguir compensatórios e mora sem cumulação presumida.",
"Implementar apenas os casos com regra completa e provar o novo delta: atraso com entrega integral/parcial, mudança de parâmetro se aplicável e dados/taxa ausentes. Reutilizar transcrição, persistência e relatórios já demonstrados."
],
"condicao_de_fecho": "Cálculo reproduzível do caso suportado com fonte/versão, principal, taxa, dias e juros discriminados; sem parâmetro aplicável, juros continuam indeterminados e o principal mantém-se disponível.",
"nao_reabrir": [
"Registo de juros documentados",
"Conferência de entrega integral no prazo documentado",
"Gravação/recuperação/histórico/relatório já demonstrados"
]
},
"cer_rubricas": {
"entregue": "Rubricas correntes correspondidas pelo rendimento e natureza; CER transcrito com referência. Ausência de CER, rubrica desconhecida e mistura de taxas/CER ficam pendentes. CER999 é exclusivamente fictício de QA.",
"resta": [
"Corresponder códigos CER a rubrica, natureza, rendimento e período com o catálogo/modelo/procedimento oficial aplicável; os números dos campos da guia antiga não são códigos CER por inferência.",
"Resolver apenas as rubricas ainda pendentes: digital/moeda electrónica, categorias especiais e outros rendimentos sem correspondência actual, incluindo casos por conta que não cabem nas rubricas já mapeadas.",
"Corresponder o tratamento de taxas/CER diferentes na mesma rubrica: conservar parcelas e documentos; não fundir taxas, calcular taxa média ou criar declarações/anexos por presunção.",
"Acrescentar casos dirigidos para cada nova correspondência e para código ausente, período incompatível ou rubrica desconhecida, mantendo casos independentes e cálculos anteriores."
],
"condicao_de_fecho": "Cada novo caso liga rendimento/natureza/período ao CER e à rubrica com referência verificável e apresentação correcta; o catálogo parcial nunca implica cobertura integral.",
"nao_reabrir": [
"Preparação de um mês/ano",
"Bases/taxas/imposto/entregas provenientes do cálculo guardado",
"Separação de dispensas, retenções sofridas e outros períodos",
"Exportação HTML e dossier demonstrados"
]
}
},
"database_migrations": 0,
"content_imports": 0,
"L5_integrally_completed": false,
"scope": "Period preparation,documentedinterestrecord andMRFTA reports only;notautomaticinterestnorcompleteCERmapping. Nexttwo-pointincrement staysQA.",
"evidence": {
"backup.txt": "151cbb1c3c4954da4679a9a2e54ba636afbd2e5c4b228f68ad4c236ae30663eb",
"installed-protected.txt": "32cff2aff1e1d7148af22820a8136267364efe3b4ede9cac3f7790f8f29bca5a",
"installed-public.txt": "82a8ae2b9113b83212f2b030abc537e1975d86b5597ed9385c67438f7d636549",
"integration.txt": "130dc00f5fafda21e09bfbfd1f934d630c03732127a60f21df69d075176950ae",
"operational-delta.txt": "e9c29d6e9691fb44f30eccc3041dfa3455905e6d5eb01410b8b898fcdf1f2f25",
"preflight.txt": "c87ceda50c49b392d55fd624bef9cc1f37cec9c1b888e4fded8c64d4b76ab60a",
"restore.txt": "d103daf999311fc6e6fd0a20f0b07162200ce5a14f1ad558f00be2fcb30d5943",
"schema-permissions-after.txt": "76b5a4d8af11e4898a205062766cfb7ab6ec0783200e77e60a1927aa1ca18597",
"schema-permissions-before.txt": "0ab7c964792acdd8917bce256c472e55e034b103500a58b49f020b5084823ed7",
"status1.txt": "f14354ab7188e19bf1188ec9ad9ed7d3834fac3996412cbda1d7436a418804fd",
"status2.txt": "a6fc5285be3f927ee0b1d882ae25f246d2026f0fe3b77d176e402ace6cf39b02",
"status3.txt": "193ef46c35707af20292cca967902aa265f90bfc1336189ea6e61b6dbb6cc9c9"
}
}
@@ -165,3 +165,10 @@ Reutilizados os29 originais aceites,sem perdas por hash. MRFTA19/39AT,PDF1–2,s
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`.
O snapshot HTML é versionado com `-text -diff` no caminho exacto de `.gitattributes`,conservando bytes e espaços do original em vez de normalizar linhas. Hash conferido também contra o blob Git antes de preparar o bundle final.
## 25/10/2026 — dois pontos restantes: parâmetros e correspondências
Reutilizados30originais aceites,sem alteração por hash. O seguimento não carrega taxa fiscal2026 nem novo CER. A aritmética exige fonte/fundamento,janela temporal,base anual e arredondamento explícitos do caso;ensaios usam parâmetros fictícios. Registo de montante liquidado e cálculo por perfil são vias distintas.
Inventário oficial AT189documentos recuperado em `outputs/irpc-interest-cer/at-legislation-inventory.json`,SHA256`d74e764224d97fdea2e6e6e3496614c9b0f94941e8f8757eed8e7847bda9508c`;escopo desta leitura:metadados/títulos,não revisão integral de novos PDFs. URL:https://www.at.gov.mz/api/documents?root=LEGISLACAO . Referências aoDecreto68/2014/desagregaçãoCER identificadas em pesquisa;sem correspondência detalhada aplicável recuperada para activação. Rascunhos sem diploma/número e agregados de receitas não usados como CER MRFTA. Sem revalidação genérica da credibilidade das fontes aceites,sem publicação/importação automática. As lacunas concretas permanecem no recibo `irpc-interest-cer-qa-20261025.json`.
@@ -5909,3 +5909,25 @@ O portal mantém a entrega instalada `ce746d88`, com `2b33a1c3` preservada;recib
**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.
## 25/10/2026 — entrega5fa64bc0 instalada;seguimento restrito a juros/CER em QA
Instalada pelo Git a revisão exacta `5fa64bc0`,com `ce746d88` preservada como release anterior. Backup fresco2026-10-25T18:28:38Z;174tabelas e369ficheiros/configuração;restauro isolado sem rede/55441 passou. Quatro ficheiros de execução,29protegidos e30originais conferidos. Schema,permissões e configuração iguais;zero migrações/importações. As172tabelas de negócio mantêm checksums;apenas cache operacional e sessões anónimas da instalação/verificação foram reconciliadas. Revisão/manifesto públicos,8marcadores compilados e simuladorHTTP200 passaram. Provas aceites reutilizadas,sem nova ronda manual;CI habitual do instalador passou.
Cobertura instalada:preparação por período,registo de juros documentados e relatório/exportação MRFTA. Registar não significa calcular automaticamente;CER/rubricas desconhecidos continuam identificados sem preenchimento presumido. Não houve submissão ou aceitação AT. Seguimento isolado apenas de juros automáticos eCER/rubricas;L5continua parcial,L3/ISPC/câmbio preservados. Recibo:`irpc-mrfta-delivery-20261025.json`.
## 25/10/2026 — seguimento QA restrito: juros automáticos e casos CER/rubricas
Portal instalado em `5fa64bc0`,com `ce746d88` preservada;recibo `irpc-mrfta-delivery-20261025.json`. Novo ramo `codex/irpc-interest-cer-qa-20261025`,sem instalação. Não foram reabertos outros saldos L5.
**Juros:** nova aritmética racional diária por parâmetros documentados do caso:taxa anual final,fonte/fundamento fiscal,vigência na data de liquidação,base anual,arredondamento do total e ocorrência. Nenhum valor vem pré-definido. Intervalos começam no dia posterior ao prazo documentado;entregas reduzem a base após o seu dia e o pagamento integral pára encargos. Contagem por ano/base365/366 e arredondamento só conforme convenção preenchida com referência. Definitiva e por conta retida cobertas neste recorte;por conta não retida e mora mantêm regra própria. Sem perfil completo,juros indeterminados e principal preservado. A taxa10%,base365 e convenções dos testes são fictícias,sem activação de parâmetro fiscal2026. O registo de liquidação documentada da entrega instalada mantém significado e histórico.
**CER/rubricas:** separação estável por período,rubrica,taxa e CER,com documentos e referências. Fichas de trabalhoHTML independentes conservam as pendências globais;não são declarações autónomas,anexos oficiais nem uma taxa média. Teste integrado distingue retenções20%/10% e CERfictícios999/998;juros por perfil permanecem demonstráveis,mas o total do formulário continua indeterminado pela transposição de rubricas distintas. Ausência de código ou referência continua identificada,sem preenchimento presumido. Nenhum catálogo CER oficial foi acrescentado.
**Compatibilidade/provas:** campos novos adicionais conservam os modos conhecidos pelo leitor5fa64bc0. Prova só de leitura/gravação exacta do novo payload nesse leitor:retenções actuais e nova preparação histórica,sem executar novamente cálculos antigos. Preservados31ficheiros de execução e30originais por hash. TypeScript,buildQA e13testes novos passaram;API1teste/19verificações com rollback em55441. UI:parâmetro fictício explícito,sem taxa→indeterminado,gravação/recuperação,relatório e download de casos20%/999 e10%/998. Relatório do dossier a partir da recuperação API. Provas aceites L3/ISPC/câmbio e5fa64bc0 reutilizadas;sem nova ronda manual das suites antigas.
**Saldo,apenas os mesmos dois pontos:** (1)corresponder fonte/parâmetro fiscal vigente2026 e convenções aplicáveis,além dos ramos mora/por conta não retida fora desta aritmética;(2)corresponder catálogo CER/rubricas ainda pendentes e procedimento oficial para taxas/códigos distintos. O inventário oficial AT189documentos foi consultado por metadados,não relido integralmente;nenhuma taxa/código foi inferido de rascunho,número de campo,agregado orçamental,MIMO ouPrime. L5 continua parcial. Recibo `irpc-interest-cer-qa-20261025.json`;provas em`outputs/irpc-interest-cer/`.
Reprodução do delta em QA: `php tools/irpc-interest-cer-qa.php routes`;`node --test tests/frontend/irpc-interest-cer.test.mjs`;`php tools/irpc-interest-cer-qa.php test`. Dependências existentes,PHP8.5/Node24;alvo QA guardado antes dos fornecedores/testes.
File diff suppressed because one or more lines are too long
@@ -1,10 +1,13 @@
import {newIrpcInterestRule,type IrpcInterestRule} from '@/lib/irpc-interest';
import {mrftaCases,mrftaCaseHtml} from '@/lib/irpc-mrfta-cases';
import {useState} from 'react';
import {irpcCurrent,type IrpcBook} from '@/lib/irpc-withholdings';
import {newMrftaInput,recordMrfta,mrftaHtml,mrftaReportRows,type MrftaInput} from '@/lib/irpc-mrfta';
import {newMrftaInput,recordMrfta,mrftaHtml,mrftaReportRows,mrftaCurrent,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 interestMode=i.interest.mode||(i.interest.calculation==='automatic'?'automatic':'');
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>;
@@ -16,11 +19,13 @@ export function SimulationIrpcMrfta({draft,book,save}:{draft:Record<string,unkno
<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></>}
<label>Tratamento dos juros<select value={interestMode} onChange={e=>interest(e.target.value==='automatic'?{mode:'',calculation:'automatic'}:{mode:e.target.value as MrftaInput['interest']['mode'],calculation:undefined})}><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="automatic">Cálculo com taxa e regra documentadas — sem parâmetro pré-definido</option><option value="timely">Conferir entrega integral dentro do prazo documentado</option></select></label>
{interestMode&&<><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></>}
{['assessment','automatic'].includes(interestMode)&&<>{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></>}
{['timely','automatic'].includes(interestMode)&&<><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></>}
{interestMode==='automatic'&&<fieldset className="space-y-2 rounded border p-2"><legend>Parâmetros documentados do cálculo — sem taxa fiscal de2026 pré-definida</legend><p>Preencha apenas a regra aplicável ao caso. Sem parâmetro completo, os juros continuam indeterminados. A taxa bancária actual não é adoptada por substituição. Imposto por conta não retido e juros de mora mantêm regra própria.</p>{(['annualRate','rateSource','fiscalBasis','validFrom','validTo','dayBasisRef','roundingRef','occurrenceRef']as const).map(k=><label key={k} className="block">{({annualRate:'Taxa anual final (%) documentada, incluindo acréscimos aplicáveis',rateSource:'Documento e citação da taxa',fiscalBasis:'Fundamento da aplicação fiscal ao caso',validFrom:'Parâmetro aplicável desde',validTo:'Parâmetro aplicável até',dayBasisRef:'Documento da base anual e contagem diária',roundingRef:'Documento do arredondamento do total',occurrenceRef:'Documento da retenção e ocorrência'})[k]}<input type={k==='validFrom'||k==='validTo'?'date':'text'} value={i.interest.automatic?.[k]??''} onChange={e=>interest({automatic:{...(i.interest.automatic??newIrpcInterestRule()),[k]:e.target.value}})}/></label>)}{(['dayBasis','rounding','retention']as const).map(k=><label key={k} className="block">{({dayBasis:'Base anual conforme documento',rounding:'Arredondamento do total conforme documento',retention:'Retenção efectuada'})[k]}<select value={i.interest.automatic?.[k]??''} onChange={e=>interest({automatic:{...(i.interest.automatic??newIrpcInterestRule()),[k]:e.target.value} as IrpcInterestRule})}>{(k==='dayBasis'?[['','Por identificar'],['365','365 dias'],['366','366 dias'],['actual_year','Dias efectivos de cada ano']]:k==='rounding'?[['','Por identificar'],['total_cent_half_up','Total aos cêntimos — metade por excesso'],['total_cent_down','Total aos cêntimos — por defeito']]:[['','Por identificar'],['withheld','Sim, documentada'],['not_withheld','Não efectuada, documentado']]).map(([v,l])=><option key={v} value={v}>{l}</option>)}</select></label>)}</fieldset>}
<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>
{book.mrftaResult&&<fieldset className="space-y-2 rounded border p-2"><legend>Casos de trabalho separados por período, rubrica, taxa e CER</legend><p>Estas fichas conservam as pendências do período. Não são declarações autónomas, anexos oficiais ou uma autorização para fundir taxas.</p>{mrftaCases(book.mrftaResult.model).map(c=><div key={c.key}><span>{c.period}; {c.rubric}; {c.rate}%; CER:{c.cer||'por corresponder'}; documentos:{c.documents.join('; ')}.</span><button type="button" onClick={()=>{try{if(!irpcCurrent(draft,book)||!mrftaCurrent(book,true))throw Error('Reprepare as entradas actuais antes de exportar o caso.');const html=mrftaCaseHtml(book.mrftaResult!.model,c.key),u=URL.createObjectURL(new Blob([html],{type:'text/html;charset=utf-8'})),a=document.createElement('a');a.href=u;a.download='MRFTA-caso-'+c.key+'.html';a.click();URL.revokeObjectURL(u);setError('');}catch(e){setError(String(e));}}}>Exportar ficha de trabalho {c.rate}% / {c.cer||'CER pendente'}</button></div>)}</fieldset>}
{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>;
}
+69
View File
@@ -0,0 +1,69 @@
import type {IrpcBook} from './irpc-withholdings.ts';
import {addFractions,decimalFraction,fraction,subtractFractions,type FiscalFraction} from './fiscal-rational.ts';
export type IrpcInterestRule={
annualRate:string;rateSource:string;fiscalBasis:string;validFrom:string;validTo:string;
dayBasis:''|'365'|'366'|'actual_year';dayBasisRef:string;
rounding:''|'total_cent_half_up'|'total_cent_down';roundingRef:string;
retention:''|'withheld'|'not_withheld';occurrenceRef:string;
};
export const newIrpcInterestRule=():IrpcInterestRule=>({annualRate:'',rateSource:'',fiscalBasis:'',validFrom:'',validTo:'',dayBasis:'',dayBasisRef:'',rounding:'',roundingRef:'',retention:'',occurrenceRef:''});
export const irpcInterestVersion='IRPC-interest/1';
export type IrpcInterestSegment={from:string;to:string;days:number;denominator:number;principal:FiscalFraction;interest:FiscalFraction};
export type IrpcInterestCalculation={version:string;rows:Array<{id:string;document:string;segments:IrpcInterestSegment[];exact:FiscalFraction}>;pending:string[];exact:FiscalFraction|null;total:FiscalFraction|null;rule:IrpcInterestRule;dueDate:string;liquidationDate:string;source:string};
const validDate=(s:string)=>/^20\d{2}-\d{2}-\d{2}$/.test(s)&&Number.isFinite(Date.parse(s))&&new Date(s).toISOString().slice(0,10)===s;
const next=(s:string)=>new Date(Date.parse(s)+86400000).toISOString().slice(0,10);
const less=(a:FiscalFraction,b:FiscalFraction)=>BigInt(a.numerator)*BigInt(b.denominator)<BigInt(b.numerator)*BigInt(a.denominator);
const zero=()=>fraction(0n,1n);
export function validateIrpcInterestRule(r:IrpcInterestRule){
if(!r||Object.keys(newIrpcInterestRule()).some(k=>typeof r[k as keyof IrpcInterestRule]!=='string')||!['','365','366','actual_year'].includes(r.dayBasis)||!['','total_cent_half_up','total_cent_down'].includes(r.rounding)||!['','withheld','not_withheld'].includes(r.retention))throw Error('Parâmetros de juros não recuperáveis;originais conservados.');
}
// No fiscal rate, annual day basis or rounding convention is supplied by default.
// Complete references describe the parameters of this case, not a global 2026 rate.
export function calculateIrpcInterest(b:IrpcBook,current:boolean,period:string,dueDate:string,dueReference:string,liquidationDate:string,r:IrpcInterestRule):IrpcInterestCalculation{
validateIrpcInterestRule(r);
const pending:string[]=[],rows:IrpcInterestCalculation['rows']=[];
const result=(exact:FiscalFraction|null=null,total:FiscalFraction|null=null):IrpcInterestCalculation=>({version:irpcInterestVersion,rows,pending,exact,total,rule:structuredClone(r),dueDate,liquidationDate,source:'Decreto9/2008 art35(1/2):dia imediato ao prazo até pagamento/liquidação;25(1):parâmetro na data da liquidação. Taxa,base anual e arredondamento apenas conforme referências do caso. Sem parâmetro fiscal2026 pré-definido,sem MIMO/Prime presumidos. Mora e retenção por conta não efectuada conservam regra própria.'});
if(!current||!b.result)pending.push('Retenções sem cálculo actual.');
if(!/^2026-(0[1-9]|1[0-2])$/.test(period)||!validDate(dueDate)||!dueReference.trim()||!validDate(liquidationDate)||liquidationDate<=dueDate||dueDate<period+'-01')pending.push('Identifique período,prazo documentado e data de liquidação posterior ao prazo;sem prazo/calendário presumidos.');
if(!validDate(r.validFrom)||!validDate(r.validTo)||r.validFrom>r.validTo||liquidationDate<r.validFrom||liquidationDate>r.validTo)pending.push('Parâmetro fora da versão/período aplicável à data de liquidação.');
const labels:Record<string,string>={rateSource:'fonte da taxa anual final',fiscalBasis:'fundamento fiscal aplicável',dayBasisRef:'fonte da base anual',roundingRef:'fonte do arredondamento',occurrenceRef:'documento da retenção/ocorrência'};
for(const[k,label]of Object.entries(labels))if(!r[k as keyof IrpcInterestRule].trim())pending.push('Falta '+label+'.');
if(!r.dayBasis||!r.rounding||!r.retention)pending.push('Regra de base anual,arredondamento e situação da retenção por identificar.');
let rate:FiscalFraction;
try{rate=decimalFraction(r.annualRate);if(!less(zero(),rate))throw Error();}catch{pending.push('Taxa anual final aplicável por identificar;sem zero ou taxa bancária de substituição.');return result();}
if(pending.length)return result();
const selected=b.result!.rows.filter(v=>v.direction==='paid'&&v.period===period&&v.treatment!=='dispensed');
if(!selected.length){pending.push('Sem retenções a entregar seleccionadas para este período.');return result();}
for(const v of selected){
const input=b.result!.inputs.find(i=>i.id===v.id);
if(!input||v.status!=='calculated'||!v.tax||less(v.tax,zero())||v.settled===null||!['none','complete'].includes(input.settlement)||!input.settlementRef.trim()||!v.nature){pending.push(v.id+': retenção/entregas não reconciliadas.');continue;}
if(v.nature==='on_account'&&r.retention!=='withheld'){pending.push(input.documentRef+': imposto por conta não retido exige o termo da declaração anual e responsabilidade correspondentes;fora deste cálculo.');continue;}
let payments:Array<{date:string;amount:FiscalFraction}>=[];
try{
const seen=new Set<string>();
payments=input.allocations.map(a=>{const p=b.result!.payments.find(p=>p.id===a.paymentId);if(!p||seen.has(p.id)||p.kind!=='remittance'||p.currency!=='MZN'||!validDate(p.date)||p.date<v.eventDate||!p.reference.trim()||p.date>liquidationDate)throw Error();seen.add(p.id);const amount=decimalFraction(a.amount);if(!less(zero(),amount))throw Error();return{date:p.date,amount};});
const allocated=addFractions(payments.map(p=>p.amount));if(less(v.tax,allocated)||!isEqual(allocated,v.settled))throw Error();
}catch{pending.push(input.documentRef+': comprovativos incoerentes ou posteriores à data de liquidação;conserve e reconcilie.');continue;}
let principal=subtractFractions(v.tax,addFractions(payments.filter(p=>p.date<=dueDate).map(p=>p.amount))),cursor=next(dueDate);
const segments:IrpcInterestSegment[]=[];
const charge=(to:string)=>{
if(cursor>to||!less(zero(),principal))return;
while(cursor<=to){
const year=Number(cursor.slice(0,4)),yearEnd=year+'-12-31',end=r.dayBasis==='actual_year'&&yearEnd<to?yearEnd:to;
const denominator=r.dayBasis==='actual_year'?(year%4===0&&(year%100!==0||year%400===0)?366:365):Number(r.dayBasis);
const days=(Date.parse(end)-Date.parse(cursor))/86400000+1;
const interest=fraction(BigInt(principal.numerator)*BigInt(rate.numerator)*BigInt(days),BigInt(principal.denominator)*BigInt(rate.denominator)*100n*BigInt(denominator));
segments.push({from:cursor,to:end,days,denominator,principal,interest});cursor=next(end);
}
};
const grouped=new Map<string,FiscalFraction[]>();for(const p of payments.filter(p=>p.date>dueDate))grouped.set(p.date,[...(grouped.get(p.date)??[]),p.amount]);
for(const[day,parts]of [...grouped].sort(([a],[c])=>a.localeCompare(c))){charge(day);principal=subtractFractions(principal,addFractions(parts));cursor=next(day);}
charge(liquidationDate);rows.push({id:v.id,document:input.documentRef,segments,exact:addFractions(segments.map(s=>s.interest))});
}
if(pending.length)return result();
const exact=addFractions(rows.map(r=>r.exact)),scaled=BigInt(exact.numerator)*100n,d=BigInt(exact.denominator);
let cents=scaled/d;if(r.rounding==='total_cent_half_up'&&scaled%d*2n>=d)cents++;
return result(exact,fraction(cents,100n));
}
function isEqual(a:FiscalFraction,b:FiscalFraction){return BigInt(a.numerator)*BigInt(b.denominator)===BigInt(b.numerator)*BigInt(a.denominator);}
+18
View File
@@ -0,0 +1,18 @@
import type {MrftaModel} from './irpc-mrfta.ts';
import {closureFingerprint} from './iva-closure.ts';
import {fractionPresentation,type FiscalFraction} from './fiscal-rational.ts';
// Groups are working sheets, never a decision to split an official declaration.
export function mrftaCases(m:MrftaModel){return m.lines.map(line=>({
key:closureFingerprint([m.period,line.rubric,line.rate,line.cer]),period:m.period,
rubric:line.rubric,rate:line.rate,cer:line.cer,ids:[...line.ids],documents:[...line.documents],references:[...line.references],
base:structuredClone(line.base),tax:structuredClone(line.tax),paid:structuredClone(line.paid),
pending:!line.cer||!/^\d+$/.test(line.cer)||!line.ids.every(id=>m.input.codes.find(c=>c.rowId===id)?.reference.trim())?'CER ou referência por corresponder;sem preenchimento presumido.':'CER transcrito com referência do caso;sem correspondência automática a catálogo integral.',
officialSeparation:'A correspondência de linhas/anexos/declaração oficial permanece por resolver quando há taxas/CER distintos na mesma rubrica.',
}));}
export function mrftaCaseHtml(m:MrftaModel,key:string):string{
const c=mrftaCases(m).find(c=>c.key===key);if(!c)throw Error('Caso MRFTA não encontrado;reprepare as entradas actuais.');
const esc=(v:unknown)=>String(v).replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]!));
const money=(v:FiscalFraction|null)=>{if(!v)return'Indeterminado';const p=fractionPresentation(v);return(p.approximate?'≈':'')+p.decimal+' MZN';};
return `<!doctype html><html lang="pt"><meta charset="utf-8"><title>MRFTA — ficha de caso</title><body><h1>MRFTA — ficha de trabalho por caso</h1><p>Não é uma declaração autónoma,guia,anexo oficial ou submissão. Não funde taxas nem substitui o total do período.</p><p>Período:${esc(c.period)};rubrica:${esc(c.rubric)};taxa:${c.rate}%;CER:${esc(c.cer||'Por corresponder')}.</p><p>Base:${esc(money(c.base))};imposto:${esc(money(c.tax))};entregas:${esc(money(c.paid))}.</p><p>Documentos:${esc(c.documents.join(';'))};origens:${esc(c.ids.join(';'))}.</p><p>${esc(c.references.join(' | '))}</p><p>${esc(c.pending)}</p><p>${esc(c.officialSeparation)}</p><h2>Pendências do período — conservadas</h2><ul>${m.pending.map(p=>'<li>'+esc(p)+'</li>').join('')}</ul><small>${esc(m.source)}</small></body></html>`;
}
+12 -4
View File
@@ -1,3 +1,4 @@
import {irpcInterestVersion,calculateIrpcInterest,validateIrpcInterestRule,type IrpcInterestRule,type IrpcInterestCalculation} from './irpc-interest.ts';
import type {IrpcBook, IrpcResult, IrpcRow} from './irpc-withholdings.ts';
import {addFractions,decimalFraction,fraction,fractionPresentation,subtractFractions,type FiscalFraction} from './fiscal-rational.ts';
@@ -7,10 +8,10 @@ export type MrftaInput = {
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};
interest:{mode:''|'assessment'|'timely';calculation?:'automatic';automatic?:IrpcInterestRule;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 MrftaModel={automatic?:IrpcInterestCalculation;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:''}});
@@ -23,6 +24,8 @@ const labels:Record<string,string>={period:'mês/ano',declaration:'tipo de decla
export function validateMrfta(b:MrftaStore){
const i=b.mrfta;
if(i?.interest?.calculation!==undefined&&i.interest.calculation!=='automatic')throw Error('Método de cálculo de juros não recuperável.');
if(i?.interest?.automatic!==undefined)validateIrpcInterestRule(i.interest.automatic);
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.');
@@ -32,7 +35,7 @@ function rubric(r:IrpcRow,v:IrpcResult){
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]);}
function key(b:IrpcBook&MrftaStore,current:boolean){return JSON.stringify(['MRFTA-L5/1',b.result,b.mrfta,current,...(b.mrfta?.interest.mode===''&&b.mrfta?.interest.calculation==='automatic'?[irpcInterestVersion]:[])]);}
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[]=[];
@@ -69,10 +72,15 @@ export function prepareMrfta(b:IrpcBook&MrftaStore,current:boolean):MrftaModel{
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.';
}
let automatic:IrpcInterestCalculation|undefined;
if(i.interest.mode===''&&i.interest.calculation==='automatic'){
if(!i.interest.automatic||i.interest.period!==i.period||!i.interest.reference.trim())interestReason='Cálculo automático sem parâmetros/ocorrência e período documentados;juros continuam indeterminados.';
else{automatic=calculateIrpcInterest(b,current,i.period,i.interest.dueDate,i.interest.dueReference,i.interest.date,i.interest.automatic);interest=automatic.total;interestReason=automatic.pending.length?automatic.pending.join(' | '):'Juros calculados por parâmetros documentados deste caso;sem taxa fiscal2026 pré-definida. Taxa anual:'+automatic.rule.annualRate+'%;fonte:'+automatic.rule.rateSource+';fundamento:'+automatic.rule.fiscalBasis+';vigência:'+automatic.rule.validFrom+'–'+automatic.rule.validTo+';base anual:'+(automatic.rule.dayBasis==='actual_year'?'dias efectivos de cada ano':automatic.rule.dayBasis+' dias')+';fonte:'+automatic.rule.dayBasisRef+';arredondamento do total aos cêntimos:'+(automatic.rule.rounding==='total_cent_half_up'?'metade por excesso':'por defeito')+';fonte:'+automatic.rule.roundingRef+';ocorrência:'+automatic.rule.occurrenceRef+';'+automatic.rows.map(r=>r.document+':'+r.segments.map(s=>s.from+'–'+s.to+':'+s.days+'dias/'+s.denominator+';principal '+money(s.principal)+';juros '+money(s.interest)).join(',')).join(' | ');}
}
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};
return {...(automatic?{automatic}:{}),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}};
@@ -0,0 +1,45 @@
<?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 IrpcInterestCerDraftTest extends TestCase
{
use DatabaseTransactions;
public function test_documented_parameter_calculation_and_segmented_cases_recover_through_private_api(): 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-interest-cer/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);
$model=$store['irpcWithholdings'][$draft['context_opportunity']]['mrftaResult']['model'];
$this->assertSame('7300',$model['principal']['numerator']);
$this->assertSame('3650',$model['paid']['numerator']);
$this->assertSame('15',$model['interest']['numerator']);
$this->assertSame('3665',$model['total']['numerator']);
$this->assertSame('IRPC-interest/1',$model['automatic']['version']);
$this->assertSame('',$model['input']['interest']['mode']);
$this->assertSame('automatic',$model['input']['interest']['calculation']);
$this->assertSame('10',$model['automatic']['rule']['annualRate']);
$this->assertStringContainsString('fictício',$model['automatic']['rule']['rateSource']);
$segments=$model['automatic']['rows'][0]['segments'];
$this->assertCount(2,$segments);
$this->assertSame(5,$segments[0]['days']);
$this->assertSame('7300',$segments[0]['principal']['numerator']);
$this->assertSame('3650',$segments[1]['principal']['numerator']);
$this->assertSame('999',$model['lines'][0]['cer']);
file_put_contents(base_path('outputs/irpc-interest-cer/recovered.json'),json_encode($recovered,JSON_THROW_ON_ERROR|JSON_UNESCAPED_UNICODE));
}
}
File diff suppressed because one or more lines are too long
+63
View File
@@ -0,0 +1,63 @@
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,recordMrfta,mrftaCurrent,mrftaHtml,prepareMrfta} from '../../resources/js/lib/irpc-mrfta.ts';
import {newIrpcInterestRule,calculateIrpcInterest} from '../../resources/js/lib/irpc-interest.ts';
import {mrftaCases,mrftaCaseHtml} from '../../resources/js/lib/irpc-mrfta-cases.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 rule=()=>({...newIrpcInterestRule(),annualRate:'10',rateSource:'QA parâmetro fictício; não é taxa fiscal2026',fiscalBasis:'QA fundamento sintético para ensaio aritmético',validFrom:'2026-01-01',validTo:'2026-12-31',dayBasis:'365',dayBasisRef:'QA fonte sintética da base365',rounding:'total_cent_half_up',roundingRef:'QA fonte sintética do arredondamento do total',retention:'withheld',occurrenceRef:'QA retenção e atraso documentados'});
const row=(id='one')=>({...newIrpcRow(id),counterparty:'QA contraparte',documentRef:'QA-'+id,base:'36500',kind:'royalty',recipientIrpc:'yes',payerIrpc:'yes',residence:'nonresident',permanentEstablishment:'no',mozambique:'yes',qualificationRef:'QA incidência e residência',specialRelief:'none',eventsComplete:'yes',events:[{kind:'cost',date:'2026-10-02',reference:'QA custo'}],settlement:'none',settlementRef:'QA sem entregas',ordinary:{...emptyIrpcOrdinaryFacts(),nonDigital:'yes',reference:'QA royalties não digitais'}});
const book=(payments=[])=>{const r=row();if(payments.length){r.settlement='complete';r.allocations=payments.map(p=>({paymentId:p.id,amount:p.amount}));}return recordIrpc(draft(),{version:1,rows:[r],payments});};
const payment=(amount,date='2026-11-25',id='p')=>({id,kind:'remittance',reference:'QA entrega-'+id,date,amount,currency:'MZN'});
const calc=(b=book(),r=rule(),due='2026-11-20',end='2026-11-30')=>calculateIrpcInterest(b,true,'2026-10',due,'QA prazo documentado',end,r);
const input=()=>({...newMrftaInput(),period:'2026-10',declaration:'initial',timing:'late',nuit:'999999999',name:'QA empresa',taxArea:'QA área fiscal',address:'QA domicílio',completeness:'yes',completenessRef:'QA reconciliação do período',otherRubrics:'none',otherRubricsRef:'QA outras rubricas não devidas',codes:[{rowId:'one',cer:'999',reference:'QA CER fictício, não oficial'}],interest:{...newMrftaInput().interest,mode:'',calculation:'automatic',period:'2026-10',date:'2026-11-30',dueDate:'2026-11-20',dueReference:'QA prazo documentado',reference:'QA ocorrência',automatic:rule()}});
test('daily interest uses documented parameters and explains principal,days and exact arithmetic',()=>{
const v=calc();assert.deepEqual(v.pending,[]);assert.equal(v.total.numerator,'20');assert.equal(v.rows[0].segments[0].from,'2026-11-21');assert.equal(v.rows[0].segments[0].to,'2026-11-30');assert.equal(v.rows[0].segments[0].days,10);assert.equal(v.rows[0].segments[0].principal.numerator,'7300');
});
test('partial remittance reduces principal after its payment day without duplicate daily interest',()=>{
const v=calc(book([payment('3650')]));assert.equal(v.total.numerator,'15');assert.deepEqual(v.rows[0].segments.map(s=>[s.days,s.principal.numerator]),[[5,'7300'],[5,'3650']]);
const sameDay=calc(book([payment('1825','2026-11-25','p1'),payment('1825','2026-11-25','p2')]));assert.equal(sameDay.total.numerator,'15');assert.equal(sameDay.rows[0].segments.length,2);
});
test('full payment stops charges and pre-deadline payment reduces the opening debt',()=>{
assert.equal(calc(book([payment('7300')])).total.numerator,'10');assert.equal(calc(book([payment('3650','2026-11-18')])).total.numerator,'10');
});
test('missing rate,reference,day basis or applicable version never becomes a default tax rate',()=>{
for(const patch of [{annualRate:''},{annualRate:'0'},{annualRate:'-1'},{rateSource:''},{fiscalBasis:''},{dayBasis:''},{dayBasisRef:''},{rounding:''},{roundingRef:''},{validTo:'2017-12-31'},{occurrenceRef:''}]){const v=calc(book(),{...rule(),...patch});assert.equal(v.total,null);assert.ok(v.pending.length);}
assert.equal(calculateIrpcInterest(book(),false,'2026-10','2026-11-20','QA prazo','2026-11-30',rule()).total,null);
});
test('on-account not-withheld cases remain separate;definitive not-withheld uses its own supported branch',()=>{
const r=row();r.residence='resident';const b=recordIrpc(draft(),{version:1,rows:[r],payments:[]});assert.equal(calc(b,{...rule(),retention:'not_withheld'}).total,null);assert.match(calc(b,{...rule(),retention:'not_withheld'}).pending.join(' '),/declaração anual/);assert.equal(calc(b).total.numerator,'20');assert.equal(calc(book(),{...rule(),retention:'not_withheld'}).total.numerator,'20');
});
test('documented actual-year basis splits year boundary including leap year without floating money',()=>{
const r={...rule(),validTo:'2028-12-31',dayBasis:'actual_year'},v=calc(book(),r,'2027-12-30','2028-01-02');assert.deepEqual(v.rows[0].segments.map(s=>[s.days,s.denominator]),[[1,365],[2,366]]);assert.deepEqual(v.total,{numerator:'599',denominator:'100'});
});
test('rounding is explicit,on total only,and matches the documented convention',()=>{
assert.deepEqual(calc(book(),{...rule(),annualRate:'10.0025'}).total,{numerator:'2001',denominator:'100'});assert.equal(calc(book(),{...rule(),annualRate:'10.0025',rounding:'total_cent_down'}).total.numerator,'20');
});
test('unreconciled payments and payments after liquidation keep the affected calculation indeterminate',()=>{
assert.equal(calc(book([payment('100','2026-12-01')])).total,null);const b=book();b.result.rows[0].settled=null;assert.equal(calc(b).total,null);
});
test('distinct rates and CER produce stable working cases,not invented declarations or merged rates',()=>{
const b={...book(),mrfta:input()};const second={...b.result.rows[0],id:'two',rate:10};b.result.rows.push(second);b.result.inputs.push(row('two'));b.mrfta.codes.push({rowId:'two',cer:'998',reference:'QA second fictionalCER'});const m=prepareMrfta(b,true),cases=mrftaCases(m);assert.equal(cases.length,2);assert.notEqual(cases[0].key,cases[1].key);assert.deepEqual(cases.map(c=>[c.rate,c.cer]),[[20,'999'],[10,'998']]);assert.equal(m.total,null);const html=mrftaCaseHtml(m,cases[0].key);assert.match(html,/Não é uma declaração autónoma/);assert.ok(html.includes('Pendências do período'));assert.deepEqual(mrftaCases({...m,lines:[...m.lines].reverse()}).map(c=>c.key).sort(),cases.map(c=>c.key).sort());
});
test('missing CER references remain visible within each case sheet without presumed correspondence',()=>{
const b={...book(),mrfta:input()};b.mrfta.codes[0].reference='';const m=prepareMrfta(b,true);assert.match(mrftaCases(m)[0].pending,/referência por corresponder/);b.mrfta.codes=[];assert.match(mrftaCases(prepareMrfta(b,true))[0].pending,/sem preenchimento presumido/);
});
test('automatic result persists,recovers and reaches dossier with traceable parameters and history',()=>{
const d=draft(),b=recordMrfta({...book([payment('3650')]),mrfta:input()},true);d.modelo_a_snapshot=saveIrpcBook(d,b);const recovered=JSON.parse(JSON.stringify(d)),saved=irpcBook(recovered);assert.equal(irpcCurrent(recovered,saved),true);assert.equal(mrftaCurrent(saved,true),true);assert.equal(saved.mrftaResult.model.interest.numerator,'15');assert.equal(saved.mrftaResult.model.total.numerator,'3665');assert.equal(saved.mrftaResult.model.automatic.rows[0].segments.length,2);
const report=JSON.stringify(irpcReportRows(recovered));assert.match(report,/Taxa anual:10%/);assert.match(report,/QA parâmetro fictício/);assert.match(report,/5dias\/365/);assert.match(report,/metade por excesso/);assert.ok(!report.includes('total_cent_half_up'));assert.match(JSON.stringify(createProjectDossierReport(recovered,{selectedSections:new Set(['finance'])})),/3665/);assert.match(mrftaHtml(saved,true),/Juros calculados por parâmetros documentados/);
saved.mrfta.interest.automatic.annualRate='';assert.equal(mrftaCurrent(saved,true),false);const changed=recordMrfta(saved,true);assert.equal(changed.mrftaResult.model.interest,null);assert.deepEqual(changed.mrftaHistory.at(-1),b.mrftaResult);
fs.mkdirSync('outputs/irpc-interest-cer',{recursive:true});fs.writeFileSync('outputs/irpc-interest-cer/calculated.json',JSON.stringify(recovered));fs.writeFileSync('outputs/irpc-interest-cer/preparation.html',mrftaHtml(b,true));
});
test('new parameters reject malformed persisted values while the installed snapshot remains current',()=>{
const d=JSON.parse(fs.readFileSync(new URL('./fixtures/irpc-mrfta-preserved.json',import.meta.url),'utf8'));const b=irpcBook(d);assert.equal(irpcCurrent(d,b),true);assert.equal(mrftaCurrent(b,true),true);const invalid={...book(),mrfta:input()};invalid.mrfta.interest.automatic.annualRate=10;assert.throws(()=>saveIrpcBook(draft(),invalid));
});
test('two calculated incomes with different treaty/internal rates remain separate without an invented declaration total',()=>{
const d=draft(),first={...row(),settlement:'complete',allocations:[{paymentId:'p',amount:'3650'}]},second={...row('two'),specialRelief:'claimed',reliefPreparation:{kind:'treaty',rule:'ptMz12',instrument:'QA CDT PT-MZ e Protocolo',article:'12',country:'PT',residenceDocument:'QA certificado Portugal2026',validFrom:'2026-01-01',validTo:'2026-12-31',conditions:'QA royalties qualificadas semEP',evidence:'QA contrato e residência',facts:{treatyResident:'yes',beneficialOwner:'yes',payerResidentMz:'yes',armLength:'yes',royaltyDefinition:'yes'}}};
const b=recordMrfta({...recordIrpc(d,{version:1,rows:[first,second],payments:[payment('3650')]}),mrfta:{...input(),codes:[...input().codes,{rowId:'two',cer:'998',reference:'QA CER998 fictício, não oficial'}]}},true);const m=b.mrftaResult.model;
assert.deepEqual(b.result.rows.map(r=>r.rate),[20,10]);assert.equal(m.interest.numerator,'25');assert.equal(m.total,null);assert.deepEqual(mrftaCases(m).map(c=>[c.rate,c.cer]),[[20,'999'],[10,'998']]);assert.match(m.pending.join(' '),/taxas ou CER distintos/);
d.modelo_a_snapshot=saveIrpcBook(d,b);fs.writeFileSync('outputs/irpc-interest-cer/mixed.json',JSON.stringify(d));
});
+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-interest-cer-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-interest-cer']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/IrpcInterestCerDraftTest.php'];$argc=count($argv);$_SERVER['argv']=$argv;$_SERVER['argc']=$argc;require $root.'/vendor/phpunit/phpunit/phpunit';