Complete scoped IRPC taxable profit with explicit evidence states
tests / ci (push) Canceled after 0s
tests / ci (push) Canceled after 0s
This commit is contained in:
@@ -460,3 +460,18 @@ Exemplo sintético:resultado financeiro6480MZN,multa100 e juros compensatórios2
|
||||
Prova do delta:7testes novos;APIprivada1teste/19asserções comrollback naQA55441;routes/actions gerados pelo procedimentoWayfinder;TypeScript/buildQA passaram. UI real:cálculo,gravação/recuperação local,relatório,informação insuficiente e download;API demonstrada separadamente,dossier gerado da recuperaçãoAPI. LivroL6 separado mantém payloadL5 e restante histórico;alterar plano/entradas torna resultado histórico sem o apagar.32ficheiros anteriores protegidos e30originais sem alteração;integração mínima emsimulation-form/project-dossier/project-dossier-report. Provas aceites não repetidas. Recibo:`irpc-l6-adjustments-qa-20261025.json`;QA isolada,sem instalaçãoL6.
|
||||
|
||||
Fonte adicional necessária apenas paraL6:exemplar oficialAT intitulado19/2009 contém tambémLei20/2009;PDF4/p272(16),36(1)a–i mantidas ealíneasj–n aditadas,conferido visualmente. Lei4/2012PDF2/3 e19/2013PDF1/2 mantêm36(1)c. Não reabre as dez dúvidasL5,que ficam paraAT até informação nova. L5 eL6 não integralmente concluídos.
|
||||
|
||||
|
||||
## 25/10/2026 — L6: percurso delimitado até ao lucro tributável demonstrado em QA
|
||||
|
||||
Base instalada `7a11c4ef`; primeiro recorte QA `664c88c8` preservado. A continuação reutiliza o plano reconciliado, o exercício, a oportunidade e os ajustamentos existentes. Não obriga a reintroduzir os montantes financeiros. Âmbito: projecção de Janeiro a Dezembro de 2026, MZN, sujeito residente no regime geral, com referência expressa de que o cenário cobre o sujeito passivo/exercício completo. Uma oportunidade parcial não se transforma automaticamente no total da empresa.
|
||||
|
||||
Cada linha do resultado exige qualificação ordinária e suporte: proveitos do exercício (18/20), custos remanescentes indispensáveis e documentados (22), sem outras exclusões/limites declarados. Os acréscimos 36(1)c reutilizam documentos e parcelas já registados. Seis grupos conservam a situação das variações patrimoniais positivas/negativas fora do resultado, outras correcções de rendimentos/custos, diferenças de periodização e operações especiais. **Valor conhecido (incluindo zero), ausência declarada com suporte e informação em falta são estados distintos.** Ausência não pode apagar uma origem financeira positiva ou descartar um montante já preenchido. Um valor conhecido não zero sem tratamento implementado permanece visível e impede apenas o fecho dependente, sem ser aplicado por presunção.
|
||||
|
||||
No caso ordinário delimitado, resultado financeiro + acréscimos fundamentados = lucro tributável simulado quando a conferência anual está completa e os outros efeitos são expressamente ausentes ou de valor conhecido zero. Exemplo sintético: 6480 + multa100 + juros compensatórios120 = 6700 MZN. Os 120 são um custo/pressuposto qualificado já incluído na origem; não são juros automaticamente calculados ou fiscalmente confirmados por L5. Falta de prova da variação patrimonial negativa mantém o lucro indeterminado, conservando resultado6480 e acréscimos220. Variação conhecida500, depreciação positiva ou juros financeiros remanescentes mantêm correspondência específica pendente. Sem acréscimos, uma conferência completa pode conservar o resultado financeiro; resultados negativos não são convertidos em zero nem em crédito/perda reportável.
|
||||
|
||||
Prova do delta: 9 testes novos; API privada 1 teste/38 verificações, criação → alteração com If-Match → recuperação, com rollback na QA isolada55441. Wayfinder gerou routes/actions; TypeScript e buildQA passaram. UI real: cálculo, gravação/recuperação local, valor conhecido zero, ausência, informação em falta, contradição e exportação HTML. Relatórios/dossiers dos casos conhecido e incompleto gerados a partir da recuperação efectiva da API. Versão2 adicional mantém o snapshot/resultado1 de664c88c8 legível e actual sem o recalcular; mudanças nas entradas desactualizam apenas o novo resultado e conservam histórico. Preservados33ficheiros anteriores de execução e31originais por hash;integrações anteriores de formulário/dossier não alteradas neste delta. Provas aceites L3/ISPC/câmbio/L5 reutilizadas, sem nova ronda manual.
|
||||
|
||||
**Preparação conjunta para entrega pelo Git:** primeiro recorte664c88c8 e continuação até lucro tributável, sobre7a11c4ef, com pacote/manifest e delta conferido. Não instalado nem integrado em main por esta preparação. Backup/restauro fresco e conferência do destino fazem parte da instalação posterior; não reutilizar como fresco o backup da entregaL5. Recibo `irpc-l6-taxable-profit-qa-20261025.json`; provas `outputs/irpc-l6-profit/` na QA isolada.
|
||||
|
||||
Matéria colectável, perdas reportadas, deduções, colecta, créditos de retenção e M20/M22 continuam fora deste percurso; L6 não está integralmente concluído. L5 permanece operacional no âmbito demonstrado; J1–J5/C1–C5 ficam para a AT e só limitam os casos efectivamente dependentes. Nenhuma nova pesquisa de taxas/CER de L5 foi feita.
|
||||
|
||||
@@ -74,3 +74,12 @@ Novas execucoes identificam-se como `portal-arithmetic-v1`, para nao sugerir que
|
||||
|
||||
- Snapshot `business-plan-v17`: adds `openingPayablesInstallments`, an optional schedule with month and percent of the original opening payable balance. Declared percentages sum to at most 100%; cent rounding is capped at the unpaid balance. Scheduled settlements reduce cash and opening payables, not profit or period expenses. Any unpaid amount remains a closing payable; an absent schedule assumes no settlement and leaves the full opening amount outstanding. Snapshots v1-v16 remain readable with an empty schedule; explicit resave produces v17.
|
||||
- Execuções API concluídas só são aceites pelo consumidor se `result_snapshot` for um envelope `contract_version: 1` válido (incluindo consistência entre `result.value` e `indicative_total`) ou um snapshot legado legível com montante decimal. Estados concluídos sem snapshot, com snapshot malformado ou com versão v1 inválida são rejeitados na fronteira da API; estados não concluídos podem manter snapshot ausente/nulo. Snapshots históricos legíveis continuam compatíveis.
|
||||
|
||||
|
||||
## IRPC normal — conferência anual opcional, versão de cálculo 2
|
||||
|
||||
O envelope de livros mantém `version:1`; `irpcNormal[opportunity]` aceita `profit` opcional (`version:1`, `completenessReference`, `lines`, `topics`). As linhas mantêm o código/origem financeira, estado `known|absent|''` e referência; os tópicos conservam separadamente estado, montante original e referência. `known` com montante zero não se torna `absent`; `''` não se torna zero. Tópicos de valor conhecido não zero sem regra implementada não são aplicados. Uma ausência incompatível com a origem ou com montante preenchido mantém pendência.
|
||||
|
||||
Sem `profit`, o cálculo/leitor/histórico `IRPC-normal-adjustments/1` mantém significado e chave anterior. Com conferência adicional, `IRPC-normal-adjustments/2` acrescenta `model.profit` congelado e pode produzir `taxableProfit` racional com sinal. `taxableBase` e `tax` continuam nulos. O cálculo só fecha no recorte anual/residente/MZN documentado, sujeito-exercício completo, receitas/custos ordinários qualificados, acréscimos resolvidos e demais efeitos expressamente ausentes ou zero conhecidos. A origem continua `projected`; não é demonstração contabilística nem confirmação AT. Depreciação positiva e juros financeiros remanescentes exigem tratamento próprio.
|
||||
|
||||
O fingerprint inclui os novos factos apenas quando presentes; alterações do plano ou da conferência desactualizam o resultado sem eliminar histórico. Os demais livros permanecem intactos no snapshot. Gravação e recuperação usam a API privada existente com ETag/If-Match; sem novo endpoint, schema, permissões ou activação de parâmetros. Matéria colectável/colecta/créditos anuais e M20/M22 não são inferidos de um lucro conhecido.
|
||||
|
||||
@@ -0,0 +1,384 @@
|
||||
{
|
||||
"status": "scoped_financial_result_to_taxable_profit_demonstrated_QA_not_installed",
|
||||
"installed_baseline": "7a11c4ef8f06ef149eb0488be3cd0749dc609bd0",
|
||||
"previous_QA": "664c88c8f7381548a2f1d8ecacdf10715e8408f5",
|
||||
"branch": "codex/irpc-l6-taxable-profit-qa-20261025",
|
||||
"QA_workspace": "C:\\laragon\\www\\portal-investidor-irpc-l6-profit-qa-20261025",
|
||||
"scope": "Annual Jan-Dec2026,MZN,resident general regime,complete subject-year projection. Ordinary recognised revenues and indispensable documented ordinary costs,with no other exclusions/limits declared. Fines and compensatory interest36(1)c added back from existing entries. Positive depreciation and remaining financial interest require their own treatment and remain pending.",
|
||||
"implemented": [
|
||||
"Optional explicit annual review reuses existing plan and adjustment rows;no repeated financial amounts entered",
|
||||
"Each financial line classified with support and known amount;declared absence cannot override nonzero source",
|
||||
"Three persistent evidence states:known amount including zero,declared supported absence,missing information",
|
||||
"Six existing-effect topics:positive/negative non-accounting patrimonial variations,other income/expense corrections,timing,special operations;known nonzero amount preserved but blocks this limited path until implemented",
|
||||
"Taxable profit=financial profit before tax+supported addbacks only when subject-year complete and remaining effects expressly absent/known zero",
|
||||
"No-adjustment and negative-result cases supported without presumed zero or loss carryforward",
|
||||
"Version2 optional closure;legacyversion1 result remains current without recalculation,newinput changes invalidate only new result;history retained",
|
||||
"Private API create/update/recovery,UI,HTML and dossier from recovered payload include all evidence states"
|
||||
],
|
||||
"examples": {
|
||||
"known_case": "6480 financial profit +100 noncontractual fine +120 compensatory interest included in financial costs =6700 simulated taxable profit;ordinary remaining costs qualified with support;all other effects expressly absent/known zero",
|
||||
"known_zero": "Income corrections known0 stored separately from absent patrimonial variations",
|
||||
"missing_case": "Missing negative-patrimonial evidence -> taxable profit indeterminate;6480 financial profit and220 additions retained",
|
||||
"unsupported_case": "Known positive variation500 or positive depreciation or financial interest remaining -> amount visible,profit indeterminate",
|
||||
"no_adjustments": "Financial profit6600 with full review/no additions -> simulated profit6600",
|
||||
"negative_result": "Financial result-5280 with full review retained signed;no carryforward or tax credit inferred"
|
||||
},
|
||||
"not_claimed": [
|
||||
"Taxable base,tax collection,loss carryforward,annual withholding credits or M20/M22",
|
||||
"Full normalIRPC or L6 completion",
|
||||
"Actual accounting statement or tax authority confirmation;origin remains projected",
|
||||
"General treatment of nonzero patrimonial changes,other corrections,depreciation/financial-interest limits",
|
||||
"Any conclusion to L5 J1-J5/C1-C5"
|
||||
],
|
||||
"verification": {
|
||||
"frontend_new_tests": 9,
|
||||
"private_API": "1test/38assertions;create then If-Match update and recovery;DatabaseTransactions rollback;isolated127.0.0.1:55441/simulator_fiscal_delta_20261021_test",
|
||||
"typescript": "passed",
|
||||
"Wayfinder": "routes/actions generated using existing guarded procedure",
|
||||
"QA_build": "passed exit0",
|
||||
"UI": {
|
||||
"calculationToTaxableProfit": true,
|
||||
"localSaveRecovery": true,
|
||||
"knownZeroDistinct": true,
|
||||
"declaredAbsenceDistinct": true,
|
||||
"missingInformationIndeterminate": true,
|
||||
"absenceContradictionDetected": true,
|
||||
"independentAdditionsPreserved": true,
|
||||
"download": true,
|
||||
"privateApi": "IrpcNormalProfitDraftTest separate",
|
||||
"errors": []
|
||||
},
|
||||
"dossier": "both known and missing outputs generated from actual private API recovery",
|
||||
"accepted_proofs": "reused;no manual repeat of accepted functional suites;legacy snapshot only read and roundtripped",
|
||||
"legacy_fixture_sha256": "22968d447a30ad222f83bfb1c142ea1900fc2456945bfc6d517440829dec32cc",
|
||||
"preserved_runtime": 33,
|
||||
"preserved_originals_from_664c88c8": 31,
|
||||
"installed_public_revision": {
|
||||
"commit": "7a11c4ef8f06ef149eb0488be3cd0749dc609bd0",
|
||||
"branch": "main",
|
||||
"built_at": "2026-10-25T19:18:14.226356+00:00",
|
||||
"manifest_sha256": "d78df5e7caee48690641143c8e8633b850db421468c837ec37992ee9cfa21aaa"
|
||||
}
|
||||
},
|
||||
"runtime_delta_joint_from_7a11c4ef": [
|
||||
{
|
||||
"path": "resources/js/lib/irpc-normal.ts",
|
||||
"sha256_LF": "387292520216d3d37870e4080880e5ec026702977a2f4c556be6c82dd34a7fce"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-irpc-normal.tsx",
|
||||
"sha256_LF": "d15a42d6bc295a4794b8427c668780335305d6091363002b570109aa0ac1aef9"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-form.tsx",
|
||||
"sha256_LF": "85b024a7f45ceb52efd6ff1f21d0eab1fd8fcf1ab59e4b5302c08685235c472a"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/project-dossier.ts",
|
||||
"sha256_LF": "a0909bbfcdf7e8542b010faa104fcb918e86ae38ddf096082d4d5be98d31b2b9"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/project-dossier-report.ts",
|
||||
"sha256_LF": "5207a2ffeb208cd5ace6713038cee85208020ee618194dca7114e94fc3200b6c"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-normal-profit.ts",
|
||||
"sha256_LF": "35dc586d9ab6c77405f3a46ed2ce3d9db74c54f338314d5e3c4959f0b6a55860"
|
||||
}
|
||||
],
|
||||
"protected_runtime": [
|
||||
{
|
||||
"path": "resources/js/components/acquisition-beverage-restitution.tsx",
|
||||
"sha256_LF": "6f44897b8b12569314551df0022c26da57a165634087942a724293e66837558d"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/fiscal-calculation-gaps.tsx",
|
||||
"sha256_LF": "1dab6ceb73756d4e023096b3b2ba767aaa843ff6cc69046ea630ab449a00b0dd"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/fiscal-l3-preparation.tsx",
|
||||
"sha256_LF": "e84a7b1ccadaa1b5bd78d778a3287f96ccaf530855eef404786cd7aa3d321b73"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-acquisitions.tsx",
|
||||
"sha256_LF": "c206d4cfe575d6ba007aa2b425b5f898efa97e4162b8710e358b6ecfd9458542"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-operations.tsx",
|
||||
"sha256_LF": "a7e55972fa790063dcf11945055de2af46a77db9e509900af22ede7ec47bb4cf"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/iva-acquisition-bc.ts",
|
||||
"sha256_LF": "b0ef55e0917bdb79f14b53ba9e95d73c08153befeb7d4ce73f81e6fb13992225"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/iva-beverage-allocation.ts",
|
||||
"sha256_LF": "32968d198c7fe0c53ca89641c22d5749be6636be2c79127b867e4e3afd0dd40d"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/iva-beverage-restitution.ts",
|
||||
"sha256_LF": "5c650cbd9469d5f99001f56fc4ff99ac973eb368036d4e96c6d171f1b220bd9e"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/iva-calculation-gaps.ts",
|
||||
"sha256_LF": "2e7d84a0c196bd5b56fd67cc1a389d43d5041894184e675680a4de3cd10b31a2"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/iva-operations.ts",
|
||||
"sha256_LF": "ec37b949f1233c5f5c14a87450c421ed29fabf1ed5b73fb32564564a2bc31f54"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/l3-form-preparation.ts",
|
||||
"sha256_LF": "fc47f5fa5ec1420fbd847d0d081f00d99ebbb524164eef785b20877e0562f704"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/ispc-residuals.ts",
|
||||
"sha256_LF": "40d5751c06c7c155ab4b7a17ee2d1653da2fbbf075807fbb8561cdbe19820955"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-ispc-residuals.tsx",
|
||||
"sha256_LF": "a9219d6f41ccbd0ce45659d4f6aa0fa1c169a685840f5cb1e18daeca377d5e18"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/ispc-eligibility.ts",
|
||||
"sha256_LF": "44675c5efacdc1ae19505667611ba3b522d3043cebacc8754597c3a68ca89d25"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-ispc-quarter.tsx",
|
||||
"sha256_LF": "2c2555309b32d4261f14e7164f39314c9188129c2990357fc54090a2468dd2b4"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-ispc-withholding.tsx",
|
||||
"sha256_LF": "e0445d015b72859e48726959aa52686f79b1cf5c2ade29751efa83257068b878"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/ispc-quarter.ts",
|
||||
"sha256_LF": "f66bd52e9cdfe1ddaa6b00a931465862b3d890b9be216b7a056740f027f24129"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/ispc-withholding.ts",
|
||||
"sha256_LF": "6c31b240299edd3262efa28f7f3d5aee21123f94e30ef4ab90e26c35777cb6e4"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-ispc-eligibility.tsx",
|
||||
"sha256_LF": "f48dd4a8f87a994eda0447f81a463589905e5873b2e1044debac9d10825a880c"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/iva-acquisitions.ts",
|
||||
"sha256_LF": "cce283b1d0a07fb383fcb527225f4dd5bec829010a662cbaae1d60e595221977"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/iva-modelo-a.ts",
|
||||
"sha256_LF": "c641619a05613c1620385baad0e9ffbf810dd970aafb30e12b3a7f9a3bf11846"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/iva-fx-selection.ts",
|
||||
"sha256_LF": "9aa07ad443bc777b80510a3b5bd6aff9ddb9814d2450f3c690e531e74d71d998"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-fx-selection.tsx",
|
||||
"sha256_LF": "39bb6d87d404745b0b66349bc11626545a3672ae27321ce9b4658416592059bf"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/business-financial-plan.ts",
|
||||
"sha256_LF": "c20274722d1ffcd0360f041a6cacb5d868fe0621cb27a1aca6e06826612c3ebb"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/iva-currency-events.ts",
|
||||
"sha256_LF": "cc9f2045d302d7987310da4006cc250fc5c99a27397918d166d649bfc32fab08"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-fx.ts",
|
||||
"sha256_LF": "5c4a7a477429f8d775acf6db3e117008468f5554e5731dec81f60fbb75e948fb"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-withholdings.ts",
|
||||
"sha256_LF": "8e5f3c035ce5e4d6c3fc82f933439ab580fa5519492f9c4d32cfb71b77b54803"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-irpc-withholdings.tsx",
|
||||
"sha256_LF": "751af4fab346659bc5ccb08b8a2169769af7521ea0035050af32b41ea7f5aa76"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-interest.ts",
|
||||
"sha256_LF": "f8e6390b3b2c58c296f7e3fd14576e06ae248bc0cabce1a666b7094611f2a7dd"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-mrfta.ts",
|
||||
"sha256_LF": "76dcce8954f68b081d500be61de9c5b146a452a3eb578a0b78776ba9e1f8051c"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-mrfta-cases.ts",
|
||||
"sha256_LF": "4266746076ab3f70312fd042351e24dd2f136c4f3e8a277b57dd97849a9f1862"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-irpc-mrfta.tsx",
|
||||
"sha256_LF": "913d1bb4ac0fc59763cf5d27bc637f6a713b2050c4463748db554af4be451538"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/financial-fiscal-bridge.ts",
|
||||
"sha256_LF": "5a30c501653b16b5fedcb12631ce81fcce4fe473aa542e94374986b3c50f2d33"
|
||||
}
|
||||
],
|
||||
"originals": [
|
||||
{
|
||||
"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-20-2009-BR36-AT-exemplar19.pdf",
|
||||
"sha256": "fc1c8144db42960cbf73e717e095896f511ece0f907f58f78819b27d6917a7ee"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-22-2022-IVA-republicada-BR-250-2-suplemento.pdf",
|
||||
"sha256": "ea1b0dc6f5cf6e595f6d9a19e7042f398999e0087caef1785f6e8fac571b5f53"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-34-2007-BR52.pdf",
|
||||
"sha256": "cdbef875ad7ccc1517b12faf5de0e893a70ed2ed81e33913cd6dc4a674474e6e"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-4-1994-BR37-3-suplemento.pdf",
|
||||
"sha256": "210df9db8980d0a5690cd187a6eeec7e37a900d743e39062c16f4e0d12215c7e"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-4-2012-BR3.pdf",
|
||||
"sha256": "b8845cef41dcbd9ba50c43dbfa743a56030dd3d348242a107e1ae30279eec121"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-7-1991-Partidos-IFES.pdf",
|
||||
"sha256": "6b783a0bb3330688ef10660a58060d9724aa2da6a873d234fe902f0eaaeda42d"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/PT-MZ-CDT-1992.pdf",
|
||||
"sha256": "d504e1024b28aee5c6acf4592acb96ec9c915a8414f67a61ab6984d6acf9df9d"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/PT-MZ-Protocolo-2009.pdf",
|
||||
"sha256": "f353773dcf41f09f4daa6cf5ba5a5f7994f3e02615867c2dcffc61c3d901bf9b"
|
||||
}
|
||||
],
|
||||
"new_source_files": 0,
|
||||
"source_review": "Existing accepted sources only. Visually read Lei34/2007 PDF6/p768(60),PDF7/p768(61):17/18/20/22;Lei20/2009 official AT archived exemplar PDF2/3,p272(14/15):18/20/21/22/24;remaining accepted amendment chain reused. New nonzero variations/other corrections not activated. Official webdav page inaccessible via web tool;archived official exact exemplar used,not secondary reconstruction. No L5 rate/CER search.",
|
||||
"proof_sha256": {
|
||||
"api.txt": "13961d8421e329123ae99e4202e182ef1089de5c5e7d28b503d3b15797eab1b2",
|
||||
"build-exit.txt": "5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9",
|
||||
"build.txt": "8272c7eb2f6058ddf64925103d5cc9ac9e4707c0a518c7073ecf04c690fb58e0",
|
||||
"calculated.json": "105857393035cb5e7c569dc9e6c7b91f51c335b9799fc148b1bfee57057f3138",
|
||||
"dossier-calculated.json": "addc82429306cac3ff0d25899f0cad48ec3756e0a8cbfbc0ec8c34507e9ca346",
|
||||
"dossier-missing.json": "b790abf0a99ef2e13091c5f63d2f697b3424133d676e97570a6b4d2196bf11e8",
|
||||
"download.html": "14935a24e370cd2378eac64264734591981c40e25c7beffbc15a9d82646c7c32",
|
||||
"frontend.txt": "ca819072c4ee39ff25d70f1d2ef4c374f7e0e4565ed59d26dd63f08c9e375bfc",
|
||||
"index.html": "9af7b69c19ca2fb213988ace8eb927008cb0bea73e267f16dee363fcdbcb0f12",
|
||||
"Lei-20-2009-BR36-AT-exemplar19.pdf-page2.png": "e39eadef731b6c3ae8cb4820238c4db3184111557d982a20dadee139ea2952a2",
|
||||
"Lei-20-2009-BR36-AT-exemplar19.pdf-page3.png": "a4a39a81849f32e4c596ef7c0726d2054e74bf45531dd8e920d3927cafdb70bd",
|
||||
"Lei-34-2007-BR52.pdf-page6.png": "de2c1cc9c8bf5b0cefa9671cc8ad776e21a8492b791648be492d1540d78f748f",
|
||||
"Lei-34-2007-BR52.pdf-page7.png": "7a934a93b77131bac30455309228232dc15c6ccd614893d6a0497f692d7970c4",
|
||||
"missing.json": "7ca5bf593230bd973b1098d8e82866f5c6e0d534c5aa0f0d794e23ffc77ce7b4",
|
||||
"preparation.html": "9a07ab3a4e7fbce036f9337c60cb1bafd385395070c38af57a9becffda852df6",
|
||||
"recovered-calculated.html": "9a07ab3a4e7fbce036f9337c60cb1bafd385395070c38af57a9becffda852df6",
|
||||
"recovered-calculated.json": "36afb1c036d7470282df6e083456da4c0be1991660a5944e784d6ae24a116e5b",
|
||||
"recovered-missing.html": "8167c25ec05f66b9a26f53d185ee7bbc64ae6ed4dc4812e4fff010b0e966eacf",
|
||||
"recovered-missing.json": "e7727a76405c2ac848c90379543e88a36d6be2ee734a2565245c5ad4e4cb6bf3",
|
||||
"types.txt": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"ui-missing.png": "aa07cc3b77c7a3a12944f1790a492ed96a3796ee3a21ea1d0fd757e7f0832996",
|
||||
"ui-profit.png": "f4359bd9719bea78587c7024abc1e72d4fbc02ea392ffed92402f7e9649b3ee8",
|
||||
"ui-proof.json": "015fd14e35d12a27c02640712222beedc00773f6b55e8a9aa94826ff120ed2f6",
|
||||
"ui.txt": "7bb7fc5145f1226fd948164fadf9f2f6a4e9efb1f070e90530b93aea4fdd13d7"
|
||||
},
|
||||
"prepared_joint_delivery": "Includes664c88c8 plus this delta,base7a11c4ef;Git bundle/manifest to be verified after commit. No push or installation;fresh destination/backup/restore remain installation steps.",
|
||||
"active_changes": false,
|
||||
"schema_API_config_permissions_dependencies_delta": false,
|
||||
"L5_operational_demonstrated_scope": true,
|
||||
"L5_complete": false,
|
||||
"L6_complete": false,
|
||||
"L5_AT_questions": "Ten questions J1-J5/C1-C5 unchanged;only dependent cases limited"
|
||||
}
|
||||
@@ -177,3 +177,8 @@ Inventário oficial AT189documentos recuperado em `outputs/irpc-interest-cer/at-
|
||||
## 25/10/2026 — L6, cadeia do primeiro acréscimo36(1)c
|
||||
|
||||
Reutilizados30originais aceites,sem alteração. Conferidos visualmenteLei34/2007PDF6/p768(60) ePDF10/p768(64);Lei4/2012PDF2/p34(5) ePDF3/p34(6);Lei19/2013PDF1/2,p682(21/22). Novo exemplar oficialAT,catálogo19/2009,conserva5páginas do4.ºsuplementoBR36,10/09/2009,incluindoLei20/2009. PDF4/p272(16)confirma36(1)a–i mantidas eaditamentosj–n;fonte original/hash/URL no reciboirpc-l6-adjustments-qa-20261025.json. Correspondência executável limitada36(1)c;demais artigos não declarados implementados. Lei20/2022altera61/62 eLei12/2025altera3/5/20/62/67/75,adita61-A/revogações próprias,conforme provas aceites. Nova consulta responde só à cadeia do acréscimoL6,não às pendênciasL5. Sem publicação/importação documental,scan ou certificação presumidos.
|
||||
|
||||
|
||||
## 25/10/2026 — L6: conferência do recorte até lucro tributável
|
||||
|
||||
Sem novos originais:31exemplares da QA664c88c8 preservados por hash. Reutilizada a fonte oficial aceite Lei34/2007, PDF6/p768(60),17/18, e PDF7/p768(61),20/22; Lei20/2009, exemplar oficialAT já arquivado, PDF2/3,p272(14/15), alterações18/20/21/22/24, conferidas visualmente. A cláusula geral de custos indispensáveis e as receitas ordinárias aplicam-se mediante qualificação expressa do caso; não se presume dedutibilidade de todas as linhas de um plano. As demais alterações aceites são reutilizadas no âmbito já documentado. Variações/correcções conhecidas não zero,amortizações e juros financeiros remanescentes não ganham regra neste incremento. A abertura do webdav pela ferramenta web falhou; foi usado o exemplar oficial arquivado exacto, sem reconstrução secundária ou nova validação genérica. Nenhuma pesquisa das pendênciasL5 foi reaberta.
|
||||
|
||||
@@ -5977,3 +5977,18 @@ Exemplo sintético:resultado financeiro6480MZN,multa100 e juros compensatórios2
|
||||
Prova do delta:7testes novos;APIprivada1teste/19asserções comrollback naQA55441;routes/actions gerados pelo procedimentoWayfinder;TypeScript/buildQA passaram. UI real:cálculo,gravação/recuperação local,relatório,informação insuficiente e download;API demonstrada separadamente,dossier gerado da recuperaçãoAPI. LivroL6 separado mantém payloadL5 e restante histórico;alterar plano/entradas torna resultado histórico sem o apagar.32ficheiros anteriores protegidos e30originais sem alteração;integração mínima emsimulation-form/project-dossier/project-dossier-report. Provas aceites não repetidas. Recibo:`irpc-l6-adjustments-qa-20261025.json`;QA isolada,sem instalaçãoL6.
|
||||
|
||||
Fonte adicional necessária apenas paraL6:exemplar oficialAT intitulado19/2009 contém tambémLei20/2009;PDF4/p272(16),36(1)a–i mantidas ealíneasj–n aditadas,conferido visualmente. Lei4/2012PDF2/3 e19/2013PDF1/2 mantêm36(1)c. Não reabre as dez dúvidasL5,que ficam paraAT até informação nova. L5 eL6 não integralmente concluídos.
|
||||
|
||||
|
||||
## 25/10/2026 — L6: percurso delimitado até ao lucro tributável demonstrado em QA
|
||||
|
||||
Base instalada `7a11c4ef`; primeiro recorte QA `664c88c8` preservado. A continuação reutiliza o plano reconciliado, o exercício, a oportunidade e os ajustamentos existentes. Não obriga a reintroduzir os montantes financeiros. Âmbito: projecção de Janeiro a Dezembro de 2026, MZN, sujeito residente no regime geral, com referência expressa de que o cenário cobre o sujeito passivo/exercício completo. Uma oportunidade parcial não se transforma automaticamente no total da empresa.
|
||||
|
||||
Cada linha do resultado exige qualificação ordinária e suporte: proveitos do exercício (18/20), custos remanescentes indispensáveis e documentados (22), sem outras exclusões/limites declarados. Os acréscimos 36(1)c reutilizam documentos e parcelas já registados. Seis grupos conservam a situação das variações patrimoniais positivas/negativas fora do resultado, outras correcções de rendimentos/custos, diferenças de periodização e operações especiais. **Valor conhecido (incluindo zero), ausência declarada com suporte e informação em falta são estados distintos.** Ausência não pode apagar uma origem financeira positiva ou descartar um montante já preenchido. Um valor conhecido não zero sem tratamento implementado permanece visível e impede apenas o fecho dependente, sem ser aplicado por presunção.
|
||||
|
||||
No caso ordinário delimitado, resultado financeiro + acréscimos fundamentados = lucro tributável simulado quando a conferência anual está completa e os outros efeitos são expressamente ausentes ou de valor conhecido zero. Exemplo sintético: 6480 + multa100 + juros compensatórios120 = 6700 MZN. Os 120 são um custo/pressuposto qualificado já incluído na origem; não são juros automaticamente calculados ou fiscalmente confirmados por L5. Falta de prova da variação patrimonial negativa mantém o lucro indeterminado, conservando resultado6480 e acréscimos220. Variação conhecida500, depreciação positiva ou juros financeiros remanescentes mantêm correspondência específica pendente. Sem acréscimos, uma conferência completa pode conservar o resultado financeiro; resultados negativos não são convertidos em zero nem em crédito/perda reportável.
|
||||
|
||||
Prova do delta: 9 testes novos; API privada 1 teste/38 verificações, criação → alteração com If-Match → recuperação, com rollback na QA isolada55441. Wayfinder gerou routes/actions; TypeScript e buildQA passaram. UI real: cálculo, gravação/recuperação local, valor conhecido zero, ausência, informação em falta, contradição e exportação HTML. Relatórios/dossiers dos casos conhecido e incompleto gerados a partir da recuperação efectiva da API. Versão2 adicional mantém o snapshot/resultado1 de664c88c8 legível e actual sem o recalcular; mudanças nas entradas desactualizam apenas o novo resultado e conservam histórico. Preservados33ficheiros anteriores de execução e31originais por hash;integrações anteriores de formulário/dossier não alteradas neste delta. Provas aceites L3/ISPC/câmbio/L5 reutilizadas, sem nova ronda manual.
|
||||
|
||||
**Preparação conjunta para entrega pelo Git:** primeiro recorte664c88c8 e continuação até lucro tributável, sobre7a11c4ef, com pacote/manifest e delta conferido. Não instalado nem integrado em main por esta preparação. Backup/restauro fresco e conferência do destino fazem parte da instalação posterior; não reutilizar como fresco o backup da entregaL5. Recibo `irpc-l6-taxable-profit-qa-20261025.json`; provas `outputs/irpc-l6-profit/` na QA isolada.
|
||||
|
||||
Matéria colectável, perdas reportadas, deduções, colecta, créditos de retenção e M20/M22 continuam fora deste percurso; L6 não está integralmente concluído. L5 permanece operacional no âmbito demonstrado; J1–J5/C1–C5 ficam para a AT e só limitam os casos efectivamente dependentes. Nenhuma nova pesquisa de taxas/CER de L5 foi feita.
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,4 +1,5 @@
|
||||
import {useState} from 'react';
|
||||
import {newNormalProfitInput,profitTopics,type NormalProfitInput,type ProfitEvidence} from '@/lib/irpc-normal-profit';
|
||||
import {normalBook,saveNormalBook,normalFinancialSource,newNormalAdjustment,recordNormal,normalHtml,normalReportRows,type NormalBook,type NormalAdjustment} from '@/lib/irpc-normal';
|
||||
|
||||
export function SimulationIrpcNormal({draft,onChange}:{draft:Record<string,unknown>;onChange:(field:string,value:string)=>void}){
|
||||
@@ -7,8 +8,9 @@ export function SimulationIrpcNormal({draft,onChange}:{draft:Record<string,unkno
|
||||
const b=normalBook(draft),source=normalFinancialSource(draft);
|
||||
const save=(next:NormalBook)=>{try{onChange('modelo_a_snapshot',saveNormalBook(draft,next));setError('');}catch(e){setError(String(e));}};
|
||||
const patch=(id:string,p:Partial<NormalAdjustment>)=>save({...b,rows:b.rows.map(r=>r.id===id?{...r,...p}:r)});
|
||||
const profit=(patch:Partial<NormalProfitInput>)=>save({...b,profit:{...(b.profit??newNormalProfitInput()),...patch}});
|
||||
const field=(r:NormalAdjustment,k:'document'|'line'|'amount'|'reference',label:string)=><label className="block">{label}<input value={r[k]} onChange={e=>patch(r.id,{[k]:e.target.value})}/></label>;
|
||||
return<section aria-label="IRPC normal — ajustamentos" className="space-y-3 rounded border p-4"><h4>L6 — IRPC normal: ajustamentos ao resultado financeiro</h4><p>Primeiro recorte: acréscimos de multas sem origem contratual e juros compensatórios incluídos nos custos do plano, artigo 36(1)c. Projecção com pressupostos, sem apuramento integral do lucro tributável, matéria colectável ou imposto. Não reutiliza automaticamente juros simulados de L5.</p>{error&&<p role="alert">{error}</p>}
|
||||
return<section aria-label="IRPC normal — ajustamentos" className="space-y-3 rounded border p-4"><h4>L6 — IRPC normal: ajustamentos ao resultado financeiro</h4><p>Acréscimos de multas sem origem contratual e juros compensatórios incluídos nos custos do plano, artigo 36(1)c. A conferência anual pode determinar o lucro tributável simulado no recorte ordinário documentado. Matéria colectável e imposto continuam por apurar. Não reutiliza automaticamente juros simulados de L5.</p>{error&&<p role="alert">{error}</p>}
|
||||
{source?<p>Origem: plano financeiro desta oportunidade; {source.periods} meses; {source.currency}; resultado antes de imposto: {source.accountingProfitBeforeTax}. Cobranças, pagamentos e reembolso de capital ficam fora do resultado.</p>:<p>Prepare o plano financeiro para reutilizar o resultado e os custos reconciliados.</p>}
|
||||
<label className="block">Exercício fiscal<input value={b.year} onChange={e=>save({...b,year:e.target.value})}/></label>
|
||||
<label className="block">Correspondência dos 12 meses do plano a Janeiro–Dezembro de 2026<input value={b.mappingReference} onChange={e=>save({...b,mappingReference:e.target.value})}/></label>
|
||||
@@ -18,6 +20,12 @@ export function SimulationIrpcNormal({draft,onChange}:{draft:Record<string,unkno
|
||||
{b.rows.map(r=><fieldset key={r.id} className="space-y-2 rounded border p-3"><legend>{r.document||'Ajustamento por identificar'}</legend><label className="block">Custo de origem na ponte<select value={r.sourceCode} onChange={e=>patch(r.id,{sourceCode:e.target.value})}><option value="">Seleccione</option>{source?.accountingLines.filter(l=>l.accountingEffect==='expense').map(l=><option key={l.code} value={l.code}>{l.origin}: {l.total} {source.currency}</option>)}</select></label>{field(r,'document','Documento ou pressuposto identificado')}{field(r,'line','Linha distinta do documento')}{field(r,'amount','Parcela do custo em MZN')}
|
||||
<label className="block">Natureza documentada<select value={r.rule} onChange={e=>patch(r.id,{rule:e.target.value as NormalAdjustment['rule']})}><option value="">Por classificar</option><option value="noncontractual_fine">Multa/encargo por infracção sem origem contratual — 36(1)c</option><option value="compensatory_interest">Juros compensatórios — 36(1)c</option><option value="other">Outro ajustamento — por corresponder</option></select></label>
|
||||
<label className="block">Parcela incluída nos custos do resultado de partida<select value={r.included} onChange={e=>patch(r.id,{included:e.target.value as NormalAdjustment['included']})}><option value="">Por demonstrar</option><option value="yes">Sim, reconciliada com a linha de origem</option><option value="no">Não — não acrescentar novamente</option></select></label>{field(r,'reference','Suporte da qualificação, ocorrência e inclusão contabilística')}<p>Juros bancários e penalidades contratuais não são classificados automaticamente como infracções.</p></fieldset>)}
|
||||
{!b.profit?<button type="button" onClick={()=>profit({})}>Iniciar conferência até ao lucro tributável</button>:<fieldset aria-label="Conferência do lucro tributável" className="space-y-3 rounded border p-3"><legend>Resultado financeiro → ajustamentos → lucro tributável simulado</legend><p>Conferência do sujeito passivo/exercício completo. Cada receita remanescente deve ser ordinária do exercício;cada custo remanescente deve estar documentado e ser indispensável à fonte produtora,sem outras exclusões ou limites. Outras situações conhecidas permanecem discriminadas. Ausência declarada exige suporte e não é informação em falta.</p>
|
||||
<label className="block">Suporte da completude do sujeito passivo no exercício<input value={b.profit.completenessReference} onChange={e=>profit({completenessReference:e.target.value})}/></label>
|
||||
{source?.accountingLines.map(l=>{const review=b.profit!.lines.find(r=>r.code===l.code)??{code:l.code,state:'' as const,reference:''};const change=(p:Partial<typeof review>)=>profit({lines:[...b.profit!.lines.filter(r=>r.code!==l.code),{...review,...p}]});return<fieldset key={l.code} className="rounded border p-2"><legend>{l.origin} — {l.total} {source.currency}</legend><label className="block">Estado fiscal — {l.origin}<select aria-label={'Estado fiscal — '+l.origin} value={review.state} onChange={e=>change({state:e.target.value as ProfitEvidence['state']})}><option value="">Informação em falta</option><option value="known">Valor conhecido e qualificação ordinária documentada</option><option value="absent">Ausência declarada — conferir origem zero</option></select></label><label className="block">Suporte fiscal — {l.origin}<input value={review.reference} onChange={e=>change({reference:e.target.value})}/></label></fieldset>;})}
|
||||
<p>Depreciação positiva e juros financeiros não integralmente acrescidos exigem correspondência própria;uma declaração de ausência não apaga os valores do plano.</p>
|
||||
{profitTopics.map(([k,label])=>{const t=b.profit!.topics[k];const change=(p:Partial<ProfitEvidence>)=>profit({topics:{...b.profit!.topics,[k]:{...t,...p}}});return<fieldset key={k} className="rounded border p-2"><legend>{label}</legend><label className="block">Estado — {label}<select aria-label={'Estado — '+label} value={t.state} onChange={e=>change({state:e.target.value as ProfitEvidence['state']})}><option value="">Informação em falta</option><option value="known">Valor conhecido</option><option value="absent">Ausência declarada com suporte</option></select></label>{(t.state==='known'||t.amount!=='')&&<label className="block">Montante conhecido — {label}<input value={t.amount} onChange={e=>change({amount:e.target.value})}/></label>}<label className="block">Suporte — {label}<input value={t.reference} onChange={e=>change({reference:e.target.value})}/></label></fieldset>;})}
|
||||
</fieldset>}
|
||||
<button type="button" onClick={()=>{try{save(recordNormal(draft,b));}catch(e){setError(String(e));}}}>Calcular e guardar ajustamentos L6</button>
|
||||
<button type="button" onClick={()=>{try{const html=normalHtml(draft),url=URL.createObjectURL(new Blob([html],{type:'text/html;charset=utf-8'})),a=document.createElement('a');a.href=url;a.download='IRPC-normal-ajustamentos.html';a.click();URL.revokeObjectURL(url);setError('');}catch(e){setError(String(e));}}}>Exportar mapa parcial L6</button>
|
||||
{normalReportRows(draft).map((r,n)=><p key={n}>{r[1]} — {r[2]} — {r[3]}</p>)}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import {addFractions,decimalFraction,fraction,type FiscalFraction} from './fiscal-rational.ts';
|
||||
|
||||
export const normalProfitVersion='IRPC-normal-adjustments/2';
|
||||
export type ProfitEvidence={state:''|'known'|'absent';amount:string;reference:string};
|
||||
export const profitTopics=[
|
||||
['positivePatrimonial','Variações patrimoniais positivas fora do resultado'],
|
||||
['negativePatrimonial','Variações patrimoniais negativas fora do resultado'],
|
||||
['incomeCorrections','Outras correcções aos proveitos, incluindo exclusões'],
|
||||
['expenseCorrections','Outras correcções aos custos, além dos acréscimos registados'],
|
||||
['timing','Diferenças de periodização e de critérios contabilísticos/fiscais'],
|
||||
['special','Operações especiais, benefícios e regimes fora deste recorte'],
|
||||
]as const;
|
||||
export type ProfitTopic=typeof profitTopics[number][0];
|
||||
export type NormalProfitInput={version:1;completenessReference:string;lines:Array<{code:string;state:''|'known'|'absent';reference:string}>;topics:Record<ProfitTopic,ProfitEvidence>};
|
||||
export const newNormalProfitInput=():NormalProfitInput=>({version:1,completenessReference:'',lines:[],topics:Object.fromEntries(profitTopics.map(([k])=>[k,{state:'',amount:'',reference:''}]))as NormalProfitInput['topics']});
|
||||
export function validateNormalProfit(i:NormalProfitInput){
|
||||
if(!i||i.version!==1||typeof i.completenessReference!=='string'||!Array.isArray(i.lines)||i.lines.some(l=>!l||typeof l.code!=='string'||typeof l.reference!=='string'||!['','known','absent'].includes(l.state))||new Set(i.lines.map(l=>l.code)).size!==i.lines.length||!i.topics||typeof i.topics!=='object'||Array.isArray(i.topics))throw Error('Conferência do lucro tributável não recuperável.');
|
||||
for(const[k]of profitTopics){const t=i.topics[k];if(!t||!['','known','absent'].includes(t.state)||typeof t.amount!=='string'||typeof t.reference!=='string')throw Error('Estado documental do lucro tributável não recuperável.');}
|
||||
}
|
||||
type Source={currency:string;periods:number;accountingLines:Array<{code:string;origin:string;accountingEffect:'income'|'expense';total:string}>};
|
||||
export type NormalProfitModel={input:NormalProfitInput;lines:Array<{code:string;label:string;amount:string;addedBack:FiscalFraction;remainder:FiscalFraction|null;state:ProfitEvidence['state'];reference:string;reason:string}>;topics:Array<ProfitEvidence&{key:ProfitTopic;label:string;value:FiscalFraction|null;reason:string}>;pending:string[];taxableProfit:FiscalFraction|null;source:string};
|
||||
const zero=()=>fraction(0n,1n);
|
||||
function money(s:string){if(!/^-?\d+(?:\.\d{1,2})?$/.test(s))throw Error('Montante inválido');const v=decimalFraction(s.startsWith('-')?s.slice(1):s);return s.startsWith('-')?fraction(-BigInt(v.numerator),BigInt(v.denominator)):v;}
|
||||
const units=(f:FiscalFraction)=>BigInt(f.numerator)*100n/BigInt(f.denominator);
|
||||
const difference=(a:FiscalFraction,b:FiscalFraction)=>fraction(BigInt(a.numerator)*BigInt(b.denominator)-BigInt(b.numerator)*BigInt(a.denominator),BigInt(a.denominator)*BigInt(b.denominator));
|
||||
export function closeNormalProfit(input:NormalProfitInput,source:Source|null,rows:Array<{sourceCode:string;adjustment:FiscalFraction|null;status:string}>,accounting:FiscalFraction|null,additions:FiscalFraction,contextReady:boolean):NormalProfitModel{
|
||||
validateNormalProfit(input);const pending:string[]=[],lines:NormalProfitModel['lines']=[],topics:NormalProfitModel['topics']=[];
|
||||
if(!contextReady)pending.push('Âmbito,exercício,moeda ou correspondência anual ainda insuficientes.');
|
||||
if(!input.completenessReference.trim())pending.push('Documente que o plano e os ajustamentos abrangem o sujeito passivo/exercício completo;uma oportunidade isolada não equivale automaticamente ao conjunto da empresa.');
|
||||
if(rows.some(r=>r.status!=='calculated'))pending.push('Existem ajustamentos registados por resolver;acréscimos independentes conservados.');
|
||||
if(!source)pending.push('Falta resultado financeiro reconciliado.');
|
||||
for(const l of source?.accountingLines??[]){
|
||||
const review=input.lines.find(r=>r.code===l.code),state=review?.state??'',reference=review?.reference??'';
|
||||
const addedBack=addFractions(rows.filter(r=>r.sourceCode===l.code&&r.adjustment).map(r=>r.adjustment!));let remainder:FiscalFraction|null=null;const issues:string[]=[];
|
||||
try{const amount=money(l.total);remainder=difference(amount,addedBack);if(units(amount)<0n||units(remainder)<0n)issues.push('Valor negativo ou acréscimos superiores à origem;reconcilie a linha sem compensação presumida.');
|
||||
if(!state||!reference.trim())issues.push('Conferência fiscal e suporte da linha em falta.');
|
||||
if(state==='absent'&&(units(amount)!==0n||units(addedBack)!==0n))issues.push('Ausência declarada contradiz o montante conhecido da origem.');
|
||||
if(state==='known'&&l.code==='depreciation'&&units(amount)!==0n)issues.push('Depreciação contabilística conhecida;amortização fiscal por corresponder,não presumir igualdade.');
|
||||
if(state==='known'&&l.code==='interest_expense'&&units(remainder)!==0n)issues.push('Juros financeiros remanescentes conhecidos;dedutibilidade e eventuais limites por corresponder. Só os juros compensatórios já acrescidos estão cobertos.');
|
||||
if(!['revenue','cost_of_sales','operating_expenses','personnel','depreciation','interest_expense'].includes(l.code))issues.push('Linha financeira sem correspondência neste recorte.');
|
||||
}catch{issues.push('Montante da linha por reconciliar.');}
|
||||
const reason=issues.join(' ')|| (state==='absent'?'Ausência expressamente declarada e compatível com a origem zero.':units(remainder??zero())===0n?'Valor conhecido,integralmente corrigido ou igual a zero;sem presumir ausência.':l.accountingEffect==='income'?'Proveito ordinário reconhecido no exercício,qualificado e documentado nos termos dos artigos18/20.':'Parcela remanescente qualificada como custo ordinário indispensável e documentado do artigo22,sem exclusões/limites adicionais declarados.');
|
||||
if(issues.length)pending.push(l.origin+': '+reason);
|
||||
lines.push({code:l.code,label:l.origin,amount:l.total,addedBack,remainder,state,reference,reason});
|
||||
}
|
||||
for(const l of input.lines)if(!source?.accountingLines.some(s=>s.code===l.code))pending.push('Conferência sem linha de origem actual: '+l.code);
|
||||
for(const[key,label]of profitTopics){const t=input.topics[key];let value:FiscalFraction|null=null,reason='';
|
||||
if(!t.state||!t.reference.trim())reason='Informação em falta;sem zero presumido.';
|
||||
else if(t.state==='absent'){if(t.amount.trim())reason='Ausência declarada com montante preenchido;reconcilie os dois estados.';else{value=zero();reason='Ausência expressamente declarada com suporte;parcela zero apenas por esta declaração.';}}
|
||||
else{try{value=money(t.amount);if(units(value)!==0n)reason='Montante conhecido,mas tratamento específico ainda por corresponder;conservado sem aplicação presumida.';else reason='Valor conhecido zero com suporte;distinto de ausência declarada.';}catch{reason='Montante conhecido por preencher ou inválido.';}}
|
||||
if(value===null||units(value)!==0n||t.state==='absent'&&!!t.amount.trim())pending.push(label+': '+reason);
|
||||
topics.push({...structuredClone(t),key,label,value,reason});
|
||||
}
|
||||
return{input:structuredClone(input),lines,topics,pending,taxableProfit:pending.length===0&&accounting?addFractions([accounting,additions]):null,source:'CIRPC17(1)/18/20/22/36(1)c;recorte anual2026,residente geral,MZN. Resultado antes de imposto + acréscimos36(1)c;demais correcções/variações sem efeito apenas quando ausência ou zero conhecidos expressamente suportados. Receita/custos ordinários classificados pelo utilizador;depreciação fiscal e juros financeiros remanescentes fora deste percurso. Lucro tributável simulado,não matéria colectável,colecta ou declaraçãoM20/22.'};
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import {normalProfitVersion,validateNormalProfit,closeNormalProfit,type NormalProfitInput,type NormalProfitModel} from './irpc-normal-profit.ts';
|
||||
import {forecastBusinessPlan,restoreBusinessPlanSnapshot} from './business-financial-plan.ts';
|
||||
import {buildFinancialFiscalBridge} from './financial-fiscal-bridge.ts';
|
||||
import {acquisitionScope} from './iva-acquisitions.ts';
|
||||
@@ -6,8 +7,8 @@ import {addFractions,decimalFraction,fraction,fractionPresentation,type FiscalFr
|
||||
export const irpcNormalVersion='IRPC-normal-adjustments/1';
|
||||
export const irpcNormalSource='CIRPC Lei34/2007 art17(1),PDF6/p768(60):resultado e correcções;art36(1)c,PDF10/p768(64):multas e encargos por infracções sem origem contratual,incluindo juros compensatórios. Lei4/2012 PDF2 altera17(6)/acrescenta36(1)o;Lei19/2013 PDF2 mantém36(1)a–o e acrescenta p/q. Lei12/2025 altera3/5/20/62/67/75,adita61-A e revoga39/47/75(2)/76;sem alteração deste recorte36(1)c. Não é apuramento integral nem Modelo20/22.';
|
||||
export type NormalAdjustment={id:string;sourceCode:string;document:string;line:string;amount:string;rule:''|'noncontractual_fine'|'compensatory_interest'|'other';included:''|'yes'|'no';reference:string};
|
||||
export type NormalBook={version:1;year:string;mappingReference:string;scope:''|'resident_general'|'other';scopeReference:string;rows:NormalAdjustment[];result?:NormalRecord;history?:NormalRecord[]};
|
||||
type NormalModel={version:string;year:string;currency:string;origin:'projected';accountingProfit:FiscalFraction|null;additions:FiscalFraction;partialAdjustedResult:FiscalFraction|null;taxableProfit:null;taxableBase:null;tax:null;rows:Array<NormalAdjustment&{status:'calculated'|'indeterminate'|'outside_scope';adjustment:FiscalFraction|null;reason:string}>;pending:string[];source:string};
|
||||
export type NormalBook={version:1;year:string;mappingReference:string;scope:''|'resident_general'|'other';scopeReference:string;rows:NormalAdjustment[];profit?:NormalProfitInput;result?:NormalRecord;history?:NormalRecord[]};
|
||||
type NormalModel={profit?:NormalProfitModel;version:string;year:string;currency:string;origin:'projected';accountingProfit:FiscalFraction|null;additions:FiscalFraction;partialAdjustedResult:FiscalFraction|null;taxableProfit:FiscalFraction|null;taxableBase:null;tax:null;rows:Array<NormalAdjustment&{status:'calculated'|'indeterminate'|'outside_scope';adjustment:FiscalFraction|null;reason:string}>;pending:string[];source:string};
|
||||
type NormalRecord={key:string;createdAt:string;model:NormalModel};
|
||||
export const newNormalBook=():NormalBook=>({version:1,year:'',mappingReference:'',scope:'',scopeReference:'',rows:[]});
|
||||
export const newNormalAdjustment=(id:string):NormalAdjustment=>({id,sourceCode:'',document:'',line:'',amount:'',rule:'',included:'',reference:''});
|
||||
@@ -18,19 +19,20 @@ export function normalFinancialSource(d:Record<string,unknown>){
|
||||
}
|
||||
function store(d:Record<string,unknown>){const s=JSON.parse(String(d.modelo_a_snapshot||'{"version":1,"books":{}}'));if(s.version!==1||!s.books||typeof s.books!=='object'||Array.isArray(s.books))throw Error('Livros fiscais não recuperáveis;originais preservados.');return s;}
|
||||
export function validateNormal(b:NormalBook){
|
||||
if(b?.profit!==undefined)validateNormalProfit(b.profit);
|
||||
if(!b||b.version!==1||['year','mappingReference','scopeReference'].some(k=>typeof b[k as 'year']!=='string')||!['','resident_general','other'].includes(b.scope)||!Array.isArray(b.rows)||b.history!==undefined&&!Array.isArray(b.history))throw Error('Preparação IRPC normal não recuperável.');
|
||||
for(const r of b.rows)if(!r||Object.keys(newNormalAdjustment('')).some(k=>typeof r[k as keyof NormalAdjustment]!=='string')||!['','noncontractual_fine','compensatory_interest','other'].includes(r.rule)||!['','yes','no'].includes(r.included))throw Error('Ajustamento IRPC não recuperável;original conservado.');
|
||||
if(new Set(b.rows.map(r=>r.id)).size!==b.rows.length)throw Error('Identificador de ajustamento duplicado.');
|
||||
for(const r of [b.result,...(b.history??[])])if(r&&(!r.model||r.model.version!==irpcNormalVersion||typeof r.key!=='string'||typeof r.createdAt!=='string'||!Array.isArray(r.model.rows)||!Array.isArray(r.model.pending)))throw Error('Histórico IRPC normal não recuperável.');
|
||||
for(const r of [b.result,...(b.history??[])])if(r&&(!r.model||![irpcNormalVersion,normalProfitVersion].includes(r.model.version)||typeof r.key!=='string'||typeof r.createdAt!=='string'||!Array.isArray(r.model.rows)||!Array.isArray(r.model.pending)))throw Error('Histórico IRPC normal não recuperável.');
|
||||
}
|
||||
export function normalBook(d:Record<string,unknown>):NormalBook{
|
||||
const m=store(d).irpcNormal;if(m===undefined)return newNormalBook();if(!m||typeof m!=='object'||Array.isArray(m))throw Error('Mapa IRPC normal não recuperável.');const b=m[acquisitionScope(d)]??newNormalBook();validateNormal(b);return b;
|
||||
}
|
||||
export function saveNormalBook(d:Record<string,unknown>,b:NormalBook){validateNormal(b);const s=store(d);if(s.irpcNormal!==undefined&&(!s.irpcNormal||typeof s.irpcNormal!=='object'||Array.isArray(s.irpcNormal)))throw Error('Mapa IRPC normal não recuperável.');return JSON.stringify({...s,irpcNormal:{...s.irpcNormal,[acquisitionScope(d)]:b}});}
|
||||
function key(d:Record<string,unknown>,b:NormalBook){return JSON.stringify([irpcNormalVersion,acquisitionScope(d),d.business_plan_snapshot??'',b.year,b.mappingReference,b.scope,b.scopeReference,b.rows]);}
|
||||
function key(d:Record<string,unknown>,b:NormalBook){return JSON.stringify([b.profit?normalProfitVersion:irpcNormalVersion,acquisitionScope(d),d.business_plan_snapshot??'',b.year,b.mappingReference,b.scope,b.scopeReference,b.rows,...(b.profit?[b.profit]:[])]);}
|
||||
export function normalCurrent(d:Record<string,unknown>,b:NormalBook){return !!b.result&&b.result.key===key(d,b);}
|
||||
const zero=()=>fraction(0n,1n);
|
||||
const cents=(s:string)=>{if(!/^-?\d+(?:\.\d{1,2})?$/.test(s))throw Error('Montante MZN inválido;máximo dois decimais.');return decimalFraction(s);};
|
||||
const cents=(s:string)=>{if(!/^-?\d+(?:\.\d{1,2})?$/.test(s))throw Error('Montante MZN inválido;máximo dois decimais.');const v=decimalFraction(s.startsWith('-')?s.slice(1):s);return s.startsWith('-')?fraction(-BigInt(v.numerator),BigInt(v.denominator)):v;};
|
||||
const integerCents=(v:FiscalFraction)=>BigInt(v.numerator)*100n/BigInt(v.denominator);
|
||||
export function calculateNormal(d:Record<string,unknown>,b:NormalBook):NormalModel{
|
||||
validateNormal(b);const pending:string[]=[],source=normalFinancialSource(d);
|
||||
@@ -58,11 +60,13 @@ export function calculateNormal(d:Record<string,unknown>,b:NormalBook):NormalMod
|
||||
const additions=addFractions(rows.flatMap(r=>r.adjustment?[r.adjustment]:[]));
|
||||
const accountingProfit=source?.currency==='MZN'?cents(source.accountingProfitBeforeTax):null;
|
||||
const contextReady=!!source&&source.currency==='MZN'&&source.periods===12&&b.year==='2026'&&!!b.mappingReference.trim()&&b.scope==='resident_general'&&!!b.scopeReference.trim();
|
||||
if(rows.length===0)pending.push('Sem ajustamentos documentados;ausência de registos não prova inexistência de correcções.');
|
||||
pending.push('L6 parcial:resultado ajustado apenas destes acréscimos;restantes custos/variações patrimoniais,amortizações,perdas,deduções e colecta por tratar. Lucro tributável,matéria colectável e imposto indeterminados.');
|
||||
return{version:irpcNormalVersion,year:b.year,currency:source?.currency??'',origin:'projected',accountingProfit,additions,partialAdjustedResult:contextReady&&rows.length>0&&rows.every(r=>r.status==='calculated')&&accountingProfit?addFractions([accountingProfit,additions]):null,taxableProfit:null,taxableBase:null,tax:null,rows,pending,source:irpcNormalSource};
|
||||
if(rows.length===0&&!b.profit)pending.push('Sem ajustamentos documentados;ausência de registos não prova inexistência de correcções.');
|
||||
if(!b.profit)pending.push('L6 parcial:resultado ajustado apenas destes acréscimos;restantes custos/variações patrimoniais,amortizações,perdas,deduções e colecta por tratar. Lucro tributável,matéria colectável e imposto indeterminados.');
|
||||
const profit=b.profit?closeNormalProfit(b.profit,source,rows,accountingProfit,additions,contextReady):undefined;
|
||||
if(profit)pending.push(...profit.pending,'Matéria colectável,perdas reportadas,deduções e imposto não apurados neste percurso.');
|
||||
return{...(profit?{profit}:{}),version:b.profit?normalProfitVersion:irpcNormalVersion,year:b.year,currency:source?.currency??'',origin:'projected',accountingProfit,additions,partialAdjustedResult:contextReady&&rows.length>0&&rows.every(r=>r.status==='calculated')&&accountingProfit?addFractions([accountingProfit,additions]):null,taxableProfit:profit?.taxableProfit??null,taxableBase:null,tax:null,rows,pending,source:irpcNormalSource};
|
||||
}
|
||||
export function recordNormal(d:Record<string,unknown>,b:NormalBook,now=new Date().toISOString()):NormalBook{return{...b,history:b.result?[...(b.history??[]),structuredClone(b.result)]:b.history??[],result:{key:key(d,b),createdAt:now,model:calculateNormal(d,b)}};}
|
||||
const money=(v:FiscalFraction|null)=>v?fractionPresentation(v).decimal+' MZN':'Indeterminado';
|
||||
export function normalReportRows(d:Record<string,unknown>):Array<[string,string,string,string]>{try{const b=normalBook(d),r=b.result;if(!r)return[];const m=r.model;return[['IRPC normal — L6','Ajustamentos ao resultado financeiro',`Exercício:${m.year};resultado contabilístico antes de imposto:${money(m.accountingProfit)};acréscimos demonstrados:${money(m.additions)};resultado parcial ajustado:${money(m.partialAdjustedResult)};lucro tributável/matéria colectável/imposto:Indeterminados. Projecção com pressupostos;${m.pending.join(' | ')}`,`${normalCurrent(d,b)?'Entradas correspondentes':'Histórico;origem ou entradas alteradas'};${r.createdAt};${m.source}`],...m.rows.map(r=>['IRPC normal — L6',r.document+'/'+r.line,`${({calculated:'Calculado neste recorte',indeterminate:'Indeterminado',outside_scope:'Fora deste recorte'})[r.status]};montante:${r.amount};acréscimo:${money(r.adjustment)};${r.reason}`,`Origem:${({revenue:'Receitas',cost_of_sales:'Custo das vendas',operating_expenses:'Despesas operacionais',personnel:'Pessoal',depreciation:'Depreciação contabilística',interest_expense:'Juros reconhecidos'}as Record<string,string>)[r.sourceCode]??'Por corresponder'};incluído:${r.included==='yes'?'Sim':r.included==='no'?'Não':'Por demonstrar'};suporte:${r.reference}`]as[string,string,string,string])];}catch(e){return[['IRPC normal — L6','Não recuperável',String(e),'Originais conservados.']];}}
|
||||
export function normalReportRows(d:Record<string,unknown>):Array<[string,string,string,string]>{try{const b=normalBook(d),r=b.result;if(!r)return[];const m=r.model;return[['IRPC normal — L6','Ajustamentos ao resultado financeiro',`Exercício:${m.year};resultado contabilístico antes de imposto:${money(m.accountingProfit)};acréscimos demonstrados:${money(m.additions)};resultado parcial ajustado:${money(m.partialAdjustedResult)};${m.profit?'lucro tributável simulado:'+money(m.taxableProfit)+';matéria colectável/imposto:Indeterminados.':'lucro tributável/matéria colectável/imposto:Indeterminados.'} Projecção com pressupostos;${m.pending.join(' | ')}`,`${normalCurrent(d,b)?'Entradas correspondentes':'Histórico;origem ou entradas alteradas'};${r.createdAt};${m.source}`],...m.rows.map(r=>['IRPC normal — L6',r.document+'/'+r.line,`${({calculated:'Calculado neste recorte',indeterminate:'Indeterminado',outside_scope:'Fora deste recorte'})[r.status]};montante:${r.amount};acréscimo:${money(r.adjustment)};${r.reason}`,`Origem:${({revenue:'Receitas',cost_of_sales:'Custo das vendas',operating_expenses:'Despesas operacionais',personnel:'Pessoal',depreciation:'Depreciação contabilística',interest_expense:'Juros reconhecidos'}as Record<string,string>)[r.sourceCode]??'Por corresponder'};incluído:${r.included==='yes'?'Sim':r.included==='no'?'Não':'Por demonstrar'};suporte:${r.reference}`]as[string,string,string,string]),...(m.profit?[...m.profit.lines.map(l=>['IRPC normal — L6','Conferência: '+l.label,`Estado:${l.state==='known'?'Valor conhecido':l.state==='absent'?'Ausência declarada':'Informação em falta'};origem:${l.amount};acréscimos:${money(l.addedBack)};remanescente:${money(l.remainder)};${l.reason}`,l.reference]as[string,string,string,string]),...m.profit.topics.map(t=>['IRPC normal — L6',t.label,`Estado:${t.state==='known'?'Valor conhecido':t.state==='absent'?'Ausência declarada':'Informação em falta'};montante:${money(t.value)};${t.reason}`,t.reference]as[string,string,string,string]),['IRPC normal — L6','Âmbito da conferência',m.profit.input.completenessReference,m.profit.source]as[string,string,string,string]]:[])];}catch(e){return[['IRPC normal — L6','Não recuperável',String(e),'Originais conservados.']];}}
|
||||
export function normalHtml(d:Record<string,unknown>){const b=normalBook(d);if(!normalCurrent(d,b))throw Error('Calcule e guarde as entradas actuais antes de exportar.');const esc=(s:string)=>s.replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]!));return'<!doctype html><html lang="pt"><meta charset="utf-8"><title>IRPC normal — ajustamentos</title><h1>L6 — mapa parcial de ajustamentos</h1><p>Projecção pelos pressupostos do plano. Não é demonstração contabilística,liquidação ou Modelo20/22.</p>'+normalReportRows(d).map(r=>'<section><h2>'+esc(r[1])+'</h2><p>'+esc(r[2])+'</p><small>'+esc(r[3])+'</small></section>').join('')+'</html>';}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<?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 IrpcNormalProfitDraftTest extends TestCase
|
||||
{
|
||||
use DatabaseTransactions;
|
||||
|
||||
public function test_scoped_taxable_profit_and_missing_information_recover_with_all_prior_books(): void
|
||||
{
|
||||
$target=DB::selectOne('select current_database() as name,inet_server_port() as port');
|
||||
$this->assertSame('simulator_fiscal_delta_20261021_test',$target->name);
|
||||
$this->assertSame(55441,(int)$target->port);
|
||||
config(['investor-path.enabled'=>true,'suitecrm.enabled'=>false]);
|
||||
$this->actingAs(User::factory()->create(['email_verified_at'=>now()]));
|
||||
$id=null;$etag=null;
|
||||
foreach(['calculated','missing'] as $case){
|
||||
$draft=json_decode(file_get_contents(base_path('outputs/irpc-l6-profit/'.$case.'.json')),true,flags:JSON_THROW_ON_ERROR);
|
||||
$personal=['person_type'=>$draft['person_type']];unset($draft['person_type']);
|
||||
if($id===null)$id=$this->postJson('/api/v1/simulation-drafts',['personal_snapshot'=>$personal,'economic_snapshot'=>$draft],['Idempotency-Key'=>(string)Str::uuid()])->assertCreated()->json('data.id');
|
||||
else $this->putJson('/api/v1/simulation-drafts/'.$id,['personal_snapshot'=>$personal,'economic_snapshot'=>$draft],['If-Match'=>$etag])->assertOk();
|
||||
$response=$this->getJson('/api/v1/simulation-drafts/'.$id)->assertOk();$etag=$response->headers->get('ETag');$recovered=$response->json('data.economic_snapshot');
|
||||
$this->assertSame($draft['modelo_a_snapshot'],$recovered['modelo_a_snapshot']);
|
||||
$this->assertSame($draft['business_plan_snapshot'],$recovered['business_plan_snapshot']);
|
||||
$store=json_decode($recovered['modelo_a_snapshot'],true,flags:JSON_THROW_ON_ERROR);
|
||||
$book=$store['irpcNormal'][$draft['context_opportunity']];$m=$book['result']['model'];
|
||||
$this->assertSame('6480',$m['accountingProfit']['numerator']);
|
||||
$this->assertSame('220',$m['additions']['numerator']);
|
||||
$this->assertNull($m['taxableBase']);$this->assertNull($m['tax']);
|
||||
$this->assertSame('IRPC-normal-adjustments/2',$m['version']);
|
||||
$this->assertSame('projected',$m['origin']);
|
||||
$this->assertSame('absent',$m['profit']['input']['topics']['positivePatrimonial']['state']);
|
||||
$this->assertCount(6,$m['profit']['lines']);
|
||||
$this->assertSame('known',$m['profit']['input']['topics']['incomeCorrections']['state']);
|
||||
$this->assertSame('0',$m['profit']['input']['topics']['incomeCorrections']['amount']);
|
||||
$this->assertArrayHasKey('irpcWithholdings',$store);
|
||||
$this->assertSame('IRPC-normal-adjustments/1',$book['history'][0]['model']['version']);
|
||||
if($case==='calculated'){$this->assertSame('6700',$m['taxableProfit']['numerator']);$this->assertCount(0,$m['profit']['pending']);}
|
||||
else{$this->assertNull($m['taxableProfit']);$this->assertSame('',$m['profit']['input']['topics']['negativePatrimonial']['state']);}
|
||||
file_put_contents(base_path('outputs/irpc-l6-profit/recovered-'.$case.'.json'),json_encode($recovered,JSON_THROW_ON_ERROR|JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,18 @@
|
||||
import test from 'node:test';import assert from 'node:assert/strict';import fs from 'node:fs';
|
||||
import {normalBook,normalFinancialSource,recordNormal,calculateNormal,normalCurrent,saveNormalBook,normalHtml,normalReportRows}from'../../resources/js/lib/irpc-normal.ts';
|
||||
import {newNormalProfitInput,profitTopics}from'../../resources/js/lib/irpc-normal-profit.ts';
|
||||
import {restoreBusinessPlanSnapshot,serializeBusinessPlanSnapshot}from'../../resources/js/lib/business-financial-plan.ts';
|
||||
import {createProjectDossierReport}from'../../resources/js/lib/project-dossier-report.ts';
|
||||
import {irpcBook,saveIrpcBook}from'../../resources/js/lib/irpc-withholdings.ts';
|
||||
const seed=()=>JSON.parse(fs.readFileSync(new URL('./fixtures/irpc-normal-preserved.json',import.meta.url),'utf8'));
|
||||
function example(){const d=seed(),b=normalBook(d),profit=newNormalProfitInput();b.rows[1].amount='120';b.rows[1].reference='QA todos120MZN de juros reconhecidos são compensatórios documentados;nenhuma taxa L5 calculada';profit.completenessReference='QA plano cobre sujeito residente/exercício2026 completo,sem outros estabelecimentos,receitas,custos ou operações omitidos';profit.lines=normalFinancialSource(d).accountingLines.map(l=>({code:l.code,state:l.code==='depreciation'?'absent':'known',reference:'QA qualificação ordinária/exercício/reconciliação de '+l.origin+';restante custo indispensável sem outras exclusões'}));for(const[k]of profitTopics)profit.topics[k]={state:'absent',amount:'',reference:'QA ausência verificada de '+k};profit.topics.incomeCorrections={state:'known',amount:'0',reference:'QA revisão de correcções ao rendimento:valor conhecido zero'};b.profit=profit;return{d,b};}
|
||||
test('documented financial result plus supported addbacks reaches scoped simulated taxable profit without tax/base',()=>{const{d,b}=example(),m=calculateNormal(d,b);assert.equal(m.accountingProfit.numerator,'6480');assert.equal(m.additions.numerator,'220');assert.equal(m.taxableProfit.numerator,'6700');assert.equal(m.taxableBase,null);assert.equal(m.tax,null);assert.equal(m.profit.pending.length,0);assert.equal(m.profit.lines.find(l=>l.code==='interest_expense').remainder.numerator,'0');assert.equal(m.profit.lines.find(l=>l.code==='operating_expenses').remainder.numerator,'1100');});
|
||||
test('known zero,declared absence,and missing information keep distinct saved evidence and effects',()=>{const{d,b}=example();b.profit.topics.incomeCorrections={state:'known',amount:'0',reference:'QA total revisto de correcções ao rendimento é zero'};let m=calculateNormal(d,b);assert.equal(m.taxableProfit.numerator,'6700');assert.equal(m.profit.topics.find(t=>t.key==='incomeCorrections').state,'known');assert.equal(m.profit.topics.find(t=>t.key==='positivePatrimonial').state,'absent');b.profit.topics.negativePatrimonial={state:'',amount:'',reference:''};m=calculateNormal(d,b);assert.equal(m.taxableProfit,null);assert.equal(m.additions.numerator,'220');assert.equal(m.profit.topics.find(t=>t.key==='negativePatrimonial').value,null);assert.match(m.profit.pending.join(' '),/sem zero presumido/);});
|
||||
test('absence cannot override known nonzero financial values or silently discard an entered amount',()=>{const{d,b}=example();b.profit.lines.find(l=>l.code==='operating_expenses').state='absent';assert.equal(calculateNormal(d,b).taxableProfit,null);b.profit.lines.find(l=>l.code==='operating_expenses').state='known';b.profit.topics.positivePatrimonial.amount='100';assert.equal(calculateNormal(d,b).taxableProfit,null);});
|
||||
test('known unimplemented variation,depreciation and remaining financial interest stay visible without presumed treatment',()=>{for(const patch of ['variation','depreciation','interest']){const{d,b}=example();if(patch==='variation')b.profit.topics.positivePatrimonial={state:'known',amount:'500',reference:'QA variação500,regra específica por corresponder'};if(patch==='depreciation'){const p=restoreBusinessPlanSnapshot(d.business_plan_snapshot);p.assetDepreciation='120';d.business_plan_snapshot=serializeBusinessPlanSnapshot(p);b.profit.lines.find(l=>l.code==='depreciation').state='known';}if(patch==='interest')b.rows[1].amount='20';const m=calculateNormal(d,b);assert.equal(m.taxableProfit,null);assert.ok(m.profit.pending.length>0);if(patch==='variation')assert.equal(m.profit.topics[0].value.numerator,'500');}});
|
||||
test('complete line classifications and subject-year completeness are required,not a bare project subtotal',()=>{for(const patch of ['line','reference','completeness','scope','year']){const{d,b}=example();if(patch==='line')b.profit.lines.pop();if(patch==='reference')b.profit.lines[0].reference='';if(patch==='completeness')b.profit.completenessReference='';if(patch==='scope')b.scope='other';if(patch==='year')b.year='2025';assert.equal(calculateNormal(d,b).taxableProfit,null);}});
|
||||
test('declared no addbacks can close only after full documented review,and fiscal losses are not clamped to zero',()=>{const{d,b}=example();let p=restoreBusinessPlanSnapshot(d.business_plan_snapshot);p.monthlyInterestExpense='0';d.business_plan_snapshot=serializeBusinessPlanSnapshot(p);b.rows=[];b.profit.lines.find(l=>l.code==='interest_expense').state='absent';assert.equal(calculateNormal(d,b).taxableProfit.numerator,'6600');p.products[0].unitPrice='1';d.business_plan_snapshot=serializeBusinessPlanSnapshot(p);assert.equal(calculateNormal(d,b).taxableProfit.numerator,'-5280');});
|
||||
test('L5 uncertainty does not block independent known expense classification or annual profit',()=>{const{d,b}=example(),s=JSON.parse(d.modelo_a_snapshot);for(const v of Object.values(s.irpcWithholdings??{})){if(v.mrfta){v.mrfta.codes=[];v.mrfta.interest.mode='';v.mrfta.interest.calculation=undefined;}}d.modelo_a_snapshot=JSON.stringify(s);assert.equal(calculateNormal(d,b).taxableProfit.numerator,'6700');});
|
||||
test('old result remains current and unchanged;new closure history and stale-input guards survive roundtrip and dossier',()=>{const legacy=seed(),old=structuredClone(normalBook(legacy));assert.equal(normalCurrent(legacy,old),true);assert.equal(old.result.model.version,'IRPC-normal-adjustments/1');const{d,b}=example();const saved=recordNormal(d,b,'2026-10-25T22:00:00Z');assert.deepEqual(saved.history.at(-1),old.result);const before=JSON.parse(d.modelo_a_snapshot);d.modelo_a_snapshot=saveNormalBook(d,saved);assert.deepEqual(JSON.parse(d.modelo_a_snapshot).irpcWithholdings,before.irpcWithholdings);const back=JSON.parse(JSON.stringify(d));assert.equal(normalCurrent(back,normalBook(back)),true);assert.match(normalHtml(back),/lucro tributável simulado:6700/);assert.match(JSON.stringify(createProjectDossierReport(back,{selectedSections:new Set(['finance'])})),/6700/);assert.match(JSON.stringify(normalReportRows(back)),/Ausência declarada/);const l5=irpcBook(back);back.modelo_a_snapshot=saveIrpcBook(back,l5);assert.deepEqual(normalBook(back),saved);normalBook(back).profit.lines[0].reference='';const changed=normalBook(back);changed.profit.lines[0].reference='';back.modelo_a_snapshot=saveNormalBook(back,changed);assert.equal(normalCurrent(back,normalBook(back)),false);assert.throws(()=>normalHtml(back));
|
||||
fs.mkdirSync('outputs/irpc-l6-profit',{recursive:true});fs.writeFileSync('outputs/irpc-l6-profit/calculated.json',JSON.stringify(d));fs.writeFileSync('outputs/irpc-l6-profit/preparation.html',normalHtml(d));const missing=normalBook(d);missing.profit.topics.negativePatrimonial={state:'',amount:'',reference:''};const md=structuredClone(d);md.modelo_a_snapshot=saveNormalBook(md,recordNormal(md,missing));fs.writeFileSync('outputs/irpc-l6-profit/missing.json',JSON.stringify(md));});
|
||||
test('malformed added evidence and unsupported stored versions fail without resetting the book',()=>{const{d,b}=example();b.profit.topics.positivePatrimonial.amount=100;assert.throws(()=>saveNormalBook(d,b));b.profit.topics.positivePatrimonial.amount='';b.profit.lines.push({...b.profit.lines[0]});assert.throws(()=>saveNormalBook(d,b));});
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
$root=dirname(__DIR__);
|
||||
$operation=$argv[1]??'';
|
||||
if(!in_array($operation,['routes','test'],true)||basename($root)!=='portal-investidor-irpc-l6-profit-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-l6-profit']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/IrpcNormalProfitDraftTest.php'];$argc=count($argv);$_SERVER['argv']=$argv;$_SERVER['argc']=$argc;require $root.'/vendor/phpunit/phpunit/phpunit';
|
||||
Reference in New Issue
Block a user