diff --git a/config/app.php b/config/app.php index 1f8dd277..af3eb1ed 100644 --- a/config/app.php +++ b/config/app.php @@ -78,7 +78,7 @@ return [ | */ - 'locale' => env('APP_LOCALE', 'en'), + 'locale' => env('APP_LOCALE', 'pt'), 'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'), diff --git a/docs/investmoz/simulations/integrated-review-20261020-receipt.json b/docs/investmoz/simulations/integrated-review-20261020-receipt.json new file mode 100644 index 00000000..d6ace55e --- /dev/null +++ b/docs/investmoz/simulations/integrated-review-20261020-receipt.json @@ -0,0 +1,281 @@ +{ + "base": "59ab4fe95635", + "branch": "codex/simulator-integrated-review", + "scope": "QA only; no main merge or remote change", + "source_files": [ + { + "path": "config/app.php", + "sha256": "a01f552093570f8744f7b874392824c15b5a6b49d61759273a658e8660a1d240" + }, + { + "path": "docs/investmoz/unification/traceability-and-manual.md", + "sha256": "347c340bcc4f5adc7965bd66cef1f357d98b6c271fdcd35206a3a086e59ad34d" + }, + { + "path": "docs/investmoz/v2/auditoria-portal/backlog-priorizado.csv", + "sha256": "a3473a391634c32fa8c53ecb2528b4de08f145ec0284b50f3bbf89aa97e5cf24" + }, + { + "path": "docs/investmoz/v2/auditoria-portal/backlog.md", + "sha256": "1294586bfd452e6430aa1fcb71a512cbf64177e529a416ac28dfab954839664b" + }, + { + "path": "docs/investmoz/v2/auditoria-portal/matriz-requisitos.csv", + "sha256": "f57861a36e310a892d55a16be6d06fecd48763defe11011036df8a3a33a57d6d" + }, + { + "path": "lang/pt.json", + "sha256": "c21fa0b63a3729e001f7d4ba891a23890bcfcfd718137d99a7c6c18d3be1a235" + }, + { + "path": "lang/pt/auth.php", + "sha256": "d32cc31b75d4056ef561baf501d9cbf71ec136c7f63bef635c0c3e31b9a2357d" + }, + { + "path": "lang/pt/validation.php", + "sha256": "bb95fe0f683cd6d8a6d0475dc322c71bce58197b952220e70ac74f5e97cdcf60" + }, + { + "path": "resources/js/components/appearance-tabs.tsx", + "sha256": "d53f48b0883713bf786fdf4b7877e03638bcf2e707b64ea6b8559501bfc8c4c7" + }, + { + "path": "resources/js/components/delete-user.tsx", + "sha256": "2f94b4dc6edf20bf9e00940d4d7e7b5da31bdf560f26b84efd65672bcee585b8" + }, + { + "path": "resources/js/components/manage-passkeys.tsx", + "sha256": "8f3e8970d3b82deba721ac27e57718e2e7dcecc67b1e78cb6404c475c9f9c62e" + }, + { + "path": "resources/js/components/manage-two-factor.tsx", + "sha256": "35fea615cf3a0454448057b0ef3137e24c21df4de30a58fce04cb847eb89eba1" + }, + { + "path": "resources/js/components/passkey-item.tsx", + "sha256": "a307c5ef209e24f3f913f77e355a4d04a1433f59f520ad45ad40112111ab4677" + }, + { + "path": "resources/js/components/passkey-register.tsx", + "sha256": "e7cb9d853eb943fd0899bc8530378eddbf941f9334a77bcbb7693d2e29d99470" + }, + { + "path": "resources/js/components/passkey-verify.tsx", + "sha256": "6db387920437ab39cd734a2af9bec7e64ae6469c0f1b368d536b275e57cbea37" + }, + { + "path": "resources/js/components/simulation-fiscal-status.tsx", + "sha256": "540dc6addbec0be79114526f7d27f53942291b0c7be05a2608a515498970d3e0" + }, + { + "path": "resources/js/components/simulation-form.tsx", + "sha256": "1b2d3a917e354eef7ccdb47201fb7e515ea646d45099beb9b15798c80e1dd2db" + }, + { + "path": "resources/js/components/two-factor-recovery-codes.tsx", + "sha256": "9384ae3987a58ae89018f2a997dec7faebec4f51f516ebbdc2b337c8e757f00f" + }, + { + "path": "resources/js/components/two-factor-setup-modal.tsx", + "sha256": "8586833e10310dbca8a097df80b86f40638f874170b7668fa476f0d7017a311b" + }, + { + "path": "resources/js/components/ui/dialog.tsx", + "sha256": "abeb6df2254f0bf5f73de1c9b5aeb4e667afe8ac0319e8b9aa099a612cc70b8d" + }, + { + "path": "resources/js/components/ui/sheet.tsx", + "sha256": "a525cc98beaf974e0dfe6cda1c5da1101798b6843ea355a758b3c5df5235f506" + }, + { + "path": "resources/js/components/user-menu-content.tsx", + "sha256": "9cb79c2a999bb363bd9d4fc1869b672c474b088eff23fca6e9f588ecae00d3ce" + }, + { + "path": "resources/js/hooks/use-two-factor-auth.ts", + "sha256": "16d9434379531f1994096e4d1fcfd778b2c829602cce5731d602a25ea9c0073f" + }, + { + "path": "resources/js/layouts/settings/layout.tsx", + "sha256": "78ea66c1eb6cad907e23de9c81a4d309a127f251355f04771f72eb5689609a4e" + }, + { + "path": "resources/js/lib/project-dossier-document.ts", + "sha256": "d0a66ca759e8710c1a3900bcd27cc7f0506587c782ab798c49703396c1fb31d6" + }, + { + "path": "resources/js/lib/project-dossier-report.ts", + "sha256": "6c5f52362a6218048f321b41211bfd328fa1e19ea4ab45fd1424932130482c8d" + }, + { + "path": "resources/js/lib/simulation-form.ts", + "sha256": "40df1a9b6939b1910d12045835e981d6e47d4c1d534775631ac2f6b655a5e2bd" + }, + { + "path": "resources/js/pages/auth/forgot-password.tsx", + "sha256": "6cdedca491d5a05dad0083fb637cd618d0b290bd6b48b98e7c6163dc0d740166" + }, + { + "path": "resources/js/pages/auth/login.tsx", + "sha256": "8468308dacd26bd94ee41683684dcb7719386a1a53196ed90496ca406b1b94da" + }, + { + "path": "resources/js/pages/auth/reset-password.tsx", + "sha256": "f3561726ae5d7ee682d968a295bbf60ec9f8cbabba1721c33f48bafd80d1b20a" + }, + { + "path": "resources/js/pages/auth/two-factor-challenge.tsx", + "sha256": "0fd14dff2f0a572f4b99905d0fb152baa476eaa55a221998b67da85fa4787610" + }, + { + "path": "resources/js/pages/auth/verify-email.tsx", + "sha256": "ffc3220d0ee6cc243e8e08ea46f8b1637794626fa45d7aeaa77f54203692864d" + }, + { + "path": "resources/js/pages/portal/services.tsx", + "sha256": "9369add7c012c1e8a9e217b46be55e2927382bb8f18b90973d3b1f4199442d9b" + }, + { + "path": "resources/js/pages/settings/appearance.tsx", + "sha256": "638367104837b028aeeb49b4cad1d2c9a44a92d1b9aebe71c31995605e0479b4" + }, + { + "path": "resources/js/pages/settings/profile.tsx", + "sha256": "c599b1ca523ece69c3f5d3fe480a9f2ce4c323e97afb42095f61f2ddb7d4ba7b" + }, + { + "path": "resources/js/pages/settings/security.tsx", + "sha256": "39264620329657af975f990a50d657c42186ead57458cd83040aa07f9cd78444" + }, + { + "path": "tests/frontend/project-dossier-report.test.mjs", + "sha256": "fcdcabfc659f2f4ca0d5a51e592719d53d51420271c7cbe350176b48cef3fc97" + }, + { + "path": "tests/frontend/simulation-form.test.mjs", + "sha256": "294a1b996d8131771ebaca85d2ff0e44bb6afd62e460073b7535486f13ae3a6b" + } + ], + "evidence": [ + { + "path": "outputs/simulator-integrated-review/effective-parameter-inventory.json", + "sha256": "aeec1251ef5dbbf7ae547af9f1ebcd2575aaa6c251ef65b37c6c31b8cf371acc" + }, + { + "path": "outputs/simulator-integrated-review/independent-controls-proof.json", + "sha256": "47ad49b2136fd74c801b7a5d057c891732d820fb6a5002ccf08c71a2ffabf575" + }, + { + "path": "outputs/simulator-integrated-review/visible-language-inventory.json", + "sha256": "5f1f50236ae492c8428a9528af350900e4f2c44c387fe8f23ebce0e4f1b14b3a" + }, + { + "path": "outputs/simulator-integrated-review/dynamic-language-inventory.json", + "sha256": "fe3246f570c333c2c59e9fe2acb0ec01078a158a46e53d37087ec59d419578a5" + }, + { + "path": "outputs/simulator-integrated-review/portuguese-validation-proof.json", + "sha256": "b7a454cdc1e934faada40b683ff2b7a6edce73aebfd22ba34e5da17a1854b0a1" + }, + { + "path": "outputs/simulator-integrated-review/http-reports-proof.json", + "sha256": "9d37fbee89f7dc556ff9496f6b0980e1c9626790eb50a41a499cf23e2f27fe5f" + }, + { + "path": "outputs/simulator-integrated-review/date-field-error.jpg", + "sha256": "6a792c09c61857a0a064a56d3d61b47fc2adc1d6c8bcf627161f6fa77c7b099d" + }, + { + "path": "outputs/simulator-integrated-review/stale-result.jpg", + "sha256": "84b37c41ba47d44f52979e83dbc3426184f5cbfee6d38e7c7d4caa3be225320b" + }, + { + "path": "outputs/simulator-integrated-review/tax-lookup-error.jpg", + "sha256": "d3f366690fa3acbda45bc021ade90dc003cea7dc0517c4d2932935548dbb40d0" + }, + { + "path": "outputs/simulator-integrated-review/positivo.docx", + "sha256": "61b1ea80476922aa1be8c29f9a757b389c3ecc8f0e75f414a29eddd5b37ecefd" + }, + { + "path": "outputs/simulator-integrated-review/positivo-model.json", + "sha256": "fc8df79e7ce81e4a475b99f5ea07763c1cee140bc9dcb24550ed26ce412e4e0d" + }, + { + "path": "outputs/simulator-integrated-review/checked-positivo/positivo.pdf", + "sha256": "60d655b33fcc27dbbaae40a0739c1ebeb9b366df9b00621a6536feecd60da452" + }, + { + "path": "outputs/simulator-integrated-review/negativo.docx", + "sha256": "f266cff1a1f9f63449703cc5ef51a7f4d2d7bf94230be70ae382bc89af8139bd" + }, + { + "path": "outputs/simulator-integrated-review/negativo-model.json", + "sha256": "6aff37f66695e1d2a7441bf7b361f7980bc10f6951f915713f55a01e5b00da48" + }, + { + "path": "outputs/simulator-integrated-review/checked-negativo/negativo.pdf", + "sha256": "3a2577194bc6c9e7234b650e69f3795f6cef6aef41d62658839c0790989a8f5c" + }, + { + "path": "outputs/simulator-integrated-review/nulo.docx", + "sha256": "efb83aa9044e35515880e40641d6bea52c6ea6f81289c3abc9d3bdbc76a9ba26" + }, + { + "path": "outputs/simulator-integrated-review/nulo-model.json", + "sha256": "94bbc6cf72cc613ebcd5ab9e9c074220b2f50dfb5b03959e92a7c84032a86750" + }, + { + "path": "outputs/simulator-integrated-review/checked-nulo/nulo.pdf", + "sha256": "c93e989a7af230e56f5d01766a4e386161e6fbbfa99341d5bfa0569bafe9e18e" + }, + { + "path": "outputs/simulator-integrated-review/incompleto.docx", + "sha256": "026f0ff1877ed5f23359d220e3223a319c213e742ff9704d81812fc2ed3ae20e" + }, + { + "path": "outputs/simulator-integrated-review/incompleto-model.json", + "sha256": "748b9cecde2acc99d7e2bbfd16188bf9f9ac0ab411ad850ba687c6e1f2e36cf5" + }, + { + "path": "outputs/simulator-integrated-review/checked-incompleto/incompleto.pdf", + "sha256": "0daac41c80ae131df6d66b7b542353a61a804d02d2e77769b9e5cc2bc9cfbc88" + }, + { + "path": "outputs/simulator-integrated-review/completed-calculation.jpg", + "sha256": "7180ff0f536e2e377c2a28b85574ec7c7fefcce5eed28ed08f07628b16870293" + } + ], + "checks": { + "targeted_frontend": 34, + "typescript": "passed", + "php_lint": "passed", + "build": "passed", + "reports_pages": { + "positivo": 6, + "negativo": 6, + "nulo": 6, + "incompleto": 5 + }, + "tax_lookup_retry": "503 then successful lookup; IVA; 2026-01-01 through 2026-12-31; indeterminate parameters", + "arithmetic_ui": "100*16/100*(1-25/100)=12; base change removes result", + "build_manifest_sha256": "af96a5f5682dad7a741d0ee8b302d8dc7f4d08a1806bc000265bd2602e5bfc61", + "terminal_status_ui": "200*16/100*(1-25/100)=24; completed notice visible; awaiting-calculation notice absent" + }, + "remaining_hours": [ + 6, + 10 + ], + "limitations": [ + "No published fiscal rule versions in QA; no legal tax validation", + "Authenticated cross-dossier and new-session frontend delta still pending", + "Parameter-version change and additional business-plan/cash-flow controls pending", + "PDF examples are DOCX rendering; native Edge print and final UI download comparison pending", + "Dynamic backend/catalogue messages and directed save/export error checks remain open" + ], + "deployment": { + "authorised_now": false, + "migrations": [], + "locale_on_future_deploy": "APP_LOCALE=pt; preserve existing secrets" + } +} diff --git a/docs/investmoz/unification/traceability-and-manual.md b/docs/investmoz/unification/traceability-and-manual.md index 89371e28..bb240277 100644 --- a/docs/investmoz/unification/traceability-and-manual.md +++ b/docs/investmoz/unification/traceability-and-manual.md @@ -1569,3 +1569,28 @@ Provas reutilizáveis identificadas: `SimulationExecutionTest`, `SimulationDraft Esforço inicial para este recorte: 18–30 h: dados/cálculos 6–10; isolamento/continuidade 5–8; relatórios/erros 4–7; idioma 3–5. Estimativa de engenharia, não tempo já executado nem desenvolvimento fiscal novo. Reestimar após a reconciliação das provas e parâmetros efectivos; fontes/catálogos realmente ausentes ficam separados e não impedem cálculos independentes que disponham dos dados necessários. Nenhuma nova função organizacional exigida. Correcções iniciais verificadas: 38 testes frontend dirigidos passaram, TypeScript passou. Sem ensaio remoto, SQL de alteração, novas migrations ou mudança do motor fiscal. A revisão integral permanece aberta. Roteiro Tempo, após completar as verificações: escolher contexto → preencher/rever dados → calcular → guardar/recuperar → alterar e recalcular → comparar e exportar. Os relatórios finais e a matriz problema/causa/correcção/prova/pendência serão complementados nesta mesma secção, sem outro backlog. + +### Simulador integrado — saldo da verificação QA, 20/10/2026 + +Continuação de `59ab4fe95635`, exclusivamente na branch `codex/simulator-integrated-review`, baseada no editorial `e69ab149`. Não houve integração em main, instalação remota, migrations, importação ou activação de parâmetros. A base utilizada foi `opportunity_search_20261020_test`, porta 55441, com guarda de isolamento. Os exemplos são sintéticos e explicitamente identificados. + +| Item | Resultado e causa/correcção | Prova e saldo concreto | +| --- | --- | --- | +| AUD-SIM-INT01 | Data pontual inválida passa a produzir erro em `fiscal_reference_date`, sem período alternativo. | Testes de vazio, data impossível e ano incompatível; navegador mostrou mensagem junto ao campo, `aria-invalid` e descrição associada. Encerrado este delta. | +| AUD-SIM-INT02 | Inventário efectivo: zero versões de regras fiscais na base QA. As seis fontes territoriais existentes não fornecem taxas ao motor. | Consulta IVA/2026 devolveu indeterminado, nunca zero. Fórmula executada é apenas base × taxa/100 × (1−redução/100), `portal-arithmetic-v1`, valores declarados, sem moeda, regra fiscal ou interpretação legal. A data de execução não é período de vigência fiscal. Fontes oficiais mantêm a aceitação; não se pediu nova autenticação da proveniência. Parâmetros fiscais ausentes ficam indisponíveis, não estimados pela aplicação. | +| AUD-SIM-INT03 | Remontagem do formulário por conta/contexto/rascunho para não reutilizar estado de outro âmbito. Recuperação do rascunho QA observada após reload. | Provas backend anteriores de isolamento mantidas; falta a conferência positiva do delta frontend com dois dossiers e nova sessão autenticada. Reload não foi contado como nova sessão. | +| AUD-SIM-INT04 | Alterar uma entrada retira o resultado actual e mostra aviso de recálculo; não escreve sobre snapshots históricos. | Navegador calculou 12 e, ao alterar base 100→200, retirou o resultado e mostrou o aviso. Versões/hashes backend preservados. Falta concluir a conferência positiva do histórico/contexto no consumidor autenticado e da mudança de versão de parâmetros. | +| AUD-SIM-INT05 | Controlos independentes por livro de caixa, Decimal e recorrência mensal/bissecção para o crédito. | Saldo mensal +100/−100/0; excedente de fundos −200; equilíbrio 11 unidades; crédito sem juros 100 por mês; nominal 12%/ano, 1000/12 meses: 88,85 na primeira prestação; campo vazio recusado. Cenário personalizado 100×16%×75%=12 confirmado no servidor. São controlos matemáticos, não validação de taxas legais. Restam controlos delimitados de cash-flow/plano de negócio. | +| AUD-SIM-INT06 | Resumo do relatório passou a apresentar os resultados financeiros do mesmo modelo de detalhe. Secção sem anexos ficou compacta, eliminando uma página quase vazia. | Quatro exemplos completos pelo exportador real: positivo, negativo, nulo (6 páginas cada), incompleto (5). Todas as páginas DOCX renderizadas conferidas; testes comparam resumo/detalhe. PDF disponibilizado é a renderização do DOCX, não prova da impressão nativa do Edge. Falta essa impressão e a comparação final ecrã/download pela interface. | +| AUD-SIM-INT07 | Removidos requisitos de residência/documentação que não entram no cenário percentual personalizado; mantida validação específica de base/taxa/redução. Consulta fiscal tem nova tentativa e ignora respostas abortadas. Estado de espera é substituído pelo estado terminal do cálculo. | Cenário personalizado calculou sem esses campos; erro 503 sintético na consulta mostrou mensagem e Tentar novamente; repetição preservou IVA/2026 e devolveu indeterminado por falta de parâmetro. Falhas de gravação/exportação ainda carecem de conferência dirigida dos consumidores afectados. | +| AUD-SIM-INT08 | Traduzidos textos de autenticação, perfil, preferências, MFA/chaves, diálogos, notificações e validações comuns, preservando códigos e mecanismos. | Inventário de 4519 textos/atributos JSX literais em todos os TSX; três candidatos restantes são português com a palavra continue. Isto não cobre todas as mensagens dinâmicas do backend ou do catálogo. Inglês explicitamente seleccionável no formulário bilingue e títulos de fontes preservados. Falta classificar o inventário dinâmico e conferir mensagens residuais em execução. | + +Operação de idioma para uma eventual disponibilização: usar `APP_LOCALE=pt` na configuração nominal. O fallback de `config/app.php` passou a pt, mas não substitui um valor explícito do ambiente; não copiar `.env` QA. Foram adicionados `lang/pt/auth.php`, `lang/pt/validation.php` e `lang/pt.json`; as regras comuns cobertas não equivalem ao catálogo completo de validações Laravel. + +Complemento do inventário: os 77 candidatos de literais TS/TSX restantes foram classificados como classes CSS internas ou traduções explicitamente seleccionáveis (incluindo menus, formulário bilingue e assistência, cuja activação não foi alterada). Foram ainda corrigidos o breadcrumb de segurança e as três mensagens de falha no carregamento de QR/chave/códigos. O fecho desse levantamento estático não certifica todas as mensagens produzidas por backend, conteúdo e serviços em execução; esse é o saldo de idioma ainda aberto. + +Provas novas: 34 testes dirigidos de formulário/relatório passaram; TypeScript, build e lint PHP dos ficheiros de idioma/configuração passaram. Mantidas as provas anteriores não afectadas de contratos, APIs, isolamento e versões; nenhum RefreshDatabase executado sobre o acervo QA. Artefactos locais: `outputs/simulator-integrated-review/` (inventário efectivo, controlos independentes, modelos dos quatro relatórios, DOCX/HTML e `checked-*` com PDF/páginas conferidas). As pastas anteriores `render-*`/`final-*` são intermédias e não são as versões entregues. + +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. diff --git a/docs/investmoz/v2/auditoria-portal/backlog-priorizado.csv b/docs/investmoz/v2/auditoria-portal/backlog-priorizado.csv index dff65c46..c030f1eb 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,Fixed in QA; invalid-date checks; 38 directed frontend tests passed,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,,,,Fixed in QA; invalid-date checks; 38 directed frontend tests passed,,,,No claim of full review completion, -P1,AUD-SIM-INT02,Verification pending; no parameter activation,Review actual fiscal parameters sources periods and eligibility,,,,,,,,,simulator and shared language,Tempo request 20/10/2026; same manual,,,,,No new activation authorised,,,,Verification pending; no parameter activation,,,,No claim of full review completion, -P1,AUD-SIM-INT03,Existing controls and accepted proofs to reconcile; no leak established,Review isolation recovery across projects and sessions,,,,,,,,,simulator and shared language,Tempo request 20/10/2026; same manual,,,,,No new activation authorised,,,,Existing controls and accepted proofs to reconcile; no leak established,,,,No claim of full review completion, -P1,AUD-SIM-INT04,Existing versions/fingerprints; targeted verification pending,Review stale and historical results and correct dossier,,,,,,,,,simulator and shared language,Tempo request 20/10/2026; same manual,,,,,No new activation authorised,,,,Existing versions/fingerprints; targeted verification pending,,,,No claim of full review completion, -P1,AUD-SIM-INT05,Reconciliation pending; no invented tax assumptions,Independent financial controls unknown zero units and currencies,,,,,,,,,simulator and shared language,Tempo request 20/10/2026; same manual,,,,,No new activation authorised,,,,Reconciliation pending; no invented tax assumptions,,,,No claim of full review completion, -P1,AUD-SIM-INT06,Existing exporters; examples for this review still pending,Generated reports screen download and pagination,,,,,,,,,simulator and shared language,Tempo request 20/10/2026; same manual,,,,,No new activation authorised,,,,Existing exporters; examples for this review still pending,,,,No claim of full review completion, -P1,AUD-SIM-INT07,Existing idempotency; consumer-level verification pending,Explicit validate calculate save export errors and retry,,,,,,,,,simulator and shared language,Tempo request 20/10/2026; same manual,,,,,No new activation authorised,,,,Existing idempotency; consumer-level verification pending,,,,No claim of full review completion, -P1,AUD-SIM-INT08,"Partial QA fix: rate labels, password confirmation/security and spinner; wider inventory pending",Portuguese across portal and simulator outputs,,,,,,,,,simulator and shared language,Tempo request 20/10/2026; same manual,,,,,No new activation authorised,,,,"Partial QA fix: rate labels, password confirmation/security and spinner; wider inventory pending",,,,No claim of full review completion, +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, diff --git a/docs/investmoz/v2/auditoria-portal/backlog.md b/docs/investmoz/v2/auditoria-portal/backlog.md index 36832f22..fee32b81 100644 --- a/docs/investmoz/v2/auditoria-portal/backlog.md +++ b/docs/investmoz/v2/auditoria-portal/backlog.md @@ -3002,3 +3002,5 @@ AUD-GUIDE-CARD-20261020: componente comum com imagem autorizada, título, resumo ## Integrated simulator review - QA 20/10/2026 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. diff --git a/docs/investmoz/v2/auditoria-portal/matriz-requisitos.csv b/docs/investmoz/v2/auditoria-portal/matriz-requisitos.csv index 066bde5c..eb244868 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,Initial diagnosis; integral review open,,,,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,Initial diagnosis; integral review open,,,,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,Initial diagnosis; integral review open,,,,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,Initial diagnosis; integral review open,,,,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,Initial diagnosis; integral review open,,,,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,Initial diagnosis; integral review open,,,,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,Initial diagnosis; integral review open,,,,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",Initial diagnosis; integral review open,,,,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,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,,,,,,,,,,,,,, diff --git a/lang/pt.json b/lang/pt.json new file mode 100644 index 00000000..0e92bdd6 --- /dev/null +++ b/lang/pt.json @@ -0,0 +1,6 @@ +{ + "The provided two factor authentication code was invalid.": "O código de autenticação está incorrecto ou expirou. Introduza um novo código da sua aplicação autenticadora.", + "The provided two factor recovery code was invalid.": "O código de recuperação está incorrecto ou já foi utilizado.", + "The provided password is incorrect.": "A palavra-passe está incorrecta.", + "The provided password does not match your current password.": "A palavra-passe indicada não corresponde à palavra-passe actual." +} diff --git a/lang/pt/auth.php b/lang/pt/auth.php new file mode 100644 index 00000000..483257bf --- /dev/null +++ b/lang/pt/auth.php @@ -0,0 +1,6 @@ + 'O email ou a palavra-passe não correspondem aos nossos registos.', + 'password' => 'A palavra-passe está incorrecta.', + 'throttle' => 'Foram feitas demasiadas tentativas. Tente novamente dentro de :seconds segundos.', +]; diff --git a/lang/pt/validation.php b/lang/pt/validation.php new file mode 100644 index 00000000..efc95c29 --- /dev/null +++ b/lang/pt/validation.php @@ -0,0 +1,29 @@ + 'O campo :attribute é obrigatório.', + 'required_if' => 'O campo :attribute é obrigatório quando :other tem o valor :value.', + 'present' => 'O campo :attribute deve estar presente.', + 'filled' => 'Preencha o campo :attribute.', + 'numeric' => 'O campo :attribute deve conter um número.', + 'integer' => 'O campo :attribute deve conter um número inteiro.', + 'decimal' => 'O campo :attribute deve ter :decimal casas decimais.', + 'string' => 'O campo :attribute deve conter texto.', + 'array' => 'O campo :attribute deve conter uma lista.', + 'boolean' => 'O campo :attribute deve indicar sim ou não.', + 'email' => 'Introduza um endereço de correio electrónico válido em :attribute.', + 'uuid' => 'A referência indicada em :attribute não é válida.', + 'in' => 'Seleccione uma opção válida em :attribute.', + 'exists' => 'A referência seleccionada em :attribute não foi encontrada.', + 'unique' => 'O valor indicado em :attribute já está registado.', + 'confirmed' => 'A confirmação de :attribute não corresponde ao valor introduzido.', + 'current_password' => 'A palavra-passe actual está incorrecta.', + 'date' => 'Introduza uma data válida em :attribute.', + 'date_format' => 'Introduza uma data válida para :attribute.', + 'before_or_equal' => 'A data em :attribute deve ser anterior ou igual a :date.', + 'after_or_equal' => 'A data em :attribute deve ser posterior ou igual a :date.', + 'accepted' => 'Confirme o campo :attribute para continuar.', + 'between' => ['numeric' => 'O valor de :attribute deve estar entre :min e :max.', 'string' => 'O campo :attribute deve ter entre :min e :max caracteres.', 'array' => 'O campo :attribute deve ter entre :min e :max elementos.', 'file' => 'O ficheiro :attribute deve ter entre :min e :max KB.'], + 'min' => ['numeric' => 'O valor de :attribute deve ser igual ou superior a :min.', 'string' => 'O campo :attribute deve ter pelo menos :min caracteres.', 'array' => 'O campo :attribute deve ter pelo menos :min elementos.', 'file' => 'O ficheiro :attribute deve ter pelo menos :min KB.'], + 'max' => ['numeric' => 'O valor de :attribute não pode exceder :max.', 'string' => 'O campo :attribute não pode exceder :max caracteres.', 'array' => 'O campo :attribute não pode exceder :max elementos.', 'file' => 'O ficheiro :attribute não pode exceder :max KB.'], + 'attributes' => ['base' => 'base de cálculo', 'rate' => 'taxa', 'reduction' => 'redução', 'email' => 'email', 'password' => 'palavra-passe', 'current_password' => 'palavra-passe actual', 'economic_snapshot.fiscal_reference_date' => 'data da operação fiscal'], +]; diff --git a/resources/js/components/appearance-tabs.tsx b/resources/js/components/appearance-tabs.tsx index b0138624..b6ac042c 100644 --- a/resources/js/components/appearance-tabs.tsx +++ b/resources/js/components/appearance-tabs.tsx @@ -12,9 +12,9 @@ export default function AppearanceToggleTab({ const { appearance, updateAppearance } = useAppearance(); const tabs: { value: Appearance; icon: LucideIcon; label: string }[] = [ - { value: 'light', icon: Sun, label: 'Light' }, - { value: 'dark', icon: Moon, label: 'Dark' }, - { value: 'system', icon: Monitor, label: 'System' }, + { value: 'light', icon: Sun, label: 'Claro' }, + { value: 'dark', icon: Moon, label: 'Escuro' }, + { value: 'system', icon: Monitor, label: 'Sistema' }, ]; return ( diff --git a/resources/js/components/delete-user.tsx b/resources/js/components/delete-user.tsx index 213e76f1..bbb9fbda 100644 --- a/resources/js/components/delete-user.tsx +++ b/resources/js/components/delete-user.tsx @@ -23,14 +23,16 @@ export default function DeleteUser() {

Warning

- Please proceed with caution, this cannot be undone. + {' '} + Esta operação é irreversível. Confirme os dados antes de + continuar.{' '}

@@ -40,18 +42,20 @@ export default function DeleteUser() { variant="destructive" data-test="delete-user-button" > - Delete account + {' '} + Eliminar conta{' '} - Are you sure you want to delete your account? + {' '} + Pretende eliminar a sua conta?{' '} - Once your account is deleted, all of its resources - and data will also be permanently deleted. Please - enter your password to confirm you would like to - permanently delete your account. + {' '} + Ao eliminar a conta, os recursos e dados associados + também serão eliminados permanentemente. Introduza a + sua palavra-passe para confirmar.{' '}
- Password + {' '} + Palavra-passe{' '} @@ -92,7 +97,8 @@ export default function DeleteUser() { resetAndClearErrors() } > - Cancel + {' '} + Cancelar{' '} @@ -105,7 +111,8 @@ export default function DeleteUser() { type="submit" data-test="confirm-delete-user-button" > - Delete account + {' '} + Eliminar conta{' '} diff --git a/resources/js/components/manage-passkeys.tsx b/resources/js/components/manage-passkeys.tsx index 41629595..e8c0aead 100644 --- a/resources/js/components/manage-passkeys.tsx +++ b/resources/js/components/manage-passkeys.tsx @@ -17,9 +17,10 @@ const EmptyState = () => {
-

No passkeys yet

+

Ainda não existem chaves de acesso

- Add a passkey to sign in without a password + {' '} + Adicione uma chave de acesso para entrar sem palavra-passe{' '}

); @@ -47,8 +48,8 @@ export default function ManagePasskeys(props: Props) {
diff --git a/resources/js/components/manage-two-factor.tsx b/resources/js/components/manage-two-factor.tsx index 9fb5370a..bd91cefc 100644 --- a/resources/js/components/manage-two-factor.tsx +++ b/resources/js/components/manage-two-factor.tsx @@ -48,15 +48,15 @@ export default function ManageTwoFactor(props: Props) {
{twoFactorEnabled ? (

- You will be prompted for a secure, random pin during - login, which you can retrieve from the TOTP-supported - application on your phone. + {' '} + Ao entrar, será solicitado o código temporário da + aplicação autenticadora do seu telefone.{' '}

@@ -67,7 +67,9 @@ export default function ManageTwoFactor(props: Props) { type="submit" disabled={processing} > - Disable 2FA + {' '} + Desactivar autenticação de dois + factores{' '} )} @@ -82,17 +84,16 @@ export default function ManageTwoFactor(props: Props) { ) : (

- When you enable two-factor authentication, you will be - prompted for a secure pin during login. This pin can be - retrieved from a TOTP-supported application on your - phone. + {' '} + Depois de activar a autenticação de dois factores, use o + código temporário da aplicação autenticadora para + entrar.{' '}

{hasSetupData ? ( ) : (
{({ processing }) => ( )}
diff --git a/resources/js/components/passkey-item.tsx b/resources/js/components/passkey-item.tsx index d1918603..a25eea57 100644 --- a/resources/js/components/passkey-item.tsx +++ b/resources/js/components/passkey-item.tsx @@ -43,13 +43,14 @@ export default function PasskeyItem({ passkey, onDelete }: Props) { )}

- Added {passkey.created_at_diff} + {' '} + Adicionada em {passkey.created_at_diff} {passkey.last_used_at_diff && ( <> / - - Last used {passkey.last_used_at_diff} + {' '} + Última utilização {passkey.last_used_at_diff} )}

@@ -64,19 +65,18 @@ export default function PasskeyItem({ passkey, onDelete }: Props) { className="text-destructive hover:bg-destructive/10 hover:text-destructive" > - Remove + Remover - Remove passkey + Remover chave de acesso - Are you sure you want to remove the "{passkey.name}" - passkey? You will no longer be able to use it to sign - in. + Pretende remover a chave de acesso «{passkey.name}»? + Deixará de poder usá-la para entrar. - +
); } @@ -67,7 +68,8 @@ export default function PasskeyRegistration({ onSuccess }: Props) { if (!showForm) { return ( ); } @@ -78,18 +80,19 @@ export default function PasskeyRegistration({ onSuccess }: Props) { className="border-border bg-muted/50 space-y-4 rounded-lg border p-4" >
- + setName(e.target.value)} - placeholder="e.g., MacBook Pro, iPhone" + placeholder="Por exemplo, MacBook Pro ou iPhone" className="border-foreground/20 mt-1 block w-full" autoFocus />

- A name helps you identify this passkey later. + {' '} + O nome permite identificar esta chave mais tarde.{' '}

@@ -97,10 +100,11 @@ export default function PasskeyRegistration({ onSuccess }: Props) {
diff --git a/resources/js/components/passkey-verify.tsx b/resources/js/components/passkey-verify.tsx index adb714f1..c46fb278 100644 --- a/resources/js/components/passkey-verify.tsx +++ b/resources/js/components/passkey-verify.tsx @@ -51,8 +51,8 @@ export default function PasskeyVerify({ > {isLoading ? : } {isLoading - ? (loadingLabel ?? 'Authenticating...') - : (label ?? 'Sign in with a passkey')} + ? (loadingLabel ?? 'A autenticar…') + : (label ?? 'Entrar com uma chave de acesso')} {error && ( @@ -65,7 +65,7 @@ export default function PasskeyVerify({
- {separator ?? 'Or continue with email'} + {separator ?? 'Ou continuar com email'}
diff --git a/resources/js/components/simulation-fiscal-status.tsx b/resources/js/components/simulation-fiscal-status.tsx index 7a4797db..846a3b52 100644 --- a/resources/js/components/simulation-fiscal-status.tsx +++ b/resources/js/components/simulation-fiscal-status.tsx @@ -66,6 +66,7 @@ export function SimulationFiscalStatus({ }) { const [parameters, setParameters] = useState([]); const [requestState, setRequestState] = useState('idle'); + const [retry, setRetry] = useState(0); useEffect(() => { const apiComponent = component @@ -104,12 +105,14 @@ export function SimulationFiscalStatus({ const payload = (await response.json()) as { parameters?: Parameter[]; }; + if (abort.signal.aborted) return; setParameters( Array.isArray(payload.parameters) ? payload.parameters : [], ); setRequestState('done'); }) .catch((error: unknown) => { + if (abort.signal.aborted) return; if ( error instanceof DOMException && error.name === 'AbortError' @@ -125,6 +128,7 @@ export function SimulationFiscalStatus({ periodStart, periodEnd, JSON.stringify(context ?? {}), + retry, ]); const salaryViewMessages: Record = { @@ -160,6 +164,16 @@ export function SimulationFiscalStatus({ : `Período consultado: ${periodStart} a ${periodEnd}.`}

)} + {requestState === 'error' && + scenarioMode === 'approved_parameters' && ( + + )} {scenarioMode === 'custom_planning' && (

Modo de planeamento separado: pressupostos do utilizador, diff --git a/resources/js/components/simulation-form.tsx b/resources/js/components/simulation-form.tsx index a97320e3..71fcc2bd 100644 --- a/resources/js/components/simulation-form.tsx +++ b/resources/js/components/simulation-form.tsx @@ -864,6 +864,10 @@ export function SimulationForm() { const [isCalculating, setIsCalculating] = useState(false); const [hasSaveConflict, setHasSaveConflict] = useState(false); const [result, setResult] = useState(null); + const [calculationStale, setCalculationStale] = useState(false); + useEffect(() => { + if (result) setCalculationStale(false); + }, [result]); const arithmeticResult = useMemo( () => readArithmeticSnapshot(result?.result_snapshot), [result], @@ -1039,6 +1043,13 @@ export function SimulationForm() { : (simulationFieldGroups[current.id] ?? []); const fields = fieldsForCurrent.filter( (field) => + !( + current.id === 'tax' && + draft.fiscal_scenario_mode === 'custom_planning' && + !['fiscal_scenario_mode', 'base', 'rate', 'reduction'].includes( + field.key, + ) + ) && (!field.showWhen || field.showWhen(draft)) && !( draft.purpose === 'simulate_only' && @@ -1943,7 +1954,8 @@ export function SimulationForm() { return next; }); if (key === 'purpose') setStep(0); - if (key !== 'purpose') setResult(null); + if (result) setCalculationStale(true); + setResult(null); } async function clearDraft() { @@ -2274,6 +2286,18 @@ export function SimulationForm() { ) setResult(simulation ?? null); if (canSavePersonalData) void loadSimulationHistory(); + if (simulation?.status === 'completed') { + setSaveStatus( + 'Cálculo concluído. Reveja o resultado e os pressupostos.', + ); + } else if ( + simulation?.status === 'failed' || + simulation?.status === 'cancelled' + ) { + setSaveStatus( + 'O cálculo não foi concluído. Os dados introduzidos foram preservados.', + ); + } sessionStorage.removeItem(idempotencyStorageKey); } catch (cause) { setServerError( @@ -2396,6 +2420,16 @@ export function SimulationForm() { aria-labelledby="simulator-step-title" aria-busy={!ready} > + {calculationStale && ( +

+ As entradas foram alteradas. Calcule novamente antes de + guardar ou exportar um novo resultado. Os resultados + anteriores mantêm-se no histórico. +

+ )} {!ready && (

A recuperar o rascunho. Aguarde para editar. diff --git a/resources/js/components/two-factor-recovery-codes.tsx b/resources/js/components/two-factor-recovery-codes.tsx index df51ca22..7c716bee 100644 --- a/resources/js/components/two-factor-recovery-codes.tsx +++ b/resources/js/components/two-factor-recovery-codes.tsx @@ -56,12 +56,14 @@ export default function TwoFactorRecoveryCodes({ - - Recovery codes let you regain access if you lose your 2FA - device. Store them in a secure password manager. + {' '} + Os códigos de recuperação permitem recuperar o acesso se + perder o dispositivo autenticador. Guarde-os num gestor de + palavras-passe seguro.{' '} @@ -76,7 +78,8 @@ export default function TwoFactorRecoveryCodes({ className="size-4" aria-hidden="true" /> - {codesAreVisible ? 'Hide' : 'View'} recovery codes + {codesAreVisible ? 'Ocultar' : 'Ver'} códigos de + recuperação{' '} {canRegenerateCodes && ( @@ -92,7 +95,7 @@ export default function TwoFactorRecoveryCodes({ disabled={processing} aria-describedby="regenerate-warning" > - Regenerate codes + Gerar novos códigos{' '} )} @@ -112,7 +115,7 @@ export default function TwoFactorRecoveryCodes({ ref={codesSectionRef} className="bg-muted grid gap-1 rounded-lg p-4 font-mono text-sm" role="list" - aria-label="Recovery codes" + aria-label="Códigos de recuperação" > {recoveryCodesList.length ? ( recoveryCodesList.map((code, index) => ( @@ -127,7 +130,7 @@ export default function TwoFactorRecoveryCodes({ ) : (

{Array.from( { length: 8 }, @@ -145,13 +148,15 @@ export default function TwoFactorRecoveryCodes({

- Each recovery code can be used once to - access your account and will be removed - after use. If you need more, click{' '} + {' '} + Cada código permite uma única entrada e + deixa de ser válido após a utilização. + Para obter novos códigos, seleccione{' '} - Regenerate codes + {' '} + Gerar novos códigos{' '} {' '} - above. + acima.{' '}

diff --git a/resources/js/components/two-factor-setup-modal.tsx b/resources/js/components/two-factor-setup-modal.tsx index 903a7894..aedecddf 100644 --- a/resources/js/components/two-factor-setup-modal.tsx +++ b/resources/js/components/two-factor-setup-modal.tsx @@ -104,7 +104,8 @@ function TwoFactorSetupStep({
- or, enter the code manually + {' '} + ou introduza a chave manualmente{' '}
@@ -210,7 +211,8 @@ function TwoFactorVerificationStep({ onClick={onBack} disabled={processing} > - Back + {' '} + Voltar{' '}
@@ -262,27 +265,27 @@ export default function TwoFactorSetupModal({ }>(() => { if (twoFactorEnabled) { return { - title: 'Two-factor authentication enabled', + title: 'Autenticação de dois factores activada', description: - 'Two-factor authentication is now enabled. Scan the QR code or enter the setup key in your authenticator app.', - buttonText: 'Close', + 'A autenticação de dois factores está activada. Leia o código QR ou introduza a chave na aplicação autenticadora.', + buttonText: 'Fechar', }; } if (showVerificationStep) { return { - title: 'Verify authentication code', + title: 'Confirmar código de autenticação', description: - 'Enter the 6-digit code from your authenticator app', - buttonText: 'Continue', + 'Introduza o código de seis dígitos da aplicação autenticadora', + buttonText: 'Continuar', }; } return { - title: 'Enable two-factor authentication', + title: 'Activar autenticação de dois factores', description: - 'To finish enabling two-factor authentication, scan the QR code or enter the setup key in your authenticator app', - buttonText: 'Continue', + 'Para concluir a configuração, leia o código QR ou introduza a chave na aplicação autenticadora.', + buttonText: 'Continuar', }; }, [twoFactorEnabled, showVerificationStep]); diff --git a/resources/js/components/ui/dialog.tsx b/resources/js/components/ui/dialog.tsx index 5f5e1c28..28fd7678 100644 --- a/resources/js/components/ui/dialog.tsx +++ b/resources/js/components/ui/dialog.tsx @@ -63,7 +63,7 @@ function DialogContent({ {children} - Close + Fechar diff --git a/resources/js/components/ui/sheet.tsx b/resources/js/components/ui/sheet.tsx index ccc11c46..de29a9ac 100644 --- a/resources/js/components/ui/sheet.tsx +++ b/resources/js/components/ui/sheet.tsx @@ -72,7 +72,7 @@ function SheetContent({ {children} - Close + Fechar diff --git a/resources/js/components/user-menu-content.tsx b/resources/js/components/user-menu-content.tsx index c3950ed1..ff1c5b93 100644 --- a/resources/js/components/user-menu-content.tsx +++ b/resources/js/components/user-menu-content.tsx @@ -40,8 +40,7 @@ export function UserMenuContent({ user }: Props) { prefetch onClick={cleanup} > - - Settings + Definições{' '} diff --git a/resources/js/hooks/use-two-factor-auth.ts b/resources/js/hooks/use-two-factor-auth.ts index c89adcd3..ed6b1416 100644 --- a/resources/js/hooks/use-two-factor-auth.ts +++ b/resources/js/hooks/use-two-factor-auth.ts @@ -55,7 +55,10 @@ export const useTwoFactorAuth = (): UseTwoFactorAuthReturn => { setQrCodeSvg(svg); } catch { - setErrors((prev) => [...prev, 'Failed to fetch QR code']); + setErrors((prev) => [ + ...prev, + 'Não foi possível carregar o código QR.', + ]); setQrCodeSvg(null); } }, [submit]); @@ -68,7 +71,10 @@ export const useTwoFactorAuth = (): UseTwoFactorAuthReturn => { setManualSetupKey(key); } catch { - setErrors((prev) => [...prev, 'Failed to fetch a setup key']); + setErrors((prev) => [ + ...prev, + 'Não foi possível carregar a chave de configuração.', + ]); setManualSetupKey(null); } }, [submit]); @@ -79,7 +85,10 @@ export const useTwoFactorAuth = (): UseTwoFactorAuthReturn => { const codes = (await submit(recoveryCodes())) as string[]; setRecoveryCodesList(codes); } catch { - setErrors((prev) => [...prev, 'Failed to fetch recovery codes']); + setErrors((prev) => [ + ...prev, + 'Não foi possível carregar os códigos de recuperação.', + ]); setRecoveryCodesList([]); } }, [submit]); diff --git a/resources/js/layouts/settings/layout.tsx b/resources/js/layouts/settings/layout.tsx index efd79042..36d947ec 100644 --- a/resources/js/layouts/settings/layout.tsx +++ b/resources/js/layouts/settings/layout.tsx @@ -12,17 +12,17 @@ import type { NavItem } from '@/types'; const sidebarNavItems: NavItem[] = [ { - title: 'Profile', + title: 'Perfil', href: edit(), icon: null, }, { - title: 'Security', + title: 'Segurança', href: editSecurity(), icon: null, }, { - title: 'Appearance', + title: 'Aparência', href: editAppearance(), icon: null, }, @@ -34,15 +34,15 @@ export default function SettingsLayout({ children }: PropsWithChildren) { return (
@@ -64,6 +67,7 @@ export default function ForgotPassword({ status }: { status?: string }) { } ForgotPassword.layout = { - title: 'Forgot password', - description: 'Enter your email to receive a password reset link', + title: 'Recuperar palavra-passe', + description: + 'Indique o seu email para receber a ligação de recuperação da palavra-passe', }; diff --git a/resources/js/pages/auth/login.tsx b/resources/js/pages/auth/login.tsx index 06c1b855..20741e7a 100644 --- a/resources/js/pages/auth/login.tsx +++ b/resources/js/pages/auth/login.tsx @@ -39,15 +39,19 @@ export default function Login({ status, canResetPassword, qaAccount }: Props) { Ambiente QA — conta {qaAccount.id}

- Para os cenários A/B: {qaAccount.email}. Use a password - e o autenticador desta conta. Outros perfis devem usar - as respetivas contas sintéticas QA. + Para os cenários A/B: {qaAccount.email}. Use a + palavra-passe e o autenticador desta conta. Outros + perfis devem usar as respetivas contas sintéticas QA.

)} {/* @chisel-passkeys */} - + {/* @end-chisel-passkeys */}
- + {canResetPassword && ( - +
)} @@ -91,6 +92,6 @@ export default function ResetPassword({ token, email, passwordRules }: Props) { } ResetPassword.layout = { - title: 'Reset password', - description: 'Please enter your new password below', + title: 'Redefinir palavra-passe', + description: 'Introduza a nova palavra-passe', }; diff --git a/resources/js/pages/auth/two-factor-challenge.tsx b/resources/js/pages/auth/two-factor-challenge.tsx index fe8b5864..9799b5cf 100644 --- a/resources/js/pages/auth/two-factor-challenge.tsx +++ b/resources/js/pages/auth/two-factor-challenge.tsx @@ -27,18 +27,18 @@ export default function TwoFactorChallenge({ }>(() => { if (showRecoveryInput) { return { - title: 'Recovery code', + title: 'Código de recuperação', description: - 'Please confirm access to your account by entering one of your emergency recovery codes.', - toggleText: 'login using an authentication code', + 'Confirme o acesso à conta com um dos seus códigos de recuperação.', + toggleText: 'entrar com um código de autenticação', }; } return { - title: 'Authentication code', + title: 'Código de autenticação', description: - 'Enter the authentication code provided by your authenticator application.', - toggleText: 'login using a recovery code', + 'Introduza o código apresentado pela sua aplicação autenticadora.', + toggleText: 'entrar com um código de recuperação', }; }, [showRecoveryInput]); @@ -55,7 +55,7 @@ export default function TwoFactorChallenge({ return ( <> - + {qaAccount && (

@@ -123,7 +123,8 @@ export default function TwoFactorChallenge({ className="w-full" disabled={processing} > - Continue + {' '} + Continuar{' '}

diff --git a/resources/js/pages/auth/verify-email.tsx b/resources/js/pages/auth/verify-email.tsx index a6fee14c..d558eec4 100644 --- a/resources/js/pages/auth/verify-email.tsx +++ b/resources/js/pages/auth/verify-email.tsx @@ -9,12 +9,13 @@ import { send } from '@/routes/verification'; export default function VerifyEmail({ status }: { status?: string }) { return ( <> - + {status === 'verification-link-sent' && (
- A new verification link has been sent to the email address - you provided during registration. + {' '} + Foi enviada uma nova ligação de confirmação para o email + indicado no registo.{' '}
)} @@ -22,15 +23,16 @@ export default function VerifyEmail({ status }: { status?: string }) { {({ processing }) => ( <> - Log out + {' '} + Terminar sessão{' '} )} @@ -40,7 +42,6 @@ export default function VerifyEmail({ status }: { status?: string }) { } VerifyEmail.layout = { - title: 'Email verification', - description: - 'Please verify your email address by clicking on the link we just emailed to you.', + title: 'Confirmação de email', + description: 'Confirme o seu email através da ligação que enviámos.', }; diff --git a/resources/js/pages/portal/services.tsx b/resources/js/pages/portal/services.tsx index 98cff894..1b74e6ce 100644 --- a/resources/js/pages/portal/services.tsx +++ b/resources/js/pages/portal/services.tsx @@ -419,6 +419,9 @@ function PartnershipForm() { } function Simulator() { + const page = usePage<{ auth: { user: { id: number } | null } }>(); + const query = new URL(page.url, 'https://investmoz.invalid').searchParams; + const scope = `${page.props.auth.user?.id ?? 'anonymous'}:${query.get('context') ?? ''}:${query.get('draft') ?? ''}`; return (

@@ -428,7 +431,7 @@ function Simulator() { para rever mercado, localização, recursos, formalização e execução.

- +
); } diff --git a/resources/js/pages/settings/appearance.tsx b/resources/js/pages/settings/appearance.tsx index ca18d76b..302d9fe2 100644 --- a/resources/js/pages/settings/appearance.tsx +++ b/resources/js/pages/settings/appearance.tsx @@ -6,15 +6,15 @@ import { edit as editAppearance } from '@/routes/appearance'; export default function Appearance() { return ( <> - + -

Appearance settings

+

Definições de aparência

@@ -25,7 +25,7 @@ export default function Appearance() { Appearance.layout = { breadcrumbs: [ { - title: 'Appearance settings', + title: 'Definições de aparência', href: editAppearance(), }, ], diff --git a/resources/js/pages/settings/profile.tsx b/resources/js/pages/settings/profile.tsx index 064f300d..485c8a3f 100644 --- a/resources/js/pages/settings/profile.tsx +++ b/resources/js/pages/settings/profile.tsx @@ -34,15 +34,15 @@ export default function Profile( return ( <> - + -

Profile settings

+

Definições do perfil

( <>
- +
- +

- Your email address is unverified.{' '} + {' '} + O seu endereço de correio + electrónico ainda não foi + confirmado.{' '} - Click here to re-send the - verification email. + {' '} + Reenviar o email de + confirmação.{' '}

{status === 'verification-link-sent' && (
- A new verification link has been - sent to your email address. + {' '} + Foi enviada uma nova ligação de + confirmação para o seu + email.{' '}
)}
@@ -125,7 +134,8 @@ export default function Profile( disabled={processing} data-test="update-profile-button" > - Save + {' '} + Guardar{' '}
@@ -141,7 +151,7 @@ export default function Profile( Profile.layout = { breadcrumbs: [ { - title: 'Profile settings', + title: 'Definições do perfil', href: edit(), }, ], diff --git a/resources/js/pages/settings/security.tsx b/resources/js/pages/settings/security.tsx index 41d14dea..bc398ecb 100644 --- a/resources/js/pages/settings/security.tsx +++ b/resources/js/pages/settings/security.tsx @@ -151,7 +151,7 @@ export default function Security(props: Props) { Security.layout = { breadcrumbs: [ { - title: 'Security settings', + title: 'Definições de segurança', href: edit(), }, ], diff --git a/tests/frontend/project-dossier-report.test.mjs b/tests/frontend/project-dossier-report.test.mjs index 7dd8a309..8e253ccc 100644 --- a/tests/frontend/project-dossier-report.test.mjs +++ b/tests/frontend/project-dossier-report.test.mjs @@ -1,6 +1,7 @@ import test from 'node:test'; import assert from 'node:assert/strict'; import JSZip from 'jszip'; +import { serializeFinancialToolsSnapshot } from '../../resources/js/lib/financial-calculators.ts'; import { createProjectDossierPrintHtml, createProjectDossierReport, @@ -10,6 +11,17 @@ import { classifyDossierAttachment, validateDossierAttachments, } from '../../resources/js/lib/project-dossier-report.ts'; + +test('executive summary exposes the same financial result and period without replacing missing data by zero', () => { + const draft = { currency:'MZN', financial_tools_snapshot:serializeFinancialToolsSnapshot({budgetPeriod:'Mensal',loan:{principal:'1200',rate:'0',months:'12',rateType:'nominal',periodMonths:'1',gracePeriods:'0',upfrontFee:'0',recurringFee:'0'},breakEven:{fixed:'100.01',price:'30',variable:'20'},funding:{total:'1000',secured:'800'},incomes:[{label:'Receita QA',amount:'1000'}],expenses:[{label:'Despesa QA',amount:'1100'}]}) }; + const report = createProjectDossierReport(draft,{selectedSections:new Set(['summary','finance'])}); + const summary=report.sections.find(s=>s.id==='summary'); + const detail=report.sections.find(s=>s.id==='finance'); + assert.equal(summary.rows.find(r=>r.field.startsWith('Saldo operacional')).value,detail.rows.find(r=>r.field==='Saldo operacional estimado').value); + assert.match(summary.rows.find(r=>r.field.startsWith('Saldo operacional')).field,/Mensal/); + const missing=createProjectDossierReport({}, {selectedSections:new Set(['summary'])}); + assert.equal(missing.sections[0].rows.some(r=>r.field.startsWith('Saldo operacional')),false); +}); import { serializeBusinessPlanSnapshot, forecastBusinessPlan, diff --git a/tests/frontend/simulation-form.test.mjs b/tests/frontend/simulation-form.test.mjs index d5350c3f..e7d7f57b 100644 --- a/tests/frontend/simulation-form.test.mjs +++ b/tests/frontend/simulation-form.test.mjs @@ -19,6 +19,15 @@ import { getFiscalPeriodRange, } from '../../resources/js/lib/simulation-form.ts'; +void test('an invalid fiscal operation date has a field error and no substitute period', () => { + for (const date of ['', '2026-99-99', '2026-02-30', '2025-06-15']) { + const draft = { fiscal_period: '2026', fiscal_periodicity: 'one_off', fiscal_scenario_mode: 'approved_parameters', fiscal_reference_date: date }; + assert.equal(getFiscalPeriodRange(draft), null); + assert.match(validateSimulationDraft(draft, 'tax').fiscal_reference_date, /data válida/); + } + assert.equal(validateSimulationDraft({ fiscal_period: '2026', fiscal_periodicity: 'one_off', fiscal_scenario_mode: 'approved_parameters', fiscal_reference_date: '2026-06-15' }, 'tax').fiscal_reference_date, undefined); +}); + void test('fiscal period range requires and respects exact annual, monthly, quarterly, and one-off dates', () => { for (const invalid of ['2026-99-99', '2026-02-30', '2026-00-01', '2026-01-00', '']) { assert.equal(getFiscalPeriodRange({ fiscal_period: '2026', fiscal_periodicity: 'one_off', fiscal_reference_date: invalid }), null); @@ -336,6 +345,7 @@ void test('approved-parameter fiscal mode does not ask users to enter a rate', ( }); void test('custom planning mode requires user assumptions and validates percentage limits', () => { + assert.deepEqual(validateSimulationDraft({ fiscal_scenario_mode: 'custom_planning' }, 'tax'), {}); const base = { purpose: 'simulate_only', fiscal_period: '2026',