From c22003a3daaf0116a00adf99e16a12a92e41cdc0 Mon Sep 17 00:00:00 2001
From: nala
Date: Tue, 20 Oct 2026 22:00:48 +0200
Subject: [PATCH] fix: recover simulator cashflow and verify QA continuity and
report assumptions
---
.../StoreSimulationFormDraftRequest.php | 3 +-
.../SimulationTaxParameterResolver.php | 16 +-
app/Services/SimulationTaxRuleExpression.php | 28 +--
bootstrap/app.php | 25 ++-
...-review-continuation-20261020-receipt.json | 162 ++++++++++++++
.../unification/traceability-and-manual.md | 29 +++
.../auditoria-portal/backlog-priorizado.csv | 16 +-
docs/investmoz/v2/auditoria-portal/backlog.md | 2 +
.../v2/auditoria-portal/matriz-requisitos.csv | 16 +-
.../simulation-cashflow-planner.tsx | 70 ++++---
.../components/simulation-financial-tools.tsx | 6 +
resources/js/components/simulation-form.tsx | 77 ++++---
resources/js/lib/financial-calculators.ts | 4 +
resources/js/lib/monthly-cashflow.ts | 68 ++++++
resources/js/lib/project-dossier-report.ts | 7 +-
resources/js/lib/project-dossier.ts | 70 ++++++-
.../SimulatorCashflowContinuityTest.php | 34 +++
tests/Feature/SimulatorReviewMessagesTest.php | 16 ++
.../SimulationTaxParameterResolverTest.php | 22 ++
.../simulator-independent-review.test.mjs | 197 ++++++++++++++++++
20 files changed, 773 insertions(+), 95 deletions(-)
create mode 100644 docs/investmoz/simulations/integrated-review-continuation-20261020-receipt.json
create mode 100644 tests/Feature/SimulatorCashflowContinuityTest.php
create mode 100644 tests/Feature/SimulatorReviewMessagesTest.php
create mode 100644 tests/frontend/simulator-independent-review.test.mjs
diff --git a/app/Http/Requests/StoreSimulationFormDraftRequest.php b/app/Http/Requests/StoreSimulationFormDraftRequest.php
index d1a8d3b8..74cf0d90 100644
--- a/app/Http/Requests/StoreSimulationFormDraftRequest.php
+++ b/app/Http/Requests/StoreSimulationFormDraftRequest.php
@@ -37,7 +37,7 @@ class StoreSimulationFormDraftRequest extends FormRequest
'contact_province', 'contact_district', 'preferred_channel',
];
$economic = [
- 'journey_type', 'detail_level', 'business_plan_snapshot', 'financial_tools_snapshot', 'project_team_snapshot', 'project_execution_snapshot', 'purpose', 'manifestation_type', 'preferred_language', 'project_title', 'project_summary', 'project_problem', 'target_customers', 'value_proposition', 'market_assumption', 'market_source', 'project_milestones', 'selection_labels', 'project_type',
+ 'journey_type', 'detail_level', 'business_plan_snapshot', 'financial_tools_snapshot', 'cashflow_snapshot', 'project_team_snapshot', 'project_execution_snapshot', 'purpose', 'manifestation_type', 'preferred_language', 'project_title', 'project_summary', 'project_problem', 'target_customers', 'value_proposition', 'market_assumption', 'market_source', 'project_milestones', 'selection_labels', 'project_type',
'competitor_assumption', 'sales_channel_assumption', 'marketing_approach', 'location_rationale', 'project_risk_responses_snapshot', 'project_questionnaire_version',
'customer_validation_status', 'cost_estimate_status', 'location_assessment_status', 'funding_status', 'receivables_timing_status',
'project_type_other', 'sector_id', 'activity_id', 'products_services', 'maturity_stage', 'expected_start_date',
@@ -71,6 +71,7 @@ class StoreSimulationFormDraftRequest extends FormRequest
'economic_snapshot.detail_level' => ['nullable', 'in:simple,advanced'],
'economic_snapshot.business_plan_snapshot' => ['nullable', 'string', 'max:60000'],
'economic_snapshot.financial_tools_snapshot' => ['nullable', 'string', 'max:30000'],
+ 'economic_snapshot.cashflow_snapshot' => ['nullable', 'string', 'max:20000'],
'economic_snapshot.project_team_snapshot' => ['nullable', 'string', 'max:30000'],
'economic_snapshot.project_execution_snapshot' => ['nullable', 'string', 'max:30000'],
'economic_snapshot.project_risk_responses_snapshot' => ['nullable', 'string', 'max:30000'],
diff --git a/app/Services/SimulationTaxParameterResolver.php b/app/Services/SimulationTaxParameterResolver.php
index 8d758d5e..a8881434 100644
--- a/app/Services/SimulationTaxParameterResolver.php
+++ b/app/Services/SimulationTaxParameterResolver.php
@@ -16,32 +16,32 @@ final class SimulationTaxParameterResolver
public function validateParameters(array $parameters): void
{
if ($parameters === [] || count($parameters) > 100) {
- throw new InvalidArgumentException('Provide between 1 and 100 versioned parameters.');
+ throw new InvalidArgumentException('Indique entre 1 e 100 parâmetros versionados.');
}
foreach ($parameters as $code => $parameter) {
if (! is_string($code) || ! preg_match('/^[A-Z][A-Z0-9._-]{1,63}$/', $code) || ! is_array($parameter)
|| array_diff(array_keys($parameter), ['value', 'unit', 'conditions']) !== []
|| array_diff(['value', 'unit', 'conditions'], array_keys($parameter)) !== []) {
- throw new InvalidArgumentException('Each parameter requires only value, unit, and conditions.');
+ throw new InvalidArgumentException('Cada parâmetro deve conter apenas valor, unidade e condições.');
}
if ((! is_string($parameter['value']) && ! is_int($parameter['value'])) || (string) $parameter['value'] === ''
|| ! is_string($parameter['unit']) || ! preg_match('/^[a-z][a-z0-9._-]{0,31}$/', $parameter['unit'])
|| ! is_array($parameter['conditions'])) {
- throw new InvalidArgumentException('Invalid parameter value, unit, or conditions.');
+ throw new InvalidArgumentException('Reveja o valor, a unidade e as condições do parâmetro.');
}
$isNumericValue = preg_match('/^-?\d+(?:\.\d+)?$/', (string) $parameter['value']) === 1;
if (in_array($parameter['unit'], ['percent', 'mzn', 'amount', 'days', 'count', 'mzn_per_unit', 'per_unit'], true) && ! $isNumericValue) {
- throw new InvalidArgumentException('Numeric parameter units require a plain decimal value.');
+ throw new InvalidArgumentException('Esta unidade requer um valor numérico decimal.');
}
if ($isNumericValue) {
$value = BigRational::of((string) $parameter['value']);
if ($value->isNegative() || ($parameter['unit'] === 'percent' && $value->compareTo(100) > 0)) {
- throw new InvalidArgumentException('Numeric parameters cannot be negative; percentages must be between 0 and 100.');
+ throw new InvalidArgumentException('Os valores não podem ser negativos; as percentagens devem estar entre 0 e 100.');
}
}
foreach ($parameter['conditions'] as $condition => $expected) {
if (! is_string($condition) || ! is_string($expected)) {
- throw new InvalidArgumentException('Conditions must be a flat map of string assumptions.');
+ throw new InvalidArgumentException('Indique as condições numa lista simples de nomes e valores em texto.');
}
}
}
@@ -59,7 +59,7 @@ final class SimulationTaxParameterResolver
$start = $this->date($periodStart);
$end = $this->date($periodEnd);
if ($end < $start || $start->diff($end)->days > 365) {
- throw new InvalidArgumentException('Invalid effective period.');
+ throw new InvalidArgumentException('Reveja o período de aplicação indicado.');
}
$rows = DB::table('simulation_tax_rule_versions as rule')
@@ -315,7 +315,7 @@ final class SimulationTaxParameterResolver
{
$parsed = DateTimeImmutable::createFromFormat('!Y-m-d', $date);
if ($parsed === false || $parsed->format('Y-m-d') !== $date) {
- throw new InvalidArgumentException('Effective date must use YYYY-MM-DD.');
+ throw new InvalidArgumentException('Indique uma data de aplicação válida no formato ano-mês-dia.');
}
return $parsed;
diff --git a/app/Services/SimulationTaxRuleExpression.php b/app/Services/SimulationTaxRuleExpression.php
index 9821ac6b..ceb1fbec 100644
--- a/app/Services/SimulationTaxRuleExpression.php
+++ b/app/Services/SimulationTaxRuleExpression.php
@@ -95,16 +95,16 @@ final class SimulationTaxRuleExpression
private function topologicalOrder(array $definition): array
{
if (($definition['language'] ?? null) !== self::LANGUAGE || ! is_array($definition['nodes'] ?? null) || ! is_string($definition['result'] ?? null)) {
- throw new InvalidArgumentException('Unsupported expression definition.');
+ throw new InvalidArgumentException('O formato da expressão não é suportado.');
}
$nodes = $definition['nodes'];
if ($nodes === [] || count($nodes) > 100) {
- throw new InvalidArgumentException('Expression must contain between 1 and 100 nodes.');
+ throw new InvalidArgumentException('A expressão deve conter entre 1 e 100 elementos.');
}
foreach ($nodes as $id => $node) {
if (! is_string($id) || ! preg_match('/^[a-z][a-z0-9_]{0,47}$/', $id) || ! is_array($node) || ! is_string($node['op'] ?? null)) {
- throw new InvalidArgumentException('Invalid expression node.');
+ throw new InvalidArgumentException('Reveja o elemento indicado na expressão.');
}
$op = $node['op'];
$allowedFields = match ($op) {
@@ -112,40 +112,40 @@ final class SimulationTaxRuleExpression
'literal' => ['op', 'value'],
'add', 'subtract', 'multiply', 'divide', 'min', 'max' => ['op', 'args'],
'round' => ['op', 'args', 'scale', 'mode'],
- default => throw new InvalidArgumentException('Expression operation is not allowed.'),
+ default => throw new InvalidArgumentException('Esta operação não é suportada na expressão.'),
};
if (array_diff(array_keys($node), $allowedFields) !== [] || array_diff($allowedFields, array_keys($node)) !== []) {
- throw new InvalidArgumentException('Expression node has missing or unsupported fields.');
+ throw new InvalidArgumentException('Há campos em falta ou não suportados num elemento da expressão.');
}
if (in_array($op, ['input', 'parameter'], true) && (! is_string($node['key']) || ! preg_match('/^[a-z][a-z0-9_.-]{0,79}$/', $node['key']))) {
- throw new InvalidArgumentException('Invalid input or parameter key.');
+ throw new InvalidArgumentException('Reveja o identificador da entrada ou do parâmetro.');
}
if ($op === 'literal') {
$this->number($node['value']);
}
if (isset($node['args'])) {
if (! is_array($node['args'])) {
- throw new InvalidArgumentException('Invalid expression arguments.');
+ throw new InvalidArgumentException('Reveja os argumentos da expressão.');
}
$count = count($node['args']);
$validCount = $op === 'round' ? $count === 1 : ($op === 'subtract' || $op === 'divide' ? $count === 2 : $count >= 2 && $count <= 20);
if (! $validCount || count(array_filter($node['args'], 'is_string')) !== $count) {
- throw new InvalidArgumentException('Invalid expression arguments.');
+ throw new InvalidArgumentException('Reveja os argumentos da expressão.');
}
}
if ($op === 'round' && (! is_int($node['scale']) || $node['scale'] < 0 || $node['scale'] > 12 || ! isset(self::ROUNDING[$node['mode']]))) {
- throw new InvalidArgumentException('Invalid explicit rounding configuration.');
+ throw new InvalidArgumentException('Reveja a configuração de arredondamento.');
}
}
if (! array_key_exists($definition['result'], $nodes)) {
- throw new InvalidArgumentException('Expression result references an unknown node.');
+ throw new InvalidArgumentException('O resultado da expressão refere um elemento inexistente.');
}
$state = [];
$order = [];
$visit = function (string $id) use (&$visit, &$state, &$order, $nodes): void {
if (($state[$id] ?? 0) === 1) {
- throw new InvalidArgumentException('Expression dependency graph contains a cycle.');
+ throw new InvalidArgumentException('A expressão contém uma dependência circular.');
}
if (($state[$id] ?? 0) === 2) {
return;
@@ -153,7 +153,7 @@ final class SimulationTaxRuleExpression
$state[$id] = 1;
foreach ($nodes[$id]['args'] ?? [] as $dependency) {
if (! is_string($dependency) || ! array_key_exists($dependency, $nodes)) {
- throw new InvalidArgumentException('Expression references an unknown node.');
+ throw new InvalidArgumentException('A expressão refere um elemento inexistente.');
}
$visit($dependency);
}
@@ -191,13 +191,13 @@ final class SimulationTaxRuleExpression
if ($op === 'min' || $op === 'max') {
return array_reduce(array_slice($args, 1), static fn (BigRational $carry, BigRational $value): BigRational => ($op === 'min' ? $carry->compareTo($value) <= 0 : $carry->compareTo($value) >= 0) ? $carry : $value, $args[0]);
}
- throw new InvalidArgumentException('Expression operation is not allowed.');
+ throw new InvalidArgumentException('Esta operação não é suportada na expressão.');
}
private function number(mixed $value): BigRational
{
if ((! is_string($value) && ! is_int($value)) || ! preg_match('/^-?\d+(?:\.\d+)?$/', (string) $value)) {
- throw new InvalidArgumentException('Numbers must be plain decimal strings or integers.');
+ throw new InvalidArgumentException('Introduza valores numéricos decimais ou inteiros.');
}
return BigRational::of((string) $value);
diff --git a/bootstrap/app.php b/bootstrap/app.php
index be108a69..f7f6d9a3 100644
--- a/bootstrap/app.php
+++ b/bootstrap/app.php
@@ -1,6 +1,7 @@
encryptCookies(except: ['appearance', 'sidebar_state', EnsureSimulationRecoveryCookie::NAME]);
$middleware->web(append: [
- \App\Http\Middleware\EnsureHomologationAccess::class,
+ EnsureHomologationAccess::class,
PortalProfileSession::class,
ProtectPrivilegedIdentity::class,
AuditProfileDecision::class,
@@ -68,6 +69,28 @@ return Application::configure(basePath: dirname(__DIR__))
fn (Request $request) => $request->is('api/*') || $request->expectsJson(),
);
$exceptions->respond(function (Response $response, Throwable $exception, Request $request): Response {
+ if (($request->is('api/*') || $request->expectsJson()) && $response->getStatusCode() >= 400) {
+ $body = json_decode((string) $response->getContent(), true);
+ $genericMessages = [
+ 'Not Found' => 'O registo não foi encontrado ou não está acessível nesta conta.',
+ 'Not found.' => 'O registo não foi encontrado ou não está acessível nesta conta.',
+ 'Forbidden' => 'Esta conta não tem acesso a esta operação.',
+ 'Forbidden.' => 'Esta conta não tem acesso a esta operação.',
+ 'Unauthenticated.' => 'Entre na sua conta para continuar esta operação.',
+ 'CSRF token mismatch.' => 'A sessão expirou. Entre novamente antes de repetir a operação.',
+ 'Too Many Attempts.' => 'Foram feitas várias tentativas. Aguarde um pouco e tente novamente.',
+ 'Server Error' => 'Não foi possível concluir a operação. Tente novamente.',
+ ];
+ if (is_array($body) && is_string($body['message'] ?? null) && isset($genericMessages[$body['message']])) {
+ $body['message'] = $genericMessages[$body['message']];
+ $response->setContent(json_encode($body, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR));
+ } elseif (is_array($body) && is_string($body['message'] ?? null)
+ && $response->getStatusCode() === 404 && str_starts_with($body['message'], 'The route ')
+ && str_ends_with($body['message'], ' could not be found.')) {
+ $body['message'] = $genericMessages['Not Found'];
+ $response->setContent(json_encode($body, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR));
+ }
+ }
if ($request->is('api/*')) {
$response->headers->set(
'X-Correlation-ID',
diff --git a/docs/investmoz/simulations/integrated-review-continuation-20261020-receipt.json b/docs/investmoz/simulations/integrated-review-continuation-20261020-receipt.json
new file mode 100644
index 00000000..54ebb083
--- /dev/null
+++ b/docs/investmoz/simulations/integrated-review-continuation-20261020-receipt.json
@@ -0,0 +1,162 @@
+{
+ "base": "a690c665fb4e",
+ "branch": "codex/simulator-integrated-review",
+ "scope": "QA only; no main merge; no remote execution; no migration; no real tax activation",
+ "source_files": [
+ {
+ "path": "app/Http/Requests/StoreSimulationFormDraftRequest.php",
+ "sha256": "b98bb46d7aab33b1e9bbc449b397dceea9aef9a07825a336f0dd5133828776b0"
+ },
+ {
+ "path": "app/Services/SimulationTaxParameterResolver.php",
+ "sha256": "62edefeb64274b7c0b516b2cebb66a96f94f498412bd0ce2f100d86bfe290e62"
+ },
+ {
+ "path": "app/Services/SimulationTaxRuleExpression.php",
+ "sha256": "a976c0eb43ebb706275ca17efc9354a8d84f2dbe698375211704b158df02f7e1"
+ },
+ {
+ "path": "bootstrap/app.php",
+ "sha256": "68e0a909a9b1c4f09cdb857707e93515672cdc7bcc87c131d6124acd8ca4ff7f"
+ },
+ {
+ "path": "docs/investmoz/unification/traceability-and-manual.md",
+ "sha256": "241bb07e89db3358c77b8984727ed1f196616a1d42560c38685b98ef0ae0c581"
+ },
+ {
+ "path": "docs/investmoz/v2/auditoria-portal/backlog-priorizado.csv",
+ "sha256": "ab72dfe5dc85569f24e1b4b0767610936c33fbd1300f4e041037eb41cf0695f0"
+ },
+ {
+ "path": "docs/investmoz/v2/auditoria-portal/backlog.md",
+ "sha256": "1d37e153719b66ed90387107151753db31256d794220eb8570aa0a3bd6f44b66"
+ },
+ {
+ "path": "docs/investmoz/v2/auditoria-portal/matriz-requisitos.csv",
+ "sha256": "b95bda4d791e666f0bc67203931c20d2dcd083fb76ea82c6760ff0f554bba815"
+ },
+ {
+ "path": "resources/js/components/simulation-cashflow-planner.tsx",
+ "sha256": "b11589c3b06129685b3105aa66992fda30b2ab699c502fdc8e1a4af320a82a03"
+ },
+ {
+ "path": "resources/js/components/simulation-financial-tools.tsx",
+ "sha256": "1275b72072d3f2b66822dbe6c1f5dd47e1a642d51460254a8871500035fa3b72"
+ },
+ {
+ "path": "resources/js/components/simulation-form.tsx",
+ "sha256": "4de2bb2fd1ccb9fb44bdec4e59820815f8a5eb0c6c326532c0e10bef087add45"
+ },
+ {
+ "path": "resources/js/lib/financial-calculators.ts",
+ "sha256": "f551ce4a818484459535d80b968ae77ee17d6baf4ae1e4f018a23e11ccb18fca"
+ },
+ {
+ "path": "resources/js/lib/monthly-cashflow.ts",
+ "sha256": "9206b7e07d5078f979d16a21c1ed22c03cce83407bf376e146cb9b7ef959762d"
+ },
+ {
+ "path": "resources/js/lib/project-dossier-report.ts",
+ "sha256": "95d491b481bccd3ac0d2169619528b40b49e4a115a03807305b69c06a95e2461"
+ },
+ {
+ "path": "resources/js/lib/project-dossier.ts",
+ "sha256": "dfd2e5e28f2e2f0d1d185f111ecf9e43984c9b5952c1a39a6d483e13ad1a5758"
+ },
+ {
+ "path": "tests/Feature/SimulatorCashflowContinuityTest.php",
+ "sha256": "11a3423c8ef970b7f5522ae07bf1538055496d88d9559fd7e687024dade8848a"
+ },
+ {
+ "path": "tests/Feature/SimulatorReviewMessagesTest.php",
+ "sha256": "571d8df887ad78335d93caffc83bc21128cf4cb96ccd0038b14a5e4d30ef9797"
+ },
+ {
+ "path": "tests/Unit/SimulationTaxParameterResolverTest.php",
+ "sha256": "c0a14ce0f5e92a7a2159266e4fff6bfe54edbbcdfd324174ff7103745cc2fe9f"
+ },
+ {
+ "path": "tests/frontend/simulator-independent-review.test.mjs",
+ "sha256": "fcfaa6659d889a72eeb8be394e039d7a0b7b458a980e503a86d414a170c1828a"
+ }
+ ],
+ "build_manifest_sha256": "2c9826427585cbf41123507611ad1f002090ceb888c2271bdb14005427181416",
+ "evidence": [
+ {
+ "path": "outputs/simulator-continuation-20261020/fiscal-corpus-inventory.json",
+ "sha256": "e51825792b5007f6bd285d066ffe683c9d241c0965a6fae74a843bbe3d8f4317"
+ },
+ {
+ "path": "outputs/simulator-continuation-20261020/local-rule-visibility.json",
+ "sha256": "99ef0aab6f30e2491692eeac123d9a9e1a4eeae08ee11bd647e832bf6db571db"
+ },
+ {
+ "path": "outputs/simulator-continuation-20261020/history-preservation-proof.json",
+ "sha256": "7e591c614f43deefc9e44cc1abe7d8a7decaaa0e8ea17c13e9893024b80bbe98"
+ },
+ {
+ "path": "outputs/simulator-continuation-20261020/interface-comparison.json",
+ "sha256": "44d54f49973d0fd20b28533a561fab03a32ccb43e0410ba54b0c017a62d96e63"
+ },
+ {
+ "path": "outputs/simulator-continuation-20261020/interface-report.docx",
+ "sha256": "de0dc3af6936020199dda963bc2aaa8d462fb560e419dcc5a86f369bf18730b7"
+ },
+ {
+ "path": "outputs/simulator-continuation-20261020/interface-financial.csv",
+ "sha256": "552817d6b9fa7e84f457f490e275f374cdd0254568e8370cfc21716147204150"
+ },
+ {
+ "path": "outputs/simulator-continuation-20261020/financial-screen.jpg",
+ "sha256": "73b6510861dfc4a29147331ac99f7a7db80987c1db03d1dc34efccfaffaa61e5"
+ },
+ {
+ "path": "outputs/simulator-continuation-20261020/financial-screen.txt",
+ "sha256": "de37ce59ee87650cdb9bf977e9dddc87ec56ecbb0efb52260c8c69f2898d6e57"
+ },
+ {
+ "path": "outputs/simulator-continuation-20261020/new-session-A.txt",
+ "sha256": "f1f2455a3d81bc9a140feae71bfd813ad3e3bd51f8167cbb8b7868540fb1b267"
+ },
+ {
+ "path": "outputs/simulator-continuation-20261020/new-session-B.txt",
+ "sha256": "ccc338bedaa8272db3a49bedb18d2872cb83cdc6b5912da73520132e14018900"
+ },
+ {
+ "path": "outputs/simulator-continuation-20261020/history-visible.txt",
+ "sha256": "39ac194341c81b1f2e81dc544f879da280ebf8f9e2d0bfad1b2ac95f2f0cc62f"
+ },
+ {
+ "path": "outputs/simulator-continuation-20261020/docx-render/interface-report.pdf",
+ "sha256": "00ae9586605bd29debffe96388bf874b0391170bbc7d60d6a94f1637975805b4"
+ }
+ ],
+ "verified": {
+ "frontend_tests": 14,
+ "php_tests": 3,
+ "php_assertions": 18,
+ "typescript": "passed",
+ "build": "passed",
+ "ui": "Two synthetic dossiers recovered after logout/login; failed-save retry; calculation navigation; results 24 and 12 visible; CSV and DOCX downloaded through application",
+ "history": "Three original hashes unchanged; other-account case 403 and result policy 404",
+ "synthetic_parameters": "In-memory resolver only; 7.5 then 9.25; period transition and stale source version; no database rows",
+ "corpus": "9901 paths enumerated; 32 fiscal filename candidates; three existing repository originals hashed; prior analyses retained",
+ "credit_recipe": "1000 MZN; nominal annual 12%; 12 months; monthly 1%; half-up cents; no grace or fees; first 88.85; last 88.84; interest 66.19; total 1066.19",
+ "cashflow_delta": "Versioned editable snapshot in existing economic JSON; two-dossier/new-session API recovery and refusal tested; blank tax not zero; report summary/detail included"
+ },
+ "limitations": [
+ "Current homologation inventory awaits local SSH authentication; remote read script prepared but not executed",
+ "Native Edge print dialog opened; user PDF save pending; DOCX render not counted as native print",
+ "New cashflow browser and generated-output visual delta awaits completion of native print",
+ "Interactive attachment attempt blocked by extension file-URL permission before upload; accepted automated exporter limits retained",
+ "QA role bypasses RLS; new refusal proof is application policy; previous nonprivileged RLS evidence retained",
+ "UI DOCX evidence is dossier B without cashflow snapshot and before historical-result selection; its unchanged six-page output is retained",
+ "Tax DSL/governance/resolver exist but execution engine remains custom arithmetic; connecting legal tax formulas is not silently included"
+ ],
+ "remaining_hours": [
+ 2,
+ 4
+ ],
+ "remaining_scope": "Read-only remote collection; native PDF; new cashflow visual continuation; evidence closure; excludes waiting and tax-engine integration",
+ "secrets_and_private_fixtures": "Not included in source manifest or Git"
+}
diff --git a/docs/investmoz/unification/traceability-and-manual.md b/docs/investmoz/unification/traceability-and-manual.md
index bb240277..efed4715 100644
--- a/docs/investmoz/unification/traceability-and-manual.md
+++ b/docs/investmoz/unification/traceability-and-manual.md
@@ -1594,3 +1594,32 @@ Provas novas: 34 testes dirigidos de formulário/relatório passaram; TypeScript
Exemplos para revisão por HTTP na aplicação QA local, sem `file:`: `http://127.0.0.1:18570/qa/relatorios/simulador/positivo.pdf`, `negativo.pdf`, `nulo.pdf`, `incompleto.pdf`, e os mesmos nomes com extensão `.docx`. Estes endereços são uma whitelist do router privado de QA, exclusivamente loopback; não são rotas públicas de produção. Ficheiros não sintéticos e configuração privada não são servidos por esta whitelist.
**Estimativa restante: 6–10 h**, já descontado o trabalho acima e as provas aceites; não são as 18–30 h iniciais. Distribuição: continuidade autenticada/histórico e mudança de parâmetros 2–3 h; controlos adicionais delimitados 1–2 h; impressão Edge e comparação ecrã/download 1–2 h; mensagens dinâmicas/falhas de gravação/exportação e fecho 2–3 h. Não inclui cadastrar regras fiscais ausentes nem decidir taxas/incentivos sem fundamento. Falta de dados fiscais não bloqueia os controlos aritméticos disponíveis. A revisão integral continua aberta nos pontos precisos desta tabela.
+
+### Continuação delimitada do simulador — controlo operacional e fontes
+
+Base `a690c665fb4e`, mesma branch QA. Clone exclusivo `simulator_review_delta_20261020_test`, loopback 55441, aplicação `http://127.0.0.1:18576`. A base anterior e as contas reais foram preservadas. Duas contas e dois dossiers SINTÉTICOS QA foram preparados apenas neste clone; sem funções de gestão e sem transporte para o destino. O sinalizador local `INVESTOR_PATH_ENABLED=true` habilitou a capacidade já existente neste clone; não representa configuração aplicada remotamente.
+
+| Item existente | Resultado desta continuação | Saldo |
+| --- | --- | --- |
+| AUD-SIM-INT01 | Prova anterior de data inválida preservada; código não alterado neste ponto. | Concluído o delta anterior; sem repetição. |
+| AUD-SIM-INT02 | Confirmado zero de regras de qualquer estado na base QA original, com visibilidade administrativa sem ocultação por RLS; biblioteca contém 30 documentos. Inventariados 9901 caminhos do acervo antes da selecção: 32 candidatos por nome fiscal e três originais versionados no repositório, estes com hash actual conferido. Existem análises de textos e correspondências preparadas em `primary-text-review-2025-2026.md`, `official-source-register.md`, `fiscal-source-references-2026-10-06.csv` e comando de arquivo existente. Não se identificou um conjunto serializado de parâmetros fiscais reais nos seeders/configuração pesquisados. | Não confundir ausência do cadastro executável QA com ausência de fontes/trabalho documental. Leitura actual da homologação aguarda autenticação SSH local; não se afirmou que o destino também está vazio. Os binários do acervo montado não foram todos relidos: enumeração de caminhos não comprova conteúdo/versão. |
+| AUD-SIM-INT03 | A e B recuperaram títulos próprios; B conservou a edição após erro/repetição. Logout e novo login normais, depois recuperação de A e B, conferidos no navegador. Recusa de outra conta: 403 nos dois dossiers e 404 no resultado, pelo serviço/política utilizados pelo controlador. | Concluída a conferência delimitada. O papel PostgreSQL deste clone é superutilizador: esta prova de recusa é da aplicação, não nova certificação de RLS. Provas anteriores de RLS não privilegiado mantidas. |
+| AUD-SIM-INT04 | Corrigida a abertura de histórico que carregava mas não mostrava o resultado no percurso de pequeno negócio. Resultado seleccionado agora visível e explicitamente separado das entradas do dossier. Navegador abriu 24 e 12; três snapshots mantiveram hashes/versões originais após a utilização. | Versões sintéticas exclusivamente em memória do resolvedor: 7,5% até Junho e 9,25% a partir de Julho, fonte e código QA que não representam legislação. Versão histórica conservada; transição dentro do período fica indeterminada; versão da fonte incompatível deixa de resolver. Nada foi registado/publicado na base ou no destino. É prova do resolvedor, não de execução fiscal integrada. |
+| AUD-SIM-INT05 | Livro independente de três meses: resultado antes de impostos 225 MZN, fundo de maneio 130, usos 730, caixa inicial após usos 470 e caixa final 638. Distintos reconhecimento/pagamento de juros, capital, compras, despesas e cobranças. Tesouraria com mês de imposto desconhecido mantém total indeterminado; caixa conhecida 210, sem somar crédito/pedido/reconhecimento de IVA como dinheiro. | Controlos passaram. Pressupostos completos no teste `simulator-independent-review.test.mjs`; não são previsões ou taxas legais. |
+| AUD-SIM-INT06 | CSV e Word efectivamente descarregados pelos botões normais do dossier B, não por gerador autónomo. Prestação 88,85, juros 66,19 e saldo 100 coincidem com o ecrã. DOCX usa vírgula decimal; CSV/ecrã usam ponto. Word de seis páginas integralmente conferido após renderização, com pressupostos e campos desconhecidos explícitos. | Impressão nativa Edge aberta, aguardando gravação de `SINTETICO-QA-Edge-continuacao.pdf` pelo utilizador; o diálogo não é acessível à automação. Não contar o PDF renderizado do DOCX como essa prova. |
+| AUD-SIM-INT07 | Corrigido Abrir cálculo: pequeno negócio abre a etapa financeira existente, em vez de procurar uma etapa fiscal ausente. Falha única 503 de gravação preservou edição; botão Guardar agora repete efectivamente a escrita, com fila serializada e rechecagem da impressão digital para evitar gravação redundante. Recuperação posterior confirmou a edição. | Exportação normal Word/CSV comprovada. Provas automáticas aceites de limites de anexos preservadas e regressão do exportador passou. A tentativa interactiva com anexo sintético de 16 MiB foi impedida pela permissão de URLs de ficheiro da extensão, antes de o ficheiro entrar na aplicação; não foi registada como falha do portal nem como prova de recusa pela interface. |
+| AUD-SIM-INT08 | Mensagem dinâmica Not found observada foi tratada no respondedor comum: mensagens genéricas de ausência/acesso/sessão/tentativas/servidor em português, preservando HTTP e correlação. Traduzidas as mensagens de validação de parâmetros/expressões que o controlador de governação apresenta nos campos, sem alterar DSL, códigos ou limites. | Conferência do 404 por pedido real ao kernel passou; mensagens das validações revistas no código/controlador. Inventário estático anterior e traduções intencionalmente seleccionáveis mantidos. Não se certificou cada conteúdo dinâmico do catálogo nem uma sessão administrativa de governação. |
+
+**Pressupostos do crédito de controlo:** capital 1000 MZN; prazo 12 meses; periodicidade mensal (12 prestações); taxa nominal anual hipotética 12%, dividida por 12, resultando em 1% por mês; sem carência, comissão inicial ou periódica. Montantes com duas casas decimais, juros/prestação com metade para cima; saldo residual ajustado na última prestação. Primeira prestação 88,85; última 88,84; juros totais 66,19; total pago 1066,19 MZN. O CSV contém o cronograma completo; o Word apresenta prestação inicial, juros e receita de cálculo. O controlo anterior de 100 por mês usa capital 1200 MZN, 12 meses, periodicidade mensal, taxa nominal anual zero, sem carência/comissões, mesmo arredondamento, total 1200. Estes pressupostos foram acrescentados às saídas e à prova; nenhum produto de crédito foi confirmado.
+
+**Reconciliação fiscal:** os três originais já versionados (BR 248/2025, republicação Lei 22/2022 e BR 89/2017), as análises e relações documentais são trabalho reutilizável. Não pedir novamente documentos já disponíveis nem reabrir a credibilidade das fontes oficiais aceites pela Tempo em 08/10. As referências antigas a reautenticação genérica ficam ultrapassadas por essa decisão; continuam a ser tarefas distintas identificar artigo, âmbito, período e parâmetro. A biblioteca documental não é a tabela de parâmetros. O repositório contém governação, DSL e resolvedor temporal; `RunSimulation` executa a aritmética personalizada e não consome essas fórmulas fiscais. Mesmo encontrar parâmetros no destino não comprovaria cálculo fiscal integrado. Essa ligação funcional não foi implementada silenciosamente neste recorte.
+
+Artefactos em `outputs/simulator-continuation-20261020/`: `interface-comparison.json`, `interface-financial.csv`, `interface-report.docx`, seis páginas em `docx-render/`, provas de A/B e nova sessão, `history-preservation-proof.json`, `fiscal-corpus-inventory.json` e `local-rule-visibility.json`. Credenciais, dump e fixtures privadas ficam excluídos do Git. O script `read-fiscal-remote.py` preparado só faz leitura administrativa e `BEGIN READ ONLY`/SELECT/ROLLBACK; ainda não foi executado por faltar autenticar a ligação. Nenhum teste ou dado sintético foi enviado à homologação.
+
+**Esforço restante do recorte: 1–3 h após os acessos/ficheiro necessários**, para recolha remota, conferência do PDF nativo e fecho das limitações de interface. O tempo de espera do utilizador não é esforço de programação. Não são as 6–10 h anteriores nem as 18–30 h iniciais. Não inclui implementar o motor fiscal integrado ou activar parâmetros reais. As pendências de acesso e impressão não bloqueiam a revisão dos resultados QA já utilizáveis.
+
+**Achado adicional de continuidade, identificado antes da correcção:** o componente de tesouraria mantinha os seus pressupostos apenas no estado React; não recebia nem devolvia snapshot ao rascunho. Foi acrescentado `cashflow_snapshot` versionado ao JSON económico existente e ao validador do pedido, sem migration. O componente recupera os campos e o modo de imposto; guarda apenas após edição e não converte vazios em zero. Snapshots desconhecidos/inválidos não geram resultados alternativos e mostram aviso. O relatório consolidado e o CSV do dossier passam a incluir pressupostos, valores conhecidos/indeterminados e meses; o resumo destaca a tesouraria quando existir snapshot. Dossiers antigos sem esse campo mantêm a apresentação anterior — as seis páginas do DOCX de B já conferidas não tinham esse campo e não foram reemitidas desnecessariamente.
+
+Provas adicionais: teste HTTP com dois dossiers, logout lógico/nova sessão, recuperação exacta e recusa por outra conta passou (8 asserções), em transacção revertida no clone. Quatro controlos frontend dirigidos passaram, incluindo recuperação de mês vazio, valor inválido para correcção, relatório sem zero inventado e pressupostos completos do crédito. As 10 verificações do exportador afectado passaram; teste temporal sintético passou (7 asserções); erro HTTP em português passou (3). TypeScript/build e sintaxe PHP passaram. A conferência visual do novo componente de tesouraria com sessão real QA permanece por executar depois de fechar a impressão do Edge; não foi substituída por uma prova unitária.
+
+**Saldo revisto após esse achado: 2–4 h de verificação delimitada**, já descontado o desenvolvimento concluído: leitura remota 0,5–1 h; PDF nativo 0,5–1 h; continuidade visual/saída da tesouraria recém-corrigida 0,5–1 h; consolidação das provas e limitações 0,5–1 h. Exclui espera por autenticação/ficheiro e a integração funcional futura das regras fiscais. Esta estimativa substitui 1–3 h acima por uma divergência concreta; não retoma as 6–10 h nem as 18–30 h originais.
diff --git a/docs/investmoz/v2/auditoria-portal/backlog-priorizado.csv b/docs/investmoz/v2/auditoria-portal/backlog-priorizado.csv
index c030f1eb..8571cb7e 100644
--- a/docs/investmoz/v2/auditoria-portal/backlog-priorizado.csv
+++ b/docs/investmoz/v2/auditoria-portal/backlog-priorizado.csv
@@ -7382,11 +7382,11 @@ P1,AUD-SEARCH-T09,QA — revisão Tempo pendente,Gestão de procura/capacidade p
P1,AUD-SEARCH-T10,QA — revisão Tempo pendente,Não houve importação/publicação; a cópia QA usada não contém recursos públicos reais para nova prova positiva.,"Resumo único nomeado Documentos, chips removíveis, controlos vazios ocultos sem apagar valores seleccionados, rodapé comum.",,HTTP/Inertia e navegador: vazio e termos preservados; retorno documental existente conservado.,,,,,,navegação/pesquisa,"Conversa Tempo 20/10/2026; manual, secção Pesquisa transversal QA",Consulta coerente e compreensível; nenhuma alteração de dados para coincidir contagens.,portal/services; portal/document,Não houve importação/publicação; a cópia QA usada não contém recursos públicos reais para nova prova positiva.,,Rever a branch antes de integrar em main; sem disponibilização remota nesta execução.,,,Pesquisa transversal QA 20/10/2026,HTTP/Inertia e navegador: vazio e termos preservados; retorno documental existente conservado.,,,,Apenas delta identificado; não certifica funcionalidades não alteradas.,
P1,AUD-SEARCH-T11,QA — revisão Tempo pendente,"Situação jurídica, consulta e cálculo continuam independentes.",Reutiliza a correcção da biblioteca e preserva filtros jurídicos/documentais pertinentes.,,HTTP/Inertia: resources.total=0; consumidor e query pública partilhados.,,,,,,navegação/pesquisa,"Conversa Tempo 20/10/2026; manual, secção Pesquisa transversal QA",Consulta coerente e compreensível; nenhuma alteração de dados para coincidir contagens.,portal/services; portal/document,"Situação jurídica, consulta e cálculo continuam independentes.",,Rever a branch antes de integrar em main; sem disponibilização remota nesta execução.,,,Pesquisa transversal QA 20/10/2026,HTTP/Inertia: resources.total=0; consumidor e query pública partilhados.,,,,Apenas delta identificado; não certifica funcionalidades não alteradas.,
P1,AUD-GUIDE-CARD-20261020,QA review pending,New delta; not approved for main,Common educational card and existing source/context adapter,,Desktop/mobile/keyboard; public-state and source checks,,,,,,educational presentation,Tempo 20/10/2026; same manual,,,Tempo visual review; library integration verified on existing QA; previous dossier proof retained,,Integration approval for new delta,,,,1 test / 8 assertions; TypeScript/build; real public-source visual preview,,,,No import; no remote changes; no claim of new dossier proof,
-P1,AUD-SIM-INT01,QA verified: inline date error and no substitute period,Invalid one-off fiscal date can throw instead of returning validation state,,,,,,,,,simulator and shared language,Tempo request 20/10/2026; same manual,,,,,No new activation authorised,,,,QA verified: inline date error and no substitute period,,,,No claim of full review completion,
-P1,AUD-SIM-INT02,QA inventory complete: no fiscal rules; custom arithmetic only; unavailable parameters remain indeterminate,Review actual fiscal parameters sources periods and eligibility,,,,,,,,,simulator and shared language,Tempo request 20/10/2026; same manual,,,,,No new activation authorised,,,,QA inventory complete: no fiscal rules; custom arithmetic only; unavailable parameters remain indeterminate,,,,No claim of full review completion,
-P1,AUD-SIM-INT03,Scope remount fixed; reload recovery verified; authenticated two-dossier and new-session frontend check pending,Review isolation recovery across projects and sessions,,,,,,,,,simulator and shared language,Tempo request 20/10/2026; same manual,,,,,No new activation authorised,,,,Scope remount fixed; reload recovery verified; authenticated two-dossier and new-session frontend check pending,,,,No claim of full review completion,
-P1,AUD-SIM-INT04,Stale warning verified after input edit; historical backend preserved; authenticated history and parameter-version delta pending,Review stale and historical results and correct dossier,,,,,,,,,simulator and shared language,Tempo request 20/10/2026; same manual,,,,,No new activation authorised,,,,Stale warning verified after input edit; historical backend preserved; authenticated history and parameter-version delta pending,,,,No claim of full review completion,
-P1,AUD-SIM-INT05,Independent cash rounding break-even and loan controls passed; remaining cash-flow and business-plan controls delimited,Independent financial controls unknown zero units and currencies,,,,,,,,,simulator and shared language,Tempo request 20/10/2026; same manual,,,,,No new activation authorised,,,,Independent cash rounding break-even and loan controls passed; remaining cash-flow and business-plan controls delimited,,,,No claim of full review completion,
-P1,AUD-SIM-INT06,Four real-exporter synthetic DOCX reports checked at 6/6/6/5 pages; native Edge print and UI comparison pending,Generated reports screen download and pagination,,,,,,,,,simulator and shared language,Tempo request 20/10/2026; same manual,,,,,No new activation authorised,,,,Four real-exporter synthetic DOCX reports checked at 6/6/6/5 pages; native Edge print and UI comparison pending,,,,No claim of full review completion,
-P1,AUD-SIM-INT07,Custom arithmetic context requirement removed; fiscal error/retry verified; save/export fault checks pending,Explicit validate calculate save export errors and retry,,,,,,,,,simulator and shared language,Tempo request 20/10/2026; same manual,,,,,No new activation authorised,,,,Custom arithmetic context requirement removed; fiscal error/retry verified; save/export fault checks pending,,,,No claim of full review completion,
-P1,AUD-SIM-INT08,4519 TSX literal inventory and auth/settings/common translations; dynamic backend/catalogue inventory remains open,Portuguese across portal and simulator outputs,,,,,,,,,simulator and shared language,Tempo request 20/10/2026; same manual,,,,,No new activation authorised,,,,4519 TSX literal inventory and auth/settings/common translations; dynamic backend/catalogue inventory remains open,,,,No claim of full review completion,
+P1,AUD-SIM-INT01,Previous inline-date proof retained; unchanged delta not repeated,Invalid one-off fiscal date can throw instead of returning validation state,,,,,,,,,simulator and shared language,Tempo request 20/10/2026; same manual,,,,,No new activation authorised,,,,Previous inline-date proof retained; unchanged delta not repeated,,,,No claim of full review completion,
+P1,AUD-SIM-INT02,QA zero rules verified with full visibility; prepared archive and analyses reused; current homologation read awaits SSH authentication,Review actual fiscal parameters sources periods and eligibility,,,,,,,,,simulator and shared language,Tempo request 20/10/2026; same manual,,,,,No new activation authorised,,,,QA zero rules verified with full visibility; prepared archive and analyses reused; current homologation read awaits SSH authentication,,,,No claim of full review completion,
+P1,AUD-SIM-INT03,Two dossiers and new login recovered in browser; cashflow persistence corrected and HTTP-tested; new visual cashflow delta pending,Review isolation recovery across projects and sessions,,,,,,,,,simulator and shared language,Tempo request 20/10/2026; same manual,,,,,No new activation authorised,,,,Two dossiers and new login recovered in browser; cashflow persistence corrected and HTTP-tested; new visual cashflow delta pending,,,,No claim of full review completion,
+P1,AUD-SIM-INT04,Historical results visible after fix; 12 and 24 recovered; hashes unchanged; synthetic temporal resolver versions verified in memory only,Review stale and historical results and correct dossier,,,,,,,,,simulator and shared language,Tempo request 20/10/2026; same manual,,,,,No new activation authorised,,,,Historical results visible after fix; 12 and 24 recovered; hashes unchanged; synthetic temporal resolver versions verified in memory only,,,,No claim of full review completion,
+P1,AUD-SIM-INT05,Independent business-plan and cashflow ledgers passed; complete credit recipe included in screen and exports,Independent financial controls unknown zero units and currencies,,,,,,,,,simulator and shared language,Tempo request 20/10/2026; same manual,,,,,No new activation authorised,,,,Independent business-plan and cashflow ledgers passed; complete credit recipe included in screen and exports,,,,No claim of full review completion,
+P1,AUD-SIM-INT06,Normal UI CSV and DOCX match shared values; all six DOCX pages checked; native Edge PDF awaits user save; new cashflow visual output pending,Generated reports screen download and pagination,,,,,,,,,simulator and shared language,Tempo request 20/10/2026; same manual,,,,,No new activation authorised,,,,Normal UI CSV and DOCX match shared values; all six DOCX pages checked; native Edge PDF awaits user save; new cashflow visual output pending,,,,No claim of full review completion,
+P1,AUD-SIM-INT07,Calculation navigation and failed-save retry fixed and browser-verified; normal downloads verified; interactive attachment blocked by extension permission,Explicit validate calculate save export errors and retry,,,,,,,,,simulator and shared language,Tempo request 20/10/2026; same manual,,,,,No new activation authorised,,,,Calculation navigation and failed-save retry fixed and browser-verified; normal downloads verified; interactive attachment blocked by extension permission,,,,No claim of full review completion,
+P1,AUD-SIM-INT08,Generic HTTP and fiscal-expression validation messages translated; status/correlation verified; selectable translations and source titles preserved,Portuguese across portal and simulator outputs,,,,,,,,,simulator and shared language,Tempo request 20/10/2026; same manual,,,,,No new activation authorised,,,,Generic HTTP and fiscal-expression validation messages translated; status/correlation verified; selectable translations and source titles preserved,,,,No claim of full review completion,
diff --git a/docs/investmoz/v2/auditoria-portal/backlog.md b/docs/investmoz/v2/auditoria-portal/backlog.md
index fee32b81..747d0beb 100644
--- a/docs/investmoz/v2/auditoria-portal/backlog.md
+++ b/docs/investmoz/v2/auditoria-portal/backlog.md
@@ -3004,3 +3004,5 @@ AUD-GUIDE-CARD-20261020: componente comum com imagem autorizada, título, resumo
AUD-SIM-INT01 through AUD-SIM-INT08 are recorded in the same matrix and manual. Initial diagnosis and targeted date/Portuguese corrections only; full review remains open. Initial effort 18-30 h, subject to discounting accepted evidence and completed work. No main integration, real-data modification or tax parameter activation.
Actualização QA de 20/10/2026: os mesmos oito itens foram reconciliados no manual e nas duas tabelas existentes. Data com mensagem junto ao campo; inventário dos parâmetros efectivos (zero regras fiscais QA); controlos matemáticos independentes; aviso de recálculo; quatro relatórios reais do exportador com 6/6/6/5 páginas; consulta fiscal com erro/nova tentativa e traduções comuns verificados. O formulário personalizado já não exige contexto fiscal que não envia ao cálculo. Restam continuidade autenticada entre dois dossiers/nova sessão e histórico, mudança de parâmetros, controlos delimitados de cash-flow/plano, impressão nativa Edge/comparação pela interface e mensagens dinâmicas/falhas dirigidas de gravação/exportação. Estimativa restante **6–10 h**, distinta das 18–30 h iniciais. Sem main, servidor remoto, importação ou activação fiscal. Exemplos PDF são renderizações do DOCX; não certificam impressão do Edge. Ver o saldo detalhado AUD-SIM-INT01–08 no mesmo manual.
+
+Continuação delimitada: dois dossiers e nova entrada conferidos no navegador; histórico agora visível no percurso de pequeno negócio, hashes preservados; Abrir cálculo encaminha para a etapa financeira; repetição de gravação após 503 corrigida e conferida; controlos independentes de tesouraria/plano passaram. CSV e Word descarregados pela interface coincidem nos valores comuns e apresentam a receita completa do crédito. Seis páginas do DOCX conferidas; PDF nativo ainda aguarda gravação pelo utilizador. QA tem zero regras com visibilidade completa, mas existem três originais fiscais versionados e análises/referências reutilizáveis; não se concluiu ausência na homologação sem autenticação SSH. Versões sintéticas testadas apenas em memória, sem cadastro/activação. Encontrada e corrigida perda dos pressupostos da tesouraria: snapshot no JSON existente, recuperação por dossier e relatório, sem migration; testes HTTP/frontend passaram, falta conferência visual deste novo delta. Saldo **2–4 h** após acessos/ficheiro, detalhado no mesmo manual; integração do motor fiscal permanece separada e não foi implementada. Sem main ou intervenção remota.
diff --git a/docs/investmoz/v2/auditoria-portal/matriz-requisitos.csv b/docs/investmoz/v2/auditoria-portal/matriz-requisitos.csv
index eb244868..f2e61250 100644
--- a/docs/investmoz/v2/auditoria-portal/matriz-requisitos.csv
+++ b/docs/investmoz/v2/auditoria-portal/matriz-requisitos.csv
@@ -10651,11 +10651,11 @@ AUD-SEARCH-T09,Consistência transversal da pesquisa e navegação — Educaçã
AUD-SEARCH-T10,Consistência transversal da pesquisa e navegação — Biblioteca,Instruções expressas da Tempo na conversa de 20/10/2026; AGENTS.md; RF-MN-010 e AUD-GEO-01 preservados.,navegação/pesquisa,/documentos,"portal/services; portal/document — Resumo único nomeado Documentos, chips removíveis, controlos vazios ocultos sem apagar valores seleccionados, rodapé comum.",Sem alteração remota nesta execução; revisão apenas na branch codex/opportunity-search.,HTTP/Inertia e navegador: vazio e termos preservados; retorno documental existente conservado.,QA — delta delimitado; revisão Tempo pendente,Não houve importação/publicação; a cópia QA usada não contém recursos públicos reais para nova prova positiva.,,"Filtros, contagem, resultados e URL coerentes; universos identificados; erro distinto de vazio; permissões e contexto conservados.",P1,,RF-MN-010; AUD-GEO-01,,,,,,,,,,,"Dois resumos de contagem, filtros activos sem remoção individual, controlos sem opções e rodapé local repetido.",Não houve importação/publicação; a cópia QA usada não contém recursos públicos reais para nova prova positiva.,portal/services; portal/document; SearchState; SearchFeedback; TerritoryFields; portalNavigation,,"Integração em main apenas após revisão; assistência NLP, contratos e P7 separados.",Correcção/simplificação em QA,,HTTP/Inertia e navegador: vazio e termos preservados; retorno documental existente conservado.,Pesquisa transversal QA 20/10/2026,20/10/2026,,,,,,,,,,Provas comuns cobrem o componente reutilizado; HTTP vazio não demonstra operações de gestão ou publicação. Não houve importação/publicação; a cópia QA usada não contém recursos públicos reais para nova prova positiva.,,falha funcional; navegação; apresentação visual,,Disponível para revisão na aplicação QA local.
AUD-SEARCH-T11,Consistência transversal da pesquisa e navegação — Legislação,Instruções expressas da Tempo na conversa de 20/10/2026; AGENTS.md; RF-MN-010 e AUD-GEO-01 preservados.,navegação/pesquisa,/legislacao,portal/services; portal/document — Reutiliza a correcção da biblioteca e preserva filtros jurídicos/documentais pertinentes.,Sem alteração remota nesta execução; revisão apenas na branch codex/opportunity-search.,HTTP/Inertia: resources.total=0; consumidor e query pública partilhados.,QA — delta delimitado; revisão Tempo pendente,"Situação jurídica, consulta e cálculo continuam independentes.",,"Filtros, contagem, resultados e URL coerentes; universos identificados; erro distinto de vazio; permissões e contexto conservados.",P1,,RF-MN-010; AUD-GEO-01,,,,,,,,,,,Mesmo padrão da biblioteca; não deve expor um universo concorrente.,"Situação jurídica, consulta e cálculo continuam independentes.",portal/services; portal/document; SearchState; SearchFeedback; TerritoryFields; portalNavigation,,"Integração em main apenas após revisão; assistência NLP, contratos e P7 separados.",Correcção/simplificação em QA,,HTTP/Inertia: resources.total=0; consumidor e query pública partilhados.,Pesquisa transversal QA 20/10/2026,20/10/2026,,,,,,,,,,"Provas comuns cobrem o componente reutilizado; HTTP vazio não demonstra operações de gestão ou publicação. Situação jurídica, consulta e cálculo continuam independentes.",,falha funcional; navegação; apresentação visual,,Disponível para revisão na aplicação QA local.
AUD-GUIDE-CARD-20261020,"Related educational cards: image, source summary, clear actions",Tempo 20/10/2026; independent delta after 0e7d51f18fb4,educational presentation,/sectores/{slug}; /explorar/{group}; articles; /oportunidades and detail,EducationalArticleCard; EducationalGuides; LegacyContent; SectorInvestment,QA only,1 test / 8 assertions; PHP syntax; TypeScript/build; desktop2048/mobile390; keyboard,QA review pending,Normal QA sector and article verified against existing library resource 10 and local image 25; previous dossier proof retained,,Source-derived summary; public images; conditional opportunity action; context preserved,P1,,,,,,,,,,,,,,,,,,presentation,,,,20/10/2026,,,,,,,,,,,,content; navigation; visual presentation,,Ready for QA review
-AUD-SIM-INT01,Invalid one-off fiscal date can throw instead of returning validation state,Tempo request 20/10/2026; integrated simulator review; same manual,simulator and shared language,,,QA only,Fixed in QA; invalid-date checks; 38 directed frontend tests passed,QA verified: inline date error and no substitute period,Delta verified; precise remaining work in same manual; 6-10 h remaining,,,P0,,,,,,,,,,,,,,,,,,diagnosis and targeted corrections,,,,20/10/2026,,,,,,,,,,,,,,
-AUD-SIM-INT02,Review actual fiscal parameters sources periods and eligibility,Tempo request 20/10/2026; integrated simulator review; same manual,simulator and shared language,,,QA only,Verification pending; no parameter activation,QA inventory complete: no fiscal rules; custom arithmetic only; unavailable parameters remain indeterminate,Delta verified; precise remaining work in same manual; 6-10 h remaining,,,P0,,,,,,,,,,,,,,,,,,diagnosis and targeted corrections,,,,20/10/2026,,,,,,,,,,,,,,
-AUD-SIM-INT03,Review isolation recovery across projects and sessions,Tempo request 20/10/2026; integrated simulator review; same manual,simulator and shared language,,,QA only,Existing controls and accepted proofs to reconcile; no leak established,Scope remount fixed; reload recovery verified; authenticated two-dossier and new-session frontend check pending,Delta verified; precise remaining work in same manual; 6-10 h remaining,,,P0,,,,,,,,,,,,,,,,,,diagnosis and targeted corrections,,,,20/10/2026,,,,,,,,,,,,,,
-AUD-SIM-INT04,Review stale and historical results and correct dossier,Tempo request 20/10/2026; integrated simulator review; same manual,simulator and shared language,,,QA only,Existing versions/fingerprints; targeted verification pending,Stale warning verified after input edit; historical backend preserved; authenticated history and parameter-version delta pending,Delta verified; precise remaining work in same manual; 6-10 h remaining,,,P1,,,,,,,,,,,,,,,,,,diagnosis and targeted corrections,,,,20/10/2026,,,,,,,,,,,,,,
-AUD-SIM-INT05,Independent financial controls unknown zero units and currencies,Tempo request 20/10/2026; integrated simulator review; same manual,simulator and shared language,,,QA only,Reconciliation pending; no invented tax assumptions,Independent cash rounding break-even and loan controls passed; remaining cash-flow and business-plan controls delimited,Delta verified; precise remaining work in same manual; 6-10 h remaining,,,P1,,,,,,,,,,,,,,,,,,diagnosis and targeted corrections,,,,20/10/2026,,,,,,,,,,,,,,
-AUD-SIM-INT06,Generated reports screen download and pagination,Tempo request 20/10/2026; integrated simulator review; same manual,simulator and shared language,,,QA only,Existing exporters; examples for this review still pending,Four real-exporter synthetic DOCX reports checked at 6/6/6/5 pages; native Edge print and UI comparison pending,Delta verified; precise remaining work in same manual; 6-10 h remaining,,,P1,,,,,,,,,,,,,,,,,,diagnosis and targeted corrections,,,,20/10/2026,,,,,,,,,,,,,,
-AUD-SIM-INT07,Explicit validate calculate save export errors and retry,Tempo request 20/10/2026; integrated simulator review; same manual,simulator and shared language,,,QA only,Existing idempotency; consumer-level verification pending,Custom arithmetic context requirement removed; fiscal error/retry verified; save/export fault checks pending,Delta verified; precise remaining work in same manual; 6-10 h remaining,,,P1,,,,,,,,,,,,,,,,,,diagnosis and targeted corrections,,,,20/10/2026,,,,,,,,,,,,,,
-AUD-SIM-INT08,Portuguese across portal and simulator outputs,Tempo request 20/10/2026; integrated simulator review; same manual,simulator and shared language,,,QA only,"Partial QA fix: rate labels, password confirmation/security and spinner; wider inventory pending",4519 TSX literal inventory and auth/settings/common translations; dynamic backend/catalogue inventory remains open,Delta verified; precise remaining work in same manual; 6-10 h remaining,,,P1,,,,,,,,,,,,,,,,,,diagnosis and targeted corrections,,,,20/10/2026,,,,,,,,,,,,,,
+AUD-SIM-INT01,Invalid one-off fiscal date can throw instead of returning validation state,Tempo request 20/10/2026; integrated simulator review; same manual,simulator and shared language,,,QA only,Fixed in QA; invalid-date checks; 38 directed frontend tests passed,Previous inline-date proof retained; unchanged delta not repeated,QA continuation verified; precise open inputs and cashflow visual check in same manual; 2-4 h remaining,,,P0,,,,,,,,,,,,,,,,,,diagnosis and targeted corrections,,,,20/10/2026,,,,,,,,,,,,,,
+AUD-SIM-INT02,Review actual fiscal parameters sources periods and eligibility,Tempo request 20/10/2026; integrated simulator review; same manual,simulator and shared language,,,QA only,Verification pending; no parameter activation,QA zero rules verified with full visibility; prepared archive and analyses reused; current homologation read awaits SSH authentication,QA continuation verified; precise open inputs and cashflow visual check in same manual; 2-4 h remaining,,,P0,,,,,,,,,,,,,,,,,,diagnosis and targeted corrections,,,,20/10/2026,,,,,,,,,,,,,,
+AUD-SIM-INT03,Review isolation recovery across projects and sessions,Tempo request 20/10/2026; integrated simulator review; same manual,simulator and shared language,,,QA only,Existing controls and accepted proofs to reconcile; no leak established,Two dossiers and new login recovered in browser; cashflow persistence corrected and HTTP-tested; new visual cashflow delta pending,QA continuation verified; precise open inputs and cashflow visual check in same manual; 2-4 h remaining,,,P0,,,,,,,,,,,,,,,,,,diagnosis and targeted corrections,,,,20/10/2026,,,,,,,,,,,,,,
+AUD-SIM-INT04,Review stale and historical results and correct dossier,Tempo request 20/10/2026; integrated simulator review; same manual,simulator and shared language,,,QA only,Existing versions/fingerprints; targeted verification pending,Historical results visible after fix; 12 and 24 recovered; hashes unchanged; synthetic temporal resolver versions verified in memory only,QA continuation verified; precise open inputs and cashflow visual check in same manual; 2-4 h remaining,,,P1,,,,,,,,,,,,,,,,,,diagnosis and targeted corrections,,,,20/10/2026,,,,,,,,,,,,,,
+AUD-SIM-INT05,Independent financial controls unknown zero units and currencies,Tempo request 20/10/2026; integrated simulator review; same manual,simulator and shared language,,,QA only,Reconciliation pending; no invented tax assumptions,Independent business-plan and cashflow ledgers passed; complete credit recipe included in screen and exports,QA continuation verified; precise open inputs and cashflow visual check in same manual; 2-4 h remaining,,,P1,,,,,,,,,,,,,,,,,,diagnosis and targeted corrections,,,,20/10/2026,,,,,,,,,,,,,,
+AUD-SIM-INT06,Generated reports screen download and pagination,Tempo request 20/10/2026; integrated simulator review; same manual,simulator and shared language,,,QA only,Existing exporters; examples for this review still pending,Normal UI CSV and DOCX match shared values; all six DOCX pages checked; native Edge PDF awaits user save; new cashflow visual output pending,QA continuation verified; precise open inputs and cashflow visual check in same manual; 2-4 h remaining,,,P1,,,,,,,,,,,,,,,,,,diagnosis and targeted corrections,,,,20/10/2026,,,,,,,,,,,,,,
+AUD-SIM-INT07,Explicit validate calculate save export errors and retry,Tempo request 20/10/2026; integrated simulator review; same manual,simulator and shared language,,,QA only,Existing idempotency; consumer-level verification pending,Calculation navigation and failed-save retry fixed and browser-verified; normal downloads verified; interactive attachment blocked by extension permission,QA continuation verified; precise open inputs and cashflow visual check in same manual; 2-4 h remaining,,,P1,,,,,,,,,,,,,,,,,,diagnosis and targeted corrections,,,,20/10/2026,,,,,,,,,,,,,,
+AUD-SIM-INT08,Portuguese across portal and simulator outputs,Tempo request 20/10/2026; integrated simulator review; same manual,simulator and shared language,,,QA only,"Partial QA fix: rate labels, password confirmation/security and spinner; wider inventory pending",Generic HTTP and fiscal-expression validation messages translated; status/correlation verified; selectable translations and source titles preserved,QA continuation verified; precise open inputs and cashflow visual check in same manual; 2-4 h remaining,,,P1,,,,,,,,,,,,,,,,,,diagnosis and targeted corrections,,,,20/10/2026,,,,,,,,,,,,,,
diff --git a/resources/js/components/simulation-cashflow-planner.tsx b/resources/js/components/simulation-cashflow-planner.tsx
index e72c19f0..92995f4d 100644
--- a/resources/js/components/simulation-cashflow-planner.tsx
+++ b/resources/js/components/simulation-cashflow-planner.tsx
@@ -1,7 +1,11 @@
-import { useState } from 'react';
+import { useEffect, useState } from 'react';
import {
exportMonthlyCashflowCsv,
projectMonthlyCashflow,
+ defaultCashflowDraftInputs,
+ restoreCashflowDraftSnapshot,
+ serializeCashflowDraftSnapshot,
+ type CashflowDraftSnapshot,
} from '@/lib/monthly-cashflow';
import {
isSimulationCurrency,
@@ -11,28 +15,38 @@ import {
export function SimulationCashflowPlanner({
currency,
onCurrencyChange,
+ snapshot,
+ onSnapshotChange,
}: {
currency: string;
onCurrencyChange: (currency: string) => void;
+ snapshot?: string;
+ onSnapshotChange?: (snapshot: string) => void;
}) {
- const [taxMode, setTaxMode] = useState<'fixed' | 'schedule'>('fixed');
- const [inputs, setInputs] = useState({
- openingCash: '0',
- monthlyRevenue: '200000',
- monthlyOperatingCosts: '150000',
- monthlyDebtService: '0',
- monthlyTaxAssessed: '',
- monthlyTaxSchedule: Array.from({ length: 12 }, () => ''),
- vatCreditEstimated: '0',
- vatRefundRequested: '0',
- vatRefundRecognized: '0',
- vatRefundExpectedReceipt: '0',
- vatRefundExpectedMonth: '',
- });
+ const restored = restoreCashflowDraftSnapshot(snapshot);
+ const [taxMode, setTaxMode] = useState<'fixed' | 'schedule'>(
+ restored?.taxMode ?? 'fixed',
+ );
+ const [inputs, setInputs] = useState(
+ restored?.inputs ?? defaultCashflowDraftInputs(),
+ );
+ useEffect(() => {
+ const saved = restoreCashflowDraftSnapshot(snapshot);
+ setTaxMode(saved?.taxMode ?? 'fixed');
+ setInputs(saved?.inputs ?? defaultCashflowDraftInputs());
+ }, [snapshot]);
+ function update(
+ next: typeof inputs,
+ mode: CashflowDraftSnapshot['taxMode'] = taxMode,
+ ) {
+ setInputs(next);
+ setTaxMode(mode);
+ onSnapshotChange?.(serializeCashflowDraftSnapshot(mode, next));
+ }
const set = (
key: Exclude,
value: string,
- ) => setInputs((current) => ({ ...current, [key]: value }));
+ ) => update({ ...inputs, [key]: value });
const cashflowInput = {
...inputs,
...(taxMode === 'fixed'
@@ -45,6 +59,10 @@ export function SimulationCashflowPlanner({
let projection;
let error = '';
try {
+ if (snapshot && !restored)
+ throw new Error(
+ 'Não foi possível recuperar os pressupostos guardados de tesouraria. Reveja os campos antes de voltar a guardar; nenhum resultado foi calculado com dados alternativos.',
+ );
projection = projectMonthlyCashflow({ ...cashflowInput, currency });
} catch (cause) {
error =
@@ -97,9 +115,11 @@ export function SimulationCashflowPlanner({
role="note"
className="rounded bg-amber-50 p-3 text-sm text-amber-950"
>
- Os valores preenchidos ao abrir são exemplos demonstrativos.
- Confirme ou altere os pressupostos antes de interpretar os
- resultados ou exportar o CSV.
+ {restored
+ ? 'Pressupostos recuperados do rascunho deste investimento; reveja-os antes de interpretar os resultados ou exportar.'
+ : snapshot
+ ? 'O rascunho guardado não pôde ser interpretado. Os campos mostram exemplos, que não substituem os dados guardados até os editar.'
+ : 'Os valores preenchidos ao abrir são exemplos demonstrativos. Confirme ou altere os pressupostos antes de interpretar os resultados ou exportar o CSV.'}
+ Montantes a duas casas decimais; juros e
+ prestação arredondados à unidade monetária
+ mínima, metade para cima. A última prestação
+ ajusta o saldo residual.
+
- Os valores abaixo são pressupostos que
- introduziu para planeamento. O resultado é uma
- operação percentual genérica, não uma taxa
- legal, imposto devido nem confirmação de
- elegibilidade.
+ {current.id !== 'tax' && result && (
+
+ Este resultado conserva os pressupostos e a
+ versão da execução seleccionada. Não substitui
+ as entradas deste dossier.
+ Os valores abaixo são pressupostos que
+ introduziu para planeamento. O resultado é
+ uma operação percentual genérica, não uma
+ taxa legal, imposto devido nem confirmação
+ de elegibilidade.
+