From 13f68f1bd8676d5ed418876a562af259e3887ba8 Mon Sep 17 00:00:00 2001 From: nala Date: Wed, 21 Oct 2026 08:43:12 +0200 Subject: [PATCH] fix: concluir recuperacao de exportacoes e consolidar candidata QA --- ...-review-continuation-20261020-receipt.json | 163 +++++++++++- .../unification/traceability-and-manual.md | 91 +++++++ .../v2/auditoria-portal/matriz-requisitos.csv | 16 +- .../js/components/project-dossier-export.tsx | 233 ++++++++++-------- resources/js/lib/dossier-export-state.ts | 89 +++++++ resources/js/lib/project-dossier-document.ts | 7 +- .../frontend/dossier-export-recovery.test.mjs | 141 +++++++++++ .../frontend/project-dossier-report.test.mjs | 9 + 8 files changed, 639 insertions(+), 110 deletions(-) create mode 100644 resources/js/lib/dossier-export-state.ts create mode 100644 tests/frontend/dossier-export-recovery.test.mjs diff --git a/docs/investmoz/simulations/integrated-review-continuation-20261020-receipt.json b/docs/investmoz/simulations/integrated-review-continuation-20261020-receipt.json index 54ebb083..d7305b5d 100644 --- a/docs/investmoz/simulations/integrated-review-continuation-20261020-receipt.json +++ b/docs/investmoz/simulations/integrated-review-continuation-20261020-receipt.json @@ -158,5 +158,166 @@ 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" + "secrets_and_private_fixtures": "Not included in source manifest or Git", + "current_reconciliation_20261021": { + "items": { + "AUD-SIM-INT01": { + "status": "Concluído no recorte", + "remaining": "Nenhum delta de validação de data por demonstrar; prova anterior preservada." + }, + "AUD-SIM-INT02": { + "status": "Parcial", + "remaining": "Leitura remota concluída: zero regras de todos os estados na homologação, tal como QA; seis referências jurídicas não são parâmetros executáveis. Falta cadastro fundamentado por componente/âmbito/período e ligação funcional ao motor fiscal. Fontes e análises existentes reutilizáveis; aceitação oficial da Tempo de08/10 preservada. Não activar taxas nem importar/publicar por esta reconciliação." + }, + "AUD-SIM-INT03": { + "status": "Concluído no recorte", + "remaining": "A/B, logout/novo login, recuperação e recusa pela aplicação demonstrados anteriormente. A tesouraria tem prova API de nova sessão e prova visual de recuperação por recarga. Não atribuir ao navegador nova sessão dos valores de tesouraria, nem nova certificação RLS; limites expressos conservados." + }, + "AUD-SIM-INT04": { + "status": "Concluído no recorte demonstrado", + "remaining": "Histórico24/12 visível, hashes preservados e versões temporais sintéticas do resolvedor conferidas. Execução fiscal integrada com parâmetros reais pertence ao saldo de02 e não foi demonstrada por estes testes." + }, + "AUD-SIM-INT05": { + "status": "Concluído nos controlos delimitados", + "remaining": "Controlos independentes de caixa/plano/crédito, zero/desconhecido e moedas preservados. Não são validação de taxas legais nem certificação de toda a contabilidade." + }, + "AUD-SIM-INT06": { + "status": "Concluído na apresentação e comparação deste relatório", + "remaining": "Rodapé do PDF nativo fechado em QA segundo provas apresentadas;10 páginas/valores/CSV não repetidos. Tempo declarou que não abriu imagem/PDF pelos caminhos locais: registar aceitação das provas, não revisão visual directa do utilizador. Outras provas de relatório conservadas; falhas interactivas de exportação/anexos mantêm saldo em07." + }, + "AUD-SIM-INT07": { + "status": "Parcial", + "remaining": "Gravação503→nova tentativa→recuperação e exportações normais demonstradas. Faltam falhas dirigidas de geração Word/ZIP/PDF e retoma; teste interactivo de limite de anexo não ocorreu por bloqueio da ferramenta antes do envio. Nova leitura do código: três catch apresentam cause.message para Error, podendo mostrar texto técnico sem tradução. Sem nova falha de execução alegada." + }, + "AUD-SIM-INT08": { + "status": "Parcial", + "remaining": "Inventário estático,404 real e traduções de validação preservados. Classificar/apresentar em português os erros dinâmicos das bibliotecas de exportação (cause.message, três consumidores) e conferir estados residuais/governação delimitados; não certificar todos os textos dinâmicos do catálogo." + } + }, + "remaining_hours_delimited": [ + 1, + 2 + ], + "integral_fiscal_implementation_estimated": false, + "user_direct_visual_review_of_local_files": false, + "native_footer_accepted_on_presented_evidence": true, + "remote_rule_count": 0, + "remote_legal_reference_count": 6, + "supersedes_prior_pending": [ + "remote authentication and inventory", + "native print save and visual footer correction", + "cashflow screen/download values" + ], + "tool_limits_retained": [ + "interactive file chooser before attachment upload", + "native save performed by user" + ], + "branch": "codex/simulator-integrated-review", + "main_merge": false, + "read_only_evidence_sha256": "bbb2d2f0b001f1f439ce2bf8e60a4233467463f5cfa430dd05b82f515f5d9a5f" + }, + "candidate_20261021": { + "branch": "codex/simulator-integrated-review", + "base": "c22003a3", + "main_integration": false, + "active_changes": false, + "new_tests": 4, + "typescript": "passed", + "browser": { + "word_failure_retry": "passed, 2 attempts / 1 success", + "pdf_failure_retry": "passed, failed popup closed / one populated retry", + "zip_failure_retry": "passed: failed ZIP, edited title/unchecked section and one attachment retained; retry produced one valid archive", + "attachment_limit_ui": "passed: refused16MB, prior valid entry retained", + "attachment_valid_retry": "passed: duplicate selection leaves one entry and translated status" + }, + "duplicate_prevention": "synchronous gate test passed; attachment name+SHA256 identity test passed; ZIP single original test passed", + "logs": "local diagnostic console; no backend telemetry added", + "accepted_evidence_reused": [ + "native PDF 10-page footer QA acceptance", + "financial values and CSV", + "A/B and new session", + "historical snapshots and temporal resolver", + "read-only remote fiscal inventory" + ], + "limitations": [ + "Synthetic wrapper injects generator faults only and is excluded from deployment", + "Native print and values/CSV proofs reused, not repeated", + "Transient attachment read/concurrent-click proofs are automated; manual chooser path observed separately", + "Local console diagnostics are not persistent server audit", + "Fiscal integration remains incomplete; catalogue/governance language is not globally certified" + ], + "sources": [ + { + "path": "resources/js/components/project-dossier-export.tsx", + "sha256": "c0bbea29c8e307785cbe54014a85d6271c723a541bea9f2ad5196118b9570764" + }, + { + "path": "resources/js/lib/dossier-export-state.ts", + "sha256": "ac40e30134ba56b67c6c6a3b044aaeb110750cac53e0934794c8a7d346d745fe" + }, + { + "path": "resources/js/lib/project-dossier-document.ts", + "sha256": "3bec7565324587bdd42f65f5b3c723597ec554b1c80376bfb45d350516eae16f" + }, + { + "path": "tests/frontend/dossier-export-recovery.test.mjs", + "sha256": "b4112d612ce204c41b7376e64a0c15e7b85a6ec19087b0ed5415c1114d99f2b2" + }, + { + "path": "tests/frontend/project-dossier-report.test.mjs", + "sha256": "77e33425e33e7ca0290e60e0b09fab7bbe5c8bd1eef196dd0863452da8f06271" + } + ], + "remaining_engineering_hours_delimited": [ + 0, + 0 + ], + "review_pending": true, + "build_manifest_sha256": "e4846e02db6112bce9c1f5466038e0a25f381c17fdcd8fe54248a7068a0645dd", + "evidence": [ + { + "path": "outputs\\simulator-continuation-20261020\\export-word-error-20261021.txt", + "sha256": "b1c984663ea27bbeb5bfe760ab181314142f32cc9f6b27fa11c2fb7d7f0456ca" + }, + { + "path": "outputs\\simulator-continuation-20261020\\export-pdf-error-20261021.txt", + "sha256": "b46f597e185874875d823d2c90219b555ee1f2028b5d144e00754db73468ed41" + }, + { + "path": "outputs\\simulator-continuation-20261020\\export-pdf-retry-20261021.txt", + "sha256": "091679de9a60e4f22e2d0cb2127b736addfc9e2752fb0bb2aec051ddcb51f3c8" + }, + { + "path": "outputs\\simulator-continuation-20261020\\export-zip-error-20261021.txt", + "sha256": "eda788b6f69b7fbcbb13aadf44dd0d3fbe695a2f050445b6f6a6d6553e7dc63d" + }, + { + "path": "outputs\\simulator-continuation-20261020\\export-zip-retry-20261021.txt", + "sha256": "ef8c3c2197cb77260c1f8a1f169f282f1a14376e2aa331d26e9b98aee45e92c3" + }, + { + "path": "outputs\\simulator-continuation-20261020\\attachment-limit-preserved-20261021.txt", + "sha256": "2132c4ddfaf044e8349766322514e9c6ce5c1a0f06236bfb4323b8a010e73197" + }, + { + "path": "outputs\\simulator-continuation-20261020\\attachment-duplicate-retry-20261021.txt", + "sha256": "3027ea5949168df879442bb04a481d3f8c5e4741e470d30875e1dc6c25892312" + }, + { + "path": "outputs\\simulator-continuation-20261020\\export-zip-content-proof-20261021.json", + "sha256": "6ee6bece4d2110c996bad2d686b24970c9d199eff2940dfba34e285f005f0f7c" + } + ], + "build_exit_code": 0, + "existing_items_current_scope": { + "AUD-SIM-INT01": "demonstrated within bounded scope; see existing manual for limits", + "AUD-SIM-INT02": "partial; fiscal implementation remains open", + "AUD-SIM-INT03": "demonstrated within bounded scope; see existing manual for limits", + "AUD-SIM-INT04": "demonstrated within bounded scope; see existing manual for limits", + "AUD-SIM-INT05": "demonstrated within bounded scope; see existing manual for limits", + "AUD-SIM-INT06": "demonstrated within bounded scope; see existing manual for limits", + "AUD-SIM-INT07": "demonstrated within bounded scope; see existing manual for limits", + "AUD-SIM-INT08": "demonstrated within bounded scope; see existing manual for limits" + } + } } diff --git a/docs/investmoz/unification/traceability-and-manual.md b/docs/investmoz/unification/traceability-and-manual.md index efed4715..ce647e09 100644 --- a/docs/investmoz/unification/traceability-and-manual.md +++ b/docs/investmoz/unification/traceability-and-manual.md @@ -1623,3 +1623,94 @@ Artefactos em `outputs/simulator-continuation-20261020/`: `interface-comparison. 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. + + +### Retoma da conferência visual de tesouraria — 21/10/2026 + +[Retoma 21/10/2026] QA 18576, clone simulator_review_delta_20261020_test: sessão sintética renovada por login normal; gravação autenticada e recarga recuperaram saldo 123, receita 900, custos 600, calendário mensal 10/vazio/0, crédito IVA estimado 500. Caixa conhecida 3713 MZN; total indeterminado desde mês 2; crédito estimado excluído da caixa. Prova visual cashflow-recovered-20261021.png e snapshot .txt em outputs/simulator-continuation-20261020. Não certifica recuperação destes novos valores após outro login, CSV/DOCX ou PDF nativo. Ligação de controlo do navegador perdida durante espera de download; não atribuir falha à aplicação. Leitura remota aguarda identificação da ligação SSH; zero alterações no destino. + + +### Leitura fiscal remota e saída nativa — 21/10/2026 + +[Leitura SSH 21/10/2026 08:17 Maputo] Zero regras em simulation_tax_rule_versions na homologação, incluindo todos os estados por leitura administrativa sem ocultação RLS. Seis referências em investment_legal_sources; não são parâmetros executáveis. QA também tinha zero regras. BEGIN READ ONLY/SELECT/ROLLBACK; zero escritas. Prova homologation-fiscal-readonly.json. O git HEAD registado é do checkout consultado, não certifica a revisão do build servido. A ausência de cadastro não é ausência de fontes oficiais nem reabre a aceitação da Tempo de 08/10. Não implementa a ligação funcional ao motor fiscal. + +[Exportação nativa 21/10/2026] Saída actual aberta pelo botão do portal: revisão 4, 12 meses, MZN, saldo inicial123/receita900/custos600, calendário10/vazio/0, caixa conhecida3713 e total indeterminado coincidem com o ecrã. Crédito estimado500 identificado como pressuposto, não somado à caixa. Prova portal-print-current-20261021.txt. Relatório de20/10/revisão2 preservado, sem o confundir com o actual. Downloads: sem novo CSV na pasta habitual ou nos temporários locais conferidos; isso não demonstra falha nem cancelamento. Histórico de downloads bloqueado pela política da ferramenta; caminho do ficheiro solicitado antes de repetir. PDF nativo aberto, mas paginação/legibilidade aguardam o ficheiro efectivamente guardado pelo Edge; conversão DOCX/PDF anterior não conta como essa prova. Perda da ligação e bloqueio de controlo durante impressão são limitações da ferramenta, sem falha do simulador demonstrada. + + +### Ficheiros de Transferências recuperados — 21/10/2026 + +[Fecho da verificação dos ficheiros 21/10/2026] Transferências Windows redireccionada para \\Mac\Home\Downloads; CSV concluído08:12:46, PDF nativo08:17:19. Sem repetir downloads. Os12 registos mensais do CSV coincidem exactamente com o snapshot do ecrã após correspondência de colunas; PDF revisão4 conserva valores mensais, MZN,12meses,123/900/600, crédito estimado500 não caixa e caixa conhecida3713. Os10 PNG do PDF nativo foram inspeccionados: defeito confirmado na linha repetida de rodapé, sobreposta ao corpo perto do topo de páginas, incluindo2,6e10. Conteúdo/valores conferidos; paginação/legibilidade não aceites até correcção e nova prova nativa. Não é conversão DOCX/PDF. Perda de ligação continua limitação da ferramenta e não causou falha do download; sobreposição é defeito distinto demonstrado no PDF. Provas: download-native-inspection-20261021.json, portal-cashflow-20261021.csv, portal-native-20261021.pdf e native-pdf-render/. A indicação anterior de ficheiros pendentes fica superada por esta prova. Sem alteração funcional nem escrita remota. + + +### Correcção do rodapé do PDF nativo em QA — 21/10/2026 + +[Correcção do rodapé nativo QA 21/10/2026] Origem: pedido expresso da Tempo nesta conversa; solução de implementação, sem regra organizacional nova. Causa em createProjectDossierPrintHtml (project-dossier-document.ts): footer position:fixed/bottom:-14mm sobrepunha a área fragmentada de impressão. Substituído na impressão por caixas @page @bottom-left/@bottom-right, dentro da margem inferior reservada20mm, com identificação/revisão e número de página/total. Rodapé normal de ecrã conservado; sem diminuir letras do corpo (10,5pt), tabelas, cabeçalhos ou origens (9pt), nem retirar conteúdo. Mesmo dossier B/revisão4 regenerado pelo botão do portal QA18576 e guardado no Edge, não por conversão DOCX. 10→10 páginas A4; comparação automática do corpo por página, descontando apenas hora de emissão/rodapé/contador, coincide integralmente. Inspecção visual dos10 PNG: sem sobreposição, cabeçalhos de tabela repetidos, quebras/linhas preservadas, última página íntegra e numeração1/10→10/10. Rodapés medidos inteiramente na margem inferior. Ecrã QA conferido após build; estilos screen e geradores DOCX/ZIP inalterados por comparação de fontes. 10 testes do exportador, TypeScript e build passaram; escape da revisão na nova string CSS verificado. Reutilizadas provas de valores e CSV aceites; não repetido download/aritmética. Provas: native-footer-fix-20261021.json; portal-native-fixed-20261021.pdf; native-footer-before-after-20261021.jpg; native-pdf-fixed-render/; screen-after-print-fix-20261021.png. Fechado exclusivamente o defeito de rodapé neste relatório QA; limitações e restantes critérios de AUD-SIM-INT06 mantêm âmbito próprio. Relato anterior de sobreposição fica superado neste delta pela comparação antes/depois. Branch codex/simulator-integrated-review separada de main, sem integração, escrita remota ou disponibilização activa; revisão prevista mantém-se pendente. + + +### Saldo consolidado dos oito itens — 21/10/2026 + +Reconciliação a pedido expresso da Tempo. O fecho do rodapé limita-se à apresentação deste relatório e baseia-se nas provas apresentadas; o utilizador declarou não ter conferido a imagem nem o PDF pelos caminhos locais. Não é fecho da revisão integral do simulador. Reutilizadas as provas aceites, sem repetir10 páginas, valores,CSV ou consulta SSH. + +| Item existente | Estado no recorte | Saldo / limite | +|---|---|---| +| AUD-SIM-INT01 | Concluído no recorte | Nenhum delta de validação de data por demonstrar; prova anterior preservada. | +| AUD-SIM-INT02 | Parcial | Leitura remota concluída: zero regras de todos os estados na homologação, tal como QA; seis referências jurídicas não são parâmetros executáveis. Falta cadastro fundamentado por componente/âmbito/período e ligação funcional ao motor fiscal. Fontes e análises existentes reutilizáveis; aceitação oficial da Tempo de08/10 preservada. Não activar taxas nem importar/publicar por esta reconciliação. | +| AUD-SIM-INT03 | Concluído no recorte | A/B, logout/novo login, recuperação e recusa pela aplicação demonstrados anteriormente. A tesouraria tem prova API de nova sessão e prova visual de recuperação por recarga. Não atribuir ao navegador nova sessão dos valores de tesouraria, nem nova certificação RLS; limites expressos conservados. | +| AUD-SIM-INT04 | Concluído no recorte demonstrado | Histórico24/12 visível, hashes preservados e versões temporais sintéticas do resolvedor conferidas. Execução fiscal integrada com parâmetros reais pertence ao saldo de02 e não foi demonstrada por estes testes. | +| AUD-SIM-INT05 | Concluído nos controlos delimitados | Controlos independentes de caixa/plano/crédito, zero/desconhecido e moedas preservados. Não são validação de taxas legais nem certificação de toda a contabilidade. | +| AUD-SIM-INT06 | Concluído na apresentação e comparação deste relatório | Rodapé do PDF nativo fechado em QA segundo provas apresentadas;10 páginas/valores/CSV não repetidos. Tempo declarou que não abriu imagem/PDF pelos caminhos locais: registar aceitação das provas, não revisão visual directa do utilizador. Outras provas de relatório conservadas; falhas interactivas de exportação/anexos mantêm saldo em07. | +| AUD-SIM-INT07 | Parcial | Gravação503→nova tentativa→recuperação e exportações normais demonstradas. Faltam falhas dirigidas de geração Word/ZIP/PDF e retoma; teste interactivo de limite de anexo não ocorreu por bloqueio da ferramenta antes do envio. Nova leitura do código: três catch apresentam cause.message para Error, podendo mostrar texto técnico sem tradução. Sem nova falha de execução alegada. | +| AUD-SIM-INT08 | Parcial | Inventário estático,404 real e traduções de validação preservados. Classificar/apresentar em português os erros dinâmicos das bibliotecas de exportação (cause.message, três consumidores) e conferir estados residuais/governação delimitados; não certificar todos os textos dinâmicos do catálogo. | + +**Estimativa restante deste recorte:1–2h de engenharia**, excluindo espera por interacção necessária da ferramenta: 07/08 agrupados0,75–1,5h para tratamento e conferência dirigida de erros dinâmicos/retoma/anexos; 0,25–0,5h para reconciliação final das provas. Não somar o mesmo erro duas vezes por aparecer em07e08. Substitui o saldo anterior2–4h porque leitura remota, apresentação nativa e comparação da tesouraria já foram executadas. Não inclui cadastrar/aplicar regras fiscais reais, integrar fórmulas no motor ou certificar todo o catálogo. O esforço para02 integral permanece por dimensionar por componente, âmbito, período e correspondência documental; não há estimativa de conclusão integral obtida apenas deste1–2h. + +Próxima execução delimitada: corrigir a apresentação de excepções brutas nos três exportadores, conferir falha/retoma e os limites interactivos que faltam, preservando os relatórios/CSV já aceites. Branch codex/simulator-integrated-review mantida separada de main até à revisão consolidada. Nenhuma nova concessão, regra fiscal, migration ou escrita remota. + + +### Candidata consolidada da revisão do simulador — 21/10/2026 + +Origem: pedido expresso da Tempo para fechar apenas falhas dirigidas de exportação/anexos, apresentação das mensagens e consolidação. As 1–2 h anteriores respeitavam a esse recorte, não à implementação fiscal integral. A branch `codex/simulator-integrated-review` permanece separada de `main`; não integrar antes da revisão consolidada porque a cadeia existente disponibiliza automaticamente a revisão integrada. + +**Separação funcional preservada.** Os cálculos de planeamento usam valores declarados pelo utilizador, com origem e pressupostos identificados. O relatório não os transforma em resultados fiscais. Falta de imposto/parâmetro continua indeterminada, não zero; crédito/pedido/reconhecimento de IVA não se torna dinheiro recebido. A correcção de falhas de exportação e do rodapé não encerra a componente fiscal. + +**Implementação deste delta.** O exportador apresenta mensagens em português, identifica a operação e a referência de diagnóstico e mantém as selecções para nova tentativa pelo mesmo botão. A excepção original é registada na consola técnica local com operação/referência; não há novo envio ao servidor, telemetria nem registo de conteúdo do dossier/anexos. A consola é um diagnóstico local, não uma auditoria persistente no servidor. Uma guarda síncrona recusa outra execução enquanto a primeira está activa, mesmo antes da actualização do estado React; após erro a guarda é libertada. Preparação dos anexos é atómica: falhas/limites não substituem a lista existente nem as legendas. A selecção repetida do mesmo nome com os mesmos bytes mantém uma entrada; bytes diferentes com o mesmo nome e nomes diferentes continuam distintos. Esta identidade é escolha de implementação local, não regra de cadastro/publicação documental. Os limites de quantidade/tamanho já existentes não foram alterados. + +**Provas reutilizadas.** Data inválida; A/B e nova sessão; recusa de outra conta; histórico24/12 e hashes; resolvedor temporal sintético; controlos financeiros independentes; Word/CSV e respectivos valores; PDF nativo10 páginas/rodapé/última página; inventários/documentos e leitura SSH sem escrita. A Tempo aceitou o fecho do rodapé segundo as provas apresentadas e declarou que não abriu a imagem/PDF pelos caminhos locais. Não lhe atribuir inspecção visual directa. Os testes temporais sintéticos não representam legislação. A prova de recusa do clone com superutilizador é da aplicação; não é uma nova certificação de RLS. Não repetir as provas aceites apenas para gerar uma nova lista de resultados. + +**Ensaio dirigido isolado.** `http://127.0.0.1:18576/qa/export-recovery` usa o componente real e os geradores reais, com uma falha única de biblioteca injectada no módulo de ensaio, sem alteração das rotas de produção. Os controlos de falha e os ficheiros sintéticos ficam em `outputs/`, fora do Git e do pacote. Os contadores referem-se a chamadas/generações; para ZIP contam a geração interna de Word e a preparação do arquivo, não duas transferências. No ensaio de nova tentativa de PDF é omitida apenas a impressão automática, para observar a janela com conteúdo e não repetir a prova nativa aceite. Nenhuma dessas injecções entra na candidata funcional. + +**Procedimento de integração após revisão.** Fixar o commit candidato e manifesto de fontes; obter o `main` remoto actualizado e reconciliar apenas os deltas dos programadores que afectem estas fontes/consumidores. Rever o diff e conferir no ambiente QA a revisão exacta a integrar; repetir apenas verificações afectadas por conflitos ou alterações posteriores. Este delta não contém migrations, cadastro de regras, importação, publicação de documentos, contas, permissões ou alterações de MFA/configuração. Após decisão da Tempo, integrar pelo processo Git existente; confirmar o recibo da cadeia e a revisão efectivamente servida em `/build/revision.json`, com consulta pública e login privado conferidos nos consumidores afectados. Uma falha deve conservar a release anterior; não editar a pasta servida para contornar a cadeia. A reversão usa o mecanismo de release anterior de código/build, preservando base, anexos, configuração e dados posteriores; não repor um dump antigo. Se uma futura entrega incluir regras/dados activos, exigir o procedimento específico já aprovado de backup/restauro e acção autenticada; esse trabalho não está incluído aqui. + +### Diagnóstico fiscal separado da candidata — 21/10/2026 + +| Camada | Existente e comprovado | Saldo efectivo | +|---|---|---| +| Fontes | Inventário anterior de9901 caminhos e32 candidatos por nome; três originais versionados: BR248/I/Suplemento1/2025, republicação Lei22/2022 no BR250/2.ºSuplemento e BR89/2017. Análises em `primary-text-review-2025-2026.md`, `official-source-register.md`, `fiscal-reform-2026-review.md` e referências em `fiscal-source-references-2026-10-06.csv`. | Reutilizar conteúdo, hashes, versões e citações já disponíveis; não pedir novamente esses documentos. A enumeração de caminhos não equivale à leitura de todos os binários. Distinguir fontes já lidas de fontes só inventariadas e alterações/anexos ainda sem correspondência. | +| Parâmetros executáveis | Leitura administrativa de todos os estados confirmou zero linhas em `simulation_tax_rule_versions` tanto na QA como na homologação. A homologação tem seis referências em `investment_legal_sources`, ligadas a estruturas territoriais; não são taxas/fórmulas prontas. | Cadastrar por componente: código/versão, diploma/artigo/versão/hash, âmbito e condições, início/fim de aplicação, valor/unidade, bases/limites/exclusões, fórmula e arredondamento fundamentados. Os campos necessários variam por regra; não preencher lacunas por zero ou pela taxa mais recente. | +| Governação e consulta | Modelo versionado, controlador de transições, API de resolução por componente/período/contexto, selecção de publicados e compatibilidade de versão da fonte; resolução temporal e indeterminação já ensaiadas. | Reutilizar estes serviços; conferir as correspondências documentais e a operação com parâmetros reais quando estes existirem. Não criar aprovações, nomeações ou uma nova exigência organizacional por escolha técnica. A Tempo define os papéis e as regras. | +| Expressões | `SimulationTaxRuleExpression` já valida e avalia a DSL `investmoz.tax.v1` com aritmética racional, arredondamento e valores em falta. | Não reconstruir o avaliador. Verificar se cada regra fundamentada cabe na linguagem existente e estender apenas operações concretamente necessárias. | +| Execução fiscal integrada | `RunSimulation` executa `base × taxa/100 × (1−redução/100)`, identifica `custom_planning`, `legal_interpretation=false`, `ruleset_version=null` e moeda não indicada nesse cenário. Consulta/DSL não são consumidas por este fluxo como motor fiscal. | Desenvolver orquestração explícita de componente/período/âmbito → regras elegíveis → entradas e parâmetros → avaliação → resultado e explicação com fontes/versões imutáveis. Tratar ambiguidade/transição/ausência como indeterminação. Manter o cenário de planeamento separado, sem o relabelar como imposto calculado. | +| Resultado, dossier e relatório | Snapshots, histórico e exportadores existentes podem transportar resultados e proveniência; relatórios actuais identificam pressupostos/estimativas. | Integrar o resultado fiscal com identificação própria de regra, período, unidade/moeda, base, passos, arredondamento e lacunas. Guardar reprodução histórica e demonstrar casos de referência por regra, isolamento e não reaplicação de uma versão actual a um resultado antigo. | + +**Componentes a parametrizar, não taxas inventadas.** O acervo já analisado identifica famílias de alterações para ICE (Lei7/2025/tabela), ISPC (Lei9/2025), IVA (republicação Lei22/2022 e Lei10/2025, além da consolidação/rectificação aplicável), IRPS (Lei11/2025) e IRPC (Lei12/2025). A API também contempla INSS, retenções e direitos aduaneiros. Isto é inventário de domínios, não prova de cobertura documental completa nem autorização para aplicar uma taxa genérica. Para ICE/aduaneiro são relevantes código/unidade/tabela e período; para IVA, operação, incidência/exclusões/dedução e estado de reembolso; para ISPC, elegibilidade/limites e obrigações; para IRPS/IRPC, categoria, residência/estabelecimento, base, retenções e regimes específicos. Prazos, taxas, anexos e incentivos só entram com correspondência documental aplicável. + +A aceitação das fontes oficiais determinada pela Tempo em08/10 prevalece sobre pedidos históricos genéricos de reconfirmação de autenticidade ou de um fiscalista adicional. Identificar o artigo, âmbito, período, alterações/revogação e parâmetros é aplicação/rastreabilidade da fonte aceite, não segunda validação da sua credibilidade. A biblioteca documental, a situação jurídica e a utilização em cálculo são estados independentes; importar ou publicar documentos nunca activa cálculo fiscal. + +**Desenvolvimento fiscal necessário:** correspondência documental e cadastro versionado; adaptação concreta de expressões quando necessária; orquestração do cálculo; contrato de resultado/proveniência/histórico; ligação à interface/relatórios e casos de referência por componente/período. Nenhuma dessas frentes é encerrada por corrigir crédito, orçamento, tesouraria, falhas de exportação ou rodapé. O esforço integral deve ser dimensionado por regras/componentes e lacunas documentais identificadas; não cabe na estimativa de1–2h do recorte de exportação. + +### Fecho do recorte de exportações e saldo da candidata — 21/10/2026 + +| Item existente | Saldo consolidado da candidata | Prova / limite | +|---|---|---| +| AUD-SIM-INT01 | Concluído no recorte | Validação de data: prova anterior reutilizada. | +| AUD-SIM-INT02 | Parcial; implementação fiscal integral aberta | Zero regras remotas/QA; fontes, cadastro e integração discriminados no diagnóstico separado. | +| AUD-SIM-INT03 | Concluído nas provas delimitadas | A/B/nova sessão e recusas anteriores; tesouraria com API/nova sessão e UI/recarga, sem alegar uma nova prova browser da mesma sessão. Limite RLS mantido. | +| AUD-SIM-INT04 | Concluído no recorte demonstrado | Histórico/snapshots e resolvedor sintético reutilizados; integração real fiscal permanece em02. | +| AUD-SIM-INT05 | Concluído nos controlos delimitados | Controlos independentes e provas financeiras aceites reutilizados; sem validação legal de impostos. | +| AUD-SIM-INT06 | Concluído na apresentação/comparação deste relatório | Rodapé nativo aceito segundo provas; sem repetir10páginas/valores/CSV; sem atribuir inspecção local ao utilizador. | +| AUD-SIM-INT07 | Concluído no recorte dirigido QA |3falhas de geração/retoma, anexo16MB recusado com lista conservada, re-selecção sem duplicação e ZIP íntegro; injecção sintética fora da entrega. | +| AUD-SIM-INT08 | Concluído nos consumidores afectados | Mensagens portuguesas/diagnóstico técnico separado; limites de catálogo/governação mantidos, sem certificação global. | + +Esforço restante do recorte de engenharia de exportações:0h; revisão consolidada e eventual integração continuam pendentes. As1–2h anteriores foram estimativa deste recorte; não eram estimativa da implementação fiscal integral. Não fechar a revisão integral do simulador nem AUD-SIM-INT02 por este incremento. Word/PDF/ZIP foram ensaiados no componente real com falhas sintéticas de geração de um invólucro QA privado; ficheiros de67bytes e16MiB seleccionados manualmente pelo utilizador, estados observados directamente pelo Codex. A primeira recusa encontrou lista vazia e não foi usada como prova de preservação; a sequência posterior demonstrou lista válida→recusa16MB mantendo1→re-selecção válida mantendo1. ZIP real verificado por bytes, entrada única, título e secção. Leitura transitória falhada e concorrência demonstradas nos testes dirigidos, não por avaria real do sistema de ficheiros. A limitação anterior da selecção automática não foi contornada: a selecção normal foi manual. Os detalhes técnicos permanecem em diagnóstico local; não foi criado serviço de recolha remoto. + +Provas actuais em outputs/simulator-continuation-20261020/: export-word-error-20261021.*, export-pdf-error-20261021.txt, export-pdf-retry-20261021.txt, export-zip-error-20261021.*, export-zip-retry-20261021.txt, attachment-limit-preserved-20261021.*, attachment-duplicate-retry-20261021.*, export-zip-content-proof-20261021.json e export-recovery-candidate-20261021.json. O procedimento de integração e diagnóstico fiscal imediatamente anteriores integram esta mesma candidata e o mesmo registo; sem matriz/backlog adicional. diff --git a/docs/investmoz/v2/auditoria-portal/matriz-requisitos.csv b/docs/investmoz/v2/auditoria-portal/matriz-requisitos.csv index f2e61250..7fc86b55 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,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,,,,,,,,,,,,,, +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,, | [Saldo consolidado21/10/2026] Concluído no recorte. Nenhum delta de validação de data por demonstrar; prova anterior preservada. Ver saldo dos oito itens no manual existente; sem fecho global por esta reconciliação.,,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,," | [Leitura SSH 21/10/2026 08:17 Maputo] Zero regras em simulation_tax_rule_versions na homologação, incluindo todos os estados por leitura administrativa sem ocultação RLS. Seis referências em investment_legal_sources; não são parâmetros executáveis. QA também tinha zero regras. BEGIN READ ONLY/SELECT/ROLLBACK; zero escritas. Prova homologation-fiscal-readonly.json. O git HEAD registado é do checkout consultado, não certifica a revisão do build servido. A ausência de cadastro não é ausência de fontes oficiais nem reabre a aceitação da Tempo de 08/10. Não implementa a ligação funcional ao motor fiscal. | [Saldo consolidado21/10/2026] Parcial. Leitura remota concluída: zero regras de todos os estados na homologação, tal como QA; seis referências jurídicas não são parâmetros executáveis. Falta cadastro fundamentado por componente/âmbito/período e ligação funcional ao motor fiscal. Fontes e análises existentes reutilizáveis; aceitação oficial da Tempo de08/10 preservada. Não activar taxas nem importar/publicar por esta reconciliação. Ver saldo dos oito itens no manual existente; sem fecho global por esta reconciliação. | [Candidata21/10/2026] Diagnóstico fiscal separado no manual existente: fontes/analises já disponíveis; zero parâmetros executáveis na QA e homologação por leitura de todos os estados; governação/resolvedor temporal/DSL existentes; RunSimulation ainda aritmética personalizada sem consumir regras fiscais. Saldo: cadastro fundamentado, orquestração, contrato de resultado/histórico e ligação ao dossier/relatório, com casos por componente/período. Nenhuma regra criada nem activada; implementação fiscal integral aberta e excluída das1–2h de exportações.",,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,,"[Retoma 21/10/2026] QA 18576, clone simulator_review_delta_20261020_test: sessão sintética renovada por login normal; gravação autenticada e recarga recuperaram saldo 123, receita 900, custos 600, calendário mensal 10/vazio/0, crédito IVA estimado 500. Caixa conhecida 3713 MZN; total indeterminado desde mês 2; crédito estimado excluído da caixa. Prova visual cashflow-recovered-20261021.png e snapshot .txt em outputs/simulator-continuation-20261020. Não certifica recuperação destes novos valores após outro login, CSV/DOCX ou PDF nativo. Ligação de controlo do navegador perdida durante espera de download; não atribuir falha à aplicação. Leitura remota aguarda identificação da ligação SSH; zero alterações no destino. | [Saldo consolidado21/10/2026] Concluído no recorte. A/B, logout/novo login, recuperação e recusa pela aplicação demonstrados anteriormente. A tesouraria tem prova API de nova sessão e prova visual de recuperação por recarga. Não atribuir ao navegador nova sessão dos valores de tesouraria, nem nova certificação RLS; limites expressos conservados. Ver saldo dos oito itens no manual existente; sem fecho global por esta reconciliação.",,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,," | [Saldo consolidado21/10/2026] Concluído no recorte demonstrado. Histórico24/12 visível, hashes preservados e versões temporais sintéticas do resolvedor conferidas. Execução fiscal integrada com parâmetros reais pertence ao saldo de02 e não foi demonstrada por estes testes. Ver saldo dos oito itens no manual existente; sem fecho global por esta reconciliação.",,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,,"[Retoma 21/10/2026] QA 18576, clone simulator_review_delta_20261020_test: sessão sintética renovada por login normal; gravação autenticada e recarga recuperaram saldo 123, receita 900, custos 600, calendário mensal 10/vazio/0, crédito IVA estimado 500. Caixa conhecida 3713 MZN; total indeterminado desde mês 2; crédito estimado excluído da caixa. Prova visual cashflow-recovered-20261021.png e snapshot .txt em outputs/simulator-continuation-20261020. Não certifica recuperação destes novos valores após outro login, CSV/DOCX ou PDF nativo. Ligação de controlo do navegador perdida durante espera de download; não atribuir falha à aplicação. Leitura remota aguarda identificação da ligação SSH; zero alterações no destino. | [Saldo consolidado21/10/2026] Concluído nos controlos delimitados. Controlos independentes de caixa/plano/crédito, zero/desconhecido e moedas preservados. Não são validação de taxas legais nem certificação de toda a contabilidade. Ver saldo dos oito itens no manual existente; sem fecho global por esta reconciliação.",,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,,"[Retoma 21/10/2026] QA 18576, clone simulator_review_delta_20261020_test: sessão sintética renovada por login normal; gravação autenticada e recarga recuperaram saldo 123, receita 900, custos 600, calendário mensal 10/vazio/0, crédito IVA estimado 500. Caixa conhecida 3713 MZN; total indeterminado desde mês 2; crédito estimado excluído da caixa. Prova visual cashflow-recovered-20261021.png e snapshot .txt em outputs/simulator-continuation-20261020. Não certifica recuperação destes novos valores após outro login, CSV/DOCX ou PDF nativo. Ligação de controlo do navegador perdida durante espera de download; não atribuir falha à aplicação. Leitura remota aguarda identificação da ligação SSH; zero alterações no destino. | [Exportação nativa 21/10/2026] Saída actual aberta pelo botão do portal: revisão 4, 12 meses, MZN, saldo inicial123/receita900/custos600, calendário10/vazio/0, caixa conhecida3713 e total indeterminado coincidem com o ecrã. Crédito estimado500 identificado como pressuposto, não somado à caixa. Prova portal-print-current-20261021.txt. Relatório de20/10/revisão2 preservado, sem o confundir com o actual. Downloads: sem novo CSV na pasta habitual ou nos temporários locais conferidos; isso não demonstra falha nem cancelamento. Histórico de downloads bloqueado pela política da ferramenta; caminho do ficheiro solicitado antes de repetir. PDF nativo aberto, mas paginação/legibilidade aguardam o ficheiro efectivamente guardado pelo Edge; conversão DOCX/PDF anterior não conta como essa prova. Perda da ligação e bloqueio de controlo durante impressão são limitações da ferramenta, sem falha do simulador demonstrada. | [Fecho da verificação dos ficheiros 21/10/2026] Transferências Windows redireccionada para \\Mac\Home\Downloads; CSV concluído08:12:46, PDF nativo08:17:19. Sem repetir downloads. Os12 registos mensais do CSV coincidem exactamente com o snapshot do ecrã após correspondência de colunas; PDF revisão4 conserva valores mensais, MZN,12meses,123/900/600, crédito estimado500 não caixa e caixa conhecida3713. Os10 PNG do PDF nativo foram inspeccionados: defeito confirmado na linha repetida de rodapé, sobreposta ao corpo perto do topo de páginas, incluindo2,6e10. Conteúdo/valores conferidos; paginação/legibilidade não aceites até correcção e nova prova nativa. Não é conversão DOCX/PDF. Perda de ligação continua limitação da ferramenta e não causou falha do download; sobreposição é defeito distinto demonstrado no PDF. Provas: download-native-inspection-20261021.json, portal-cashflow-20261021.csv, portal-native-20261021.pdf e native-pdf-render/. A indicação anterior de ficheiros pendentes fica superada por esta prova. Sem alteração funcional nem escrita remota. | [Correcção do rodapé nativo QA 21/10/2026] Origem: pedido expresso da Tempo nesta conversa; solução de implementação, sem regra organizacional nova. Causa em createProjectDossierPrintHtml (project-dossier-document.ts): footer position:fixed/bottom:-14mm sobrepunha a área fragmentada de impressão. Substituído na impressão por caixas @page @bottom-left/@bottom-right, dentro da margem inferior reservada20mm, com identificação/revisão e número de página/total. Rodapé normal de ecrã conservado; sem diminuir letras do corpo (10,5pt), tabelas, cabeçalhos ou origens (9pt), nem retirar conteúdo. Mesmo dossier B/revisão4 regenerado pelo botão do portal QA18576 e guardado no Edge, não por conversão DOCX. 10→10 páginas A4; comparação automática do corpo por página, descontando apenas hora de emissão/rodapé/contador, coincide integralmente. Inspecção visual dos10 PNG: sem sobreposição, cabeçalhos de tabela repetidos, quebras/linhas preservadas, última página íntegra e numeração1/10→10/10. Rodapés medidos inteiramente na margem inferior. Ecrã QA conferido após build; estilos screen e geradores DOCX/ZIP inalterados por comparação de fontes. 10 testes do exportador, TypeScript e build passaram; escape da revisão na nova string CSS verificado. Reutilizadas provas de valores e CSV aceites; não repetido download/aritmética. Provas: native-footer-fix-20261021.json; portal-native-fixed-20261021.pdf; native-footer-before-after-20261021.jpg; native-pdf-fixed-render/; screen-after-print-fix-20261021.png. Fechado exclusivamente o defeito de rodapé neste relatório QA; limitações e restantes critérios de AUD-SIM-INT06 mantêm âmbito próprio. Relato anterior de sobreposição fica superado neste delta pela comparação antes/depois. Branch codex/simulator-integrated-review separada de main, sem integração, escrita remota ou disponibilização activa; revisão prevista mantém-se pendente. | [Saldo consolidado21/10/2026] Concluído na apresentação e comparação deste relatório. Rodapé do PDF nativo fechado em QA segundo provas apresentadas;10 páginas/valores/CSV não repetidos. Tempo declarou que não abriu imagem/PDF pelos caminhos locais: registar aceitação das provas, não revisão visual directa do utilizador. Outras provas de relatório conservadas; falhas interactivas de exportação/anexos mantêm saldo em07. Ver saldo dos oito itens no manual existente; sem fecho global por esta reconciliação.",,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,," | [Saldo consolidado21/10/2026] Parcial. Gravação503→nova tentativa→recuperação e exportações normais demonstradas. Faltam falhas dirigidas de geração Word/ZIP/PDF e retoma; teste interactivo de limite de anexo não ocorreu por bloqueio da ferramenta antes do envio. Nova leitura do código: três catch apresentam cause.message para Error, podendo mostrar texto técnico sem tradução. Sem nova falha de execução alegada. Ver saldo dos oito itens no manual existente; sem fecho global por esta reconciliação. | [Candidata21/10/2026] Concluído o recorte dirigido em QA: Word/PDF/ZIP falha única injectada→mensagem portuguesa→nova tentativa no mesmo botão. ZIP preserva título editado, secção desmarcada e anexo; arquivo real contém uma só cópia original/hash igual. Recusa16MB observada mantendo1anexo; nova selecção válida limpa erro e mantém1entrada. Guarda síncrona impede concorrência/duplicação; teste de falha de leitura preserva lista/legenda e permite retoma.4testes dirigidos passaram, TypeScript/build passaram. UI usa componente/geradores reais num invólucro QA sintético; a injecção e a omissão da impressão automática nesse ensaio não integram a entrega. Não repetidas10páginas/valores/CSV aceites.",,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,," | [Saldo consolidado21/10/2026] Parcial. Inventário estático,404 real e traduções de validação preservados. Classificar/apresentar em português os erros dinâmicos das bibliotecas de exportação (cause.message, três consumidores) e conferir estados residuais/governação delimitados; não certificar todos os textos dinâmicos do catálogo. Ver saldo dos oito itens no manual existente; sem fecho global por esta reconciliação. | [Candidata21/10/2026] Mensagens dinâmicas dos exportadores Word/ZIP/PDF/CSV e leitura de anexos usam texto português por operação, preservação e nova tentativa com referênciaEXP. Excepção original só na consola técnica local com referência/operação; sem telemetria nem auditoria remota novas. UI confirmou português nas3falhas de geração;4testes verificam separação mensagem/diagnóstico e correlação em5operações. Traduções/404/validações anteriores reutilizados. Fecho dos consumidores afectados, não certificação de todos os textos de catálogo ou nova sessão administrativa de governação.",,20/10/2026,,,,,,,,,,,,,, diff --git a/resources/js/components/project-dossier-export.tsx b/resources/js/components/project-dossier-export.tsx index b506c52c..2500796f 100644 --- a/resources/js/components/project-dossier-export.tsx +++ b/resources/js/components/project-dossier-export.tsx @@ -1,11 +1,15 @@ -import { useMemo, useState } from 'react'; +import { useMemo, useRef, useState } from 'react'; +import { + DossierExportGate, + dossierExportFailure, + prepareDossierAttachments, +} from '@/lib/dossier-export-state'; import { createProjectDossierPrintHtml, createProjectDossierReport, dossierReportSections, generateProjectDossierDocx, generateProjectDossierPackage, - validateDossierAttachments, type DossierAttachment, type DossierReference, type DossierCurrentCalculation, @@ -57,6 +61,8 @@ export function ProjectDossierExport({ const [attachments, setAttachments] = useState([]); const [error, setError] = useState(''); const [busy, setBusy] = useState(''); + const [notice, setNotice] = useState(''); + const exportGate = useRef(new DossierExportGate()); const selected = useMemo( () => attachments.filter((item) => item.include), [attachments], @@ -66,20 +72,33 @@ export function ProjectDossierExport({ [selectedSections], ); - function addFiles(files: FileList | null): void { + async function addFiles(files: FileList | null): Promise { if (!files?.length) return; const incoming = Array.from(files); - const combined = [...attachments.map((item) => item.file), ...incoming]; - const problem = validateDossierAttachments(combined); - if (problem) { - setError(problem); - return; - } - setAttachments((current) => [ - ...current, - ...incoming.map((file) => ({ file, include: true, caption: '' })), - ]); - setError(''); + await exportGate.current.run(async () => { + setBusy('attachments'); + setNotice(''); + try { + const result = await prepareDossierAttachments( + attachments, + incoming, + ); + if (result.problem) { + setError(result.problem); + return; + } + setAttachments(result.attachments); + setError(''); + if (result.duplicateCount) + setNotice( + 'Os ficheiros que já estavam na lista foram mantidos uma única vez.', + ); + } catch (cause) { + setError(dossierExportFailure('attachments', cause)); + } finally { + setBusy(''); + } + }); } function toggleSection(id: DossierReportSectionId, checked: boolean): void { @@ -118,99 +137,103 @@ export function ProjectDossierExport({ } async function exportWord(): Promise { - setBusy('word'); - setError(''); - try { - const report = model(); - const blob = await generateProjectDossierDocx( - report, - selected, - selectedSections.has('annexes'), - ); - download( - blob, - `relatorio-investmoz-${filenameStem(report.title)}.docx`, - ); - } catch (cause) { - setError( - cause instanceof Error - ? cause.message - : 'Não foi possível gerar o relatório Word.', - ); - } finally { - setBusy(''); - } + await exportGate.current.run(async () => { + setBusy('word'); + setError(''); + try { + const report = model(); + const blob = await generateProjectDossierDocx( + report, + selected, + selectedSections.has('annexes'), + ); + download( + blob, + `relatorio-investmoz-${filenameStem(report.title)}.docx`, + ); + } catch (cause) { + setError(dossierExportFailure('word', cause)); + } finally { + setBusy(''); + } + }); } async function exportPackage(): Promise { - setBusy('package'); - setError(''); - try { - const report = model(); - const docx = await generateProjectDossierDocx( - report, - selected, - selectedSections.has('annexes'), - ); - const archive = await generateProjectDossierPackage( - docx, - selected, - selectedSections.has('annexes'), - ); - download( - archive, - `relatorio-investmoz-${filenameStem(report.title)}-anexos.zip`, - ); - } catch (cause) { - setError( - cause instanceof Error - ? cause.message - : 'Não foi possível preparar o pacote de anexos.', - ); - } finally { - setBusy(''); - } + await exportGate.current.run(async () => { + setBusy('package'); + setError(''); + try { + const report = model(); + const docx = await generateProjectDossierDocx( + report, + selected, + selectedSections.has('annexes'), + ); + const archive = await generateProjectDossierPackage( + docx, + selected, + selectedSections.has('annexes'), + ); + download( + archive, + `relatorio-investmoz-${filenameStem(report.title)}-anexos.zip`, + ); + } catch (cause) { + setError(dossierExportFailure('package', cause)); + } finally { + setBusy(''); + } + }); } async function exportPdf(): Promise { - const printWindow = window.open('about:blank', '_blank'); - if (!printWindow) { - setError( - 'O browser bloqueou a janela de impressão. Permita pop-ups para abrir a versão PDF.', - ); - return; - } - setBusy('pdf'); - setError(''); - try { - const html = await createProjectDossierPrintHtml( - model(), - selected, - selectedSections.has('annexes'), - ); - printWindow.document.open(); - printWindow.document.write(html); - printWindow.document.close(); - } catch (cause) { - printWindow.close(); - setError( - cause instanceof Error - ? cause.message - : 'Não foi possível preparar a versão para PDF.', - ); - } finally { - setBusy(''); - } + await exportGate.current.run(async () => { + const printWindow = window.open('about:blank', '_blank'); + if (!printWindow) { + setError( + 'O browser bloqueou a janela de impressão. Permita pop-ups para abrir a versão PDF.', + ); + return; + } + setBusy('pdf'); + setError(''); + try { + const html = await createProjectDossierPrintHtml( + model(), + selected, + selectedSections.has('annexes'), + ); + printWindow.document.open(); + printWindow.document.write(html); + printWindow.document.close(); + } catch (cause) { + printWindow.close(); + setError(dossierExportFailure('pdf', cause)); + } finally { + setBusy(''); + } + }); } - function exportCsv(): void { - const blob = new Blob([exportProjectDossierCsv(draft)], { - type: 'text/csv;charset=utf-8', + async function exportCsv(): Promise { + await exportGate.current.run(async () => { + setBusy('csv'); + setError(''); + try { + const blob = new Blob([exportProjectDossierCsv(draft)], { + type: 'text/csv;charset=utf-8', + }); + download( + blob, + `dossier-projecto-investmoz-${filenameStem(String(draft.project_title ?? ''))}.csv`, + ); + } catch (cause) { + setError(dossierExportFailure('csv', cause)); + } finally { + setBusy(''); + } }); - download( - blob, - `dossier-projecto-investmoz-${filenameStem(String(draft.project_title ?? ''))}.csv`, - ); } const totalBytes = attachments.reduce( @@ -261,6 +284,7 @@ export function ProjectDossierExport({ > toggleSection( @@ -298,9 +322,10 @@ export function ProjectDossierExport({ id="project-report-files" type="file" multiple + disabled={Boolean(busy)} accept="image/jpeg,image/png,text/csv,text/plain,.csv,.tsv,.txt,.pdf,.doc,.docx,.xlsx,.odt" onChange={(event) => { - addFiles(event.currentTarget.files); + void addFiles(event.currentTarget.files); event.currentTarget.value = ''; }} /> @@ -322,6 +347,7 @@ export function ProjectDossierExport({ updateAttachment(index, { @@ -340,6 +366,7 @@ export function ProjectDossierExport({ diff --git a/resources/js/lib/dossier-export-state.ts b/resources/js/lib/dossier-export-state.ts new file mode 100644 index 00000000..12eda97d --- /dev/null +++ b/resources/js/lib/dossier-export-state.ts @@ -0,0 +1,89 @@ +import { + validateDossierAttachments, + type DossierAttachment, +} from './project-dossier-report.ts'; + +export type DossierExportOperation = + | 'word' + | 'package' + | 'pdf' + | 'csv' + | 'attachments'; +const messages: Record = { + word: 'Não foi possível gerar o relatório Word.', + package: 'Não foi possível preparar o pacote de anexos.', + pdf: 'Não foi possível preparar a versão para PDF.', + csv: 'Não foi possível preparar o ficheiro CSV.', + attachments: 'Não foi possível ler os anexos seleccionados.', +}; +let failureSequence = 0; + +export function dossierExportFailure( + operation: DossierExportOperation, + cause: unknown, + log: (message: string, detail: unknown) => void = console.error, +): string { + const reference = `EXP-${Date.now().toString(36)}-${++failureSequence}`; + // Local diagnostics only: never send dossier values or file contents. + log('[InvesteMoz exportação]', { reference, operation, cause }); + return `${messages[operation]} Os dados, as secções e os anexos foram mantidos. Tente novamente pelo mesmo botão. Referência: ${reference}.`; +} + +/** Synchronous acquisition prevents a second click before React renders busy. */ +export class DossierExportGate { + private active = false; + async run(task: () => Promise): Promise { + if (this.active) return false; + this.active = true; + try { + await task(); + return true; + } finally { + this.active = false; + } + } +} + +export async function prepareDossierAttachments( + current: DossierAttachment[], + incoming: File[], +): Promise<{ + attachments: DossierAttachment[]; + duplicateCount: number; + problem?: string; +}> { + for (const file of incoming) { + const problem = validateDossierAttachments([file]); + if (problem) + return { attachments: current, duplicateCount: 0, problem }; + } + const identity = async (file: File) => { + const hash = await crypto.subtle.digest( + 'SHA-256', + await file.arrayBuffer(), + ); + return `${file.name}:${Array.from(new Uint8Array(hash), (b) => b.toString(16).padStart(2, '0')).join('')}`; + }; + const known = new Set( + await Promise.all(current.map((item) => identity(item.file))), + ); + const added: DossierAttachment[] = []; + let duplicateCount = 0; + for (const file of incoming) { + const key = await identity(file); + if (known.has(key)) { + duplicateCount++; + continue; + } + known.add(key); + added.push({ file, include: true, caption: '' }); + } + const problem = validateDossierAttachments( + [...current, ...added].map((item) => item.file), + ); + return { + attachments: problem ? current : [...current, ...added], + duplicateCount, + problem: problem ?? undefined, + }; +} diff --git a/resources/js/lib/project-dossier-document.ts b/resources/js/lib/project-dossier-document.ts index 8edd15e3..6d28f078 100644 --- a/resources/js/lib/project-dossier-document.ts +++ b/resources/js/lib/project-dossier-document.ts @@ -659,8 +659,13 @@ export async function createProjectDossierPrintHtml( content.push('

Não há informação fornecida nesta secção.

'); content.push(''); } + // Page margin boxes reserve footer space on every printed page. A fixed + // body element can overlap fragmented tables in Chromium's print layout. + const printFooter = JSON.stringify( + `InvesteMoz · Relatório privado · ${model.version}`, + ).replaceAll('<', '\\3c '); return `Relatório — ${escapeHtml(model.title)}

InvesteMoz
Relatório consolidado do projecto

${escapeHtml(model.title)}
Gerado em ${escapeHtml(new Date(model.generatedAt).toLocaleString('pt-MZ'))}
Versão/revisão: ${escapeHtml(model.version)}

Documento privado. O portal não publica nem partilha este conteúdo.

${content.join('')}
InvesteMoz · Relatório privado · ${escapeHtml(model.version)}
`; } diff --git a/tests/frontend/dossier-export-recovery.test.mjs b/tests/frontend/dossier-export-recovery.test.mjs new file mode 100644 index 00000000..bf43dda6 --- /dev/null +++ b/tests/frontend/dossier-export-recovery.test.mjs @@ -0,0 +1,141 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { + DossierExportGate, + dossierExportFailure, + prepareDossierAttachments, +} from '../../resources/js/lib/dossier-export-state.ts'; +import { + createProjectDossierReport, + generateProjectDossierDocx, + createProjectDossierPrintHtml, + generateProjectDossierPackage, +} from '../../resources/js/lib/project-dossier-report.ts'; + +test('concurrent export is ignored; failed generation unlocks one retry without mutating inputs', async () => { + const gate = new DossierExportGate(); + const draft = { + project_title: 'SINTÉTICO QA', + project_summary: 'Pressupostos declarados; não é resultado fiscal.', + }; + const saved = JSON.stringify(draft); + let release; + let starts = 0; + const first = gate.run(async () => { + starts++; + await new Promise((resolve) => { + release = resolve; + }); + throw new Error('Library failure'); + }); + assert.equal( + await gate.run(async () => { + starts++; + }), + false, + ); + release(); + await assert.rejects(first, /Library failure/); + assert.equal( + await gate.run(async () => { + starts++; + }), + true, + ); + assert.equal(starts, 2); + assert.equal(JSON.stringify(draft), saved); +}); + +test('all operations show Portuguese recovery text while retaining technical details in diagnostics', () => { + for (const operation of ['word', 'package', 'pdf', 'csv', 'attachments']) { + const error = new Error('English internals / private stack'); + const logs = []; + const text = dossierExportFailure(operation, error, (...args) => + logs.push(args), + ); + assert.match(text, /Não foi possível/); + assert.match(text, /foram mantidos.*Tente novamente/); + assert.doesNotMatch(text, /English internals|private stack/); + assert.equal(logs.length, 1); + assert.equal(logs[0][1].cause, error); + assert.ok(text.includes(logs[0][1].reference)); + } +}); + +test('attachment read failure and size refusal preserve existing selection; retry deduplicates bytes and keeps captions', async () => { + const file = new File(['SINTÉTICO QA'], 'qa.txt', { type: 'text/plain' }); + const current = [{ file, include: false, caption: 'Legenda conservada' }]; + const broken = new File(['another'], 'broken.txt'); + broken.arrayBuffer = async () => { + throw new Error('OS read failed'); + }; + await assert.rejects( + prepareDossierAttachments(current, [broken]), + /OS read failed/, + ); + assert.equal(current.length, 1); + const tooLarge = new File([new Uint8Array(16 * 1024 * 1024)], 'large.txt'); + const refused = await prepareDossierAttachments(current, [tooLarge]); + assert.equal(refused.attachments, current); + assert.match(refused.problem, /15 MB/); + const result = await prepareDossierAttachments(current, [ + new File(['SINTÉTICO QA'], 'qa.txt'), + new File(['distinct'], 'qa.txt'), + ]); + assert.equal(result.duplicateCount, 1); + assert.equal(result.attachments.length, 2); + assert.equal(result.attachments[0], current[0]); + assert.equal(result.attachments[0].caption, 'Legenda conservada'); + assert.equal(result.attachments[0].include, false); +}); + +test('actual Word PDF and ZIP generators recover after a transient attachment read failure; archive has one original', async () => { + const model = createProjectDossierReport( + { project_title: 'QA recuperação' }, + { selectedSections: new Set(['summary', 'annexes']) }, + ); + const file = new File(['Texto QA'], 'qa.txt', { type: 'text/plain' }); + const original = file.text.bind(file); + let fail = false; + file.text = async () => { + if (fail) { + fail = false; + throw new Error('Transient attachment read failure'); + } + return original(); + }; + const attachments = [{ file, include: true, caption: '' }]; + for (const generate of [ + () => generateProjectDossierDocx(model, attachments, true), + () => createProjectDossierPrintHtml(model, attachments, true), + ]) { + fail = true; + await assert.rejects(generate, /Transient attachment/); + assert.ok(await generate()); + assert.equal(attachments.length, 1); + } + const word = await generateProjectDossierDocx(model, [], false); + let broken = true; + const { DossierExportGate: Gate } = + await import('../../resources/js/lib/dossier-export-state.ts'); + const gate = new Gate(); + await assert.rejects( + gate.run(async () => { + if (broken) throw new Error('ZIP write fault'); + }), + /ZIP write fault/, + ); + broken = false; + let zip; + await gate.run(async () => { + zip = await generateProjectDossierPackage(word, attachments, true); + }); + const { default: JSZip } = await import('jszip'); + const archive = await JSZip.loadAsync(await zip.arrayBuffer()); + assert.deepEqual( + Object.keys(archive.files).filter( + (name) => name.startsWith('Anexos/') && !archive.files[name].dir, + ), + ['Anexos/qa.txt'], + ); +}); diff --git a/tests/frontend/project-dossier-report.test.mjs b/tests/frontend/project-dossier-report.test.mjs index 8e253ccc..7d91670e 100644 --- a/tests/frontend/project-dossier-report.test.mjs +++ b/tests/frontend/project-dossier-report.test.mjs @@ -295,6 +295,15 @@ void test('print/PDF HTML escapes user content and explains omitted annexes', as assert.match(pdf, /<script>/); assert.match(pdf, /não foram seleccionados|secção de anexos foi omitida/); assert.match(pdf, /@page\{size:A4/); + assert.match(pdf, /@bottom-left\{content:/); + assert.match(pdf, /@bottom-right\{content:counter\(page\)/); + assert.doesNotMatch(pdf, /position:fixed|bottom:-14mm/); + const escapedVersion = await createProjectDossierPrintHtml( + { ...report, version: 'Revisão "QA" ' }, + [], + false, + ); + assert.doesNotMatch(escapedVersion, /