audit(offers): record acceptance blockers before homologation

This commit is contained in:
2026-11-14 18:25:09 +02:00
parent 528a2597ed
commit ed2fd86f6b
10 changed files with 360 additions and 1 deletions
@@ -10199,3 +10199,6 @@ Denúncias fundamentadas ficam cifradas com origem, actor e versão, ligadas a t
Verificação: percurso novo inicialmente com 12 testes/151 asserções aprovados; regressão integrada final com 59 testes/780 asserções aprovados, com resultados no recibo outputs/offers-private-phase5-20261114/qa-result.json. Grupos sobrepostos não devem ser somados. Relatório técnico e matriz de aceitação das cinco fases: docs/investmoz/v2/auditoria-portal/ofertas-fase5-fecho-qa-20261114.md. A matriz RF-MN-003 existente foi actualizada, sem novo inventário geral.
Estado: cinco fases preparadas em QA; F1-F4 aceites tecnicamente com ressalvas, F5 para apreciação; nenhuma instalação ou publicação efectiva. Mantêm-se pendências NDA/exemplar/versão/aprovação, tipos jurídicos de comprovativos, prazo final, avisos jurídicos, visual desktop/móvel, GD/WebP e email real F2. Formatação global mantém61ficheiros preexistentes fora do delta. NDA mantém configuração nula; sem migrações, alterações em mne, dados operacionais, pagamentos ou promoção comercial. Procuras/restante portal não encerrados por este lote. Próximo passo: apreciação QA e decisões documentais; instalação/activação/publicação aguardam autorização expressa.
# Auditoria de aceitação Ofertas — 14/11/2026, antes de instalar
Referência: `../v2/auditoria-portal/ofertas-aceitacao-final-20261114.md`. Candidata `528a2597`; commits e hashes F1–F5 conferidos, JUnit aceite 59 testes/780 asserções confirmado sem o apresentar como nova execução. Nova regressão dos componentes nativos: 14/227 aprovada. Novo teste de integração: 2/11, um aprovado e uma falha na reutilização da descrição da Oferta pelo contexto de investimento; recuperação financeira privada e isolamento passaram. A aceitação integral permanece bloqueada: falta demonstrar Oferta → dossier → viabilidade em função da localização → relatório → recuperação. Publicação efectiva não implementada/activada; NDA real, visual/móvel, imagens e entrega real de email mantêm os estados específicos do relatório. Plano de instalação preparado, zero novas migrações no delta, schema do destino e reversão compatível por conferir. Nenhuma instalação ou alteração funcional efectuada nesta auditoria. RF-MN-003 actualizado sem alterar os restantes requisitos.
File diff suppressed because one or more lines are too long
@@ -0,0 +1,64 @@
# Ofertas — auditoria final antes da instalação
14/11/2026. Candidata auditada: `528a2597`. Auditoria exclusiva em QA, por instrução da Tempo. Nenhuma instalação, activação pública, migração operacional ou alteração funcional realizada nesta passagem.
**Decisão: aprovação técnica parcial com ressalvas; aceitação integral bloqueada.** As provas das cinco fases são válidas para os comportamentos que demonstram. Não demonstram uma publicação operacional nem a continuidade completa Oferta → dossier → viabilidade territorial. Foi reproduzida uma perda de reutilização da descrição nessa fronteira.
## Evidência e limites
- Confirmada a ancestralidade dos commits F1 `1f2990e4`, plano `6e302db6`, F2 `91c18573`, F3 `7691cab3`, F4 `e431de8b` e F5 `528a2597`.
- Conferidos todos os hashes de código e provas do recibo F5, antes das alterações documentais desta auditoria: todos correspondem. O JUnit original confirma **59 testes, 780 asserções, zero erros/falhas**. Não se apresenta esta conferência como nova execução dos 59 testes.
- Nova execução de `OpportunityManagementTest`, `OpportunitySearchTest` e `OpportunityResourcesTest`: **14 testes / 227 asserções aprovados**, na candidata, para compatibilidade com cadastro nativo, versões públicas, pesquisa/lista/mapa/exportação, geografia e recursos.
- Novo `OfferInvestmentAcceptanceAuditTest`: **2 testes / 11 asserções; 1 aprovado e 1 falhado**. Recuperação financeira privada e isolamento na selecção de contexto passaram; reutilização da descrição falhou. A primeira execução revelou a chave ausente; a segunda tornou a observação numa asserção explícita, sem alterar o produto.
- Base exclusiva `offers_phase1_20261114_test`, loopback:55441, utilizador de testes existente e dados sintéticos; guardas do repositório preservadas. Não se executaram suites fixadas noutra base nem se enfraqueceram as respectivas guardas.
- Recibo: `outputs/offers-final-acceptance-20261114/receipt-verification.json`; execuções adicionais: `native-integration.xml/log` e `investment-acceptance.xml/log`, na mesma pasta. Não somar as asserções das várias passagens como medida de conclusão funcional.
- A compilação, PHPStan, tipos e lint focado aceites são reutilizados: não se alterou código funcional. Os 61 ficheiros com formatação preexistente foram novamente comparados por caminho e conteúdo Git: todos permanecem inalterados e fora do delta das cinco fases. Prova adicional em `matrix-and-format-verification.json`; isto não constitui aprovação de formatação global. O novo teste passou na verificação sintáctica PHP e no Pint; `git diff --check` passou.
## Matriz final consolidada
| Percurso / requisito | Estado | Evidência e limite concreto |
| --- | --- | --- |
| F1: criação, gravação, recuperação, edição e submissão privada no cadastro existente | aprovado com ressalvas | Suite F1, 14/146; mesma identidade e etags. Conferência visual pendente. |
| F2: submissão, revisão, rejeição fundamentada, correcção, reenvio e aprovação independente | aprovado com ressalvas | Suite F2, 11/161; decisões por snapshot, conflitos, tarefas, auditoria, autor/contribuidor sem auto-aprovação. Email real e visual pendentes. |
| Separação aprovação/publicação e bloqueios dos percursos alternativos | aprovado | Testes F2 e barreira do modelo/consumidores; aprovação privada não publica. |
| F3: filtros, listagem, detalhe, API, mapa e CSV | aprovado com ressalvas | Suite F3, 10/133, projecções exclusivamente sintéticas; moeda sem conversão, coordenadas administrativas. Limites 1.000 candidatos/30 territórios e visual por conferir. |
| Publicação controlada operacional | bloqueado | Não existe publicador efectivo das Ofertas. `OfferPublicCatalogue` só aceita projecções de teste por injecção interna em base isolada. Não é uma configuração pública pronta a ligar. |
| F4: campos de empresas/imóveis, recuperação e vínculo à versão | aprovado com ressalvas | Suite F4, 11/160; campos opcionais confirmados. Não certifica titularidade nem demonstra cálculo de rentabilidade. |
| Seis fotografias, ficheiros, quarentena e comprovativos internos | aprovado com ressalvas | Upload/limites/autorização/scan/cifra testados; processamento e galeria com derivadas não demonstrados por ausência de GD/WebP no QA. |
| NDA + autorização do vendedor + revogação + mudança de versão | aprovado com ressalvas | Mecanismo técnico testado com documentos sintéticos e revalidação em cada leitura. NDA aprovado real ausente; configuração nula mantém fechado. |
| F5: retirada imediata, expiração, revalidação e reactivações | aprovado com ressalvas | Suite F5, 13/180; fronteira temporal, idempotência, ciclo novo e grants antigos recusados. Prazo definitivo ainda por decidir; não se inventou duração. |
| Denúncias e tratamento no Dashboard, auditoria e destaques administrativos | aprovado com ressalvas | F5 cobre isolamento, fundamento cifrado, tarefas existentes, versões e responsável. Destaque só rascunho/arquivo, sem promoção/pagamentos. Visual pendente. |
| Isolamento de utilizadores, páginas, APIs, anexos, mapas e CSV | aprovado com ressalvas | Suites F1–F5 e regressão nativa; leitura pública limitada à projecção, comprovativos internos, sem originais externos. Não equivale a teste de intrusão independente nem validação visual. |
| Preservação da versão pública durante edição | aprovado com ressalvas | Contrato sintético de Ofertas e nova regressão nativa aprovados; não existe ainda versão pública operacional de Ofertas para demonstrar o percurso real. |
| Escrever uma vez: recuperar a descrição no contexto de investimento | bloqueado | Novo teste reproduz `project_summary` ausente embora `description` esteja guardada. `InvestorContext::reference` lê resumo canónico/`summary`, sem aproveitar a descrição da Oferta. |
| Reutilização de valores financeiros autorizados na viabilidade, por localização e factores do negócio | não verificado | Os campos privados são recuperáveis, mas não foi localizado um contrato específico que os leve ao simulador, explicite pressupostos territoriais e preserve alterações deliberadas. Não inferir viabilidade a partir de receita/caixa/preço. Falta prova integrada e decisão de mapeamento quando necessária. |
| Consulta da Oferta → acção no mesmo dossier → simulação → relatório → recuperação | bloqueado | Detalhe público apenas liga a regresso/denúncia e mapa; consulta normal fechada. O contexto nativo admite referências privadas autorizadas, mas não demonstra este percurso completo de aquisição/investimento. |
| Restantes oportunidades, pesquisa e recursos afectados | aprovado com ressalvas | Nova regressão 14/227 passou. Não constitui declaração de ausência de defeitos em todo o portal ou em serviços externos não ensaiados. |
| Desktop/móvel e acessibilidade funcional | não verificado | Última evidência do conector: `The browser access host is unavailable`; não foi repetido o ciclo de reinícios. HTML compilado não substitui conferência visual. |
| Entrega real de notificações por email | não verificado | Serviço existente e permissões foram testados com transporte de teste; SMTP/fila/recepção real não ensaiados. Não foram enviados emails operacionais. |
## Bloqueantes e ressalvas para decisão
1. **Integração funcional:** corrigir e repetir o teste da descrição; demonstrar, sem exposição pública, o percurso de contexto/dossier/viabilidade/relatório/recuperação. Definir o mapeamento autorizado de dados económicos e pressupostos da localização antes de transportar informação confidencial; preservar proveniência, moeda e escolhas deliberadas. Não confundir preço de aquisição com investimento total nem dados históricos com previsão.
2. **Publicação:** implementar e testar um publicador controlado, se autorizado em nova instrução. A aceitação do consumidor sintético não autoriza remover as barreiras existentes. Instalação privada de avaliação e abertura pública são decisões separadas.
3. **Jurídico:** faltam exemplar/versão/referência de aprovação do NDA, tipos admissíveis de comprovativo imobiliário, política final de validade e condições/avisos aplicáveis à divulgação. Não são motivos para invalidar as provas técnicas já aceites; bloqueiam os comportamentos dependentes.
4. **Imagens:** comprovar processamento seguro e derivadas numa instalação compatível, incluindo scan, metadados, acesso e limite de seis. Não instalar extensões nem declarar galeria pronta nesta auditoria.
5. **Visual e correio:** realizar conferência real desktop/móvel e entrega de email autorizada em ambiente de teste. Permanecem não verificados, sem solicitar que o utilizador repita autenticações nesta passagem.
6. **Reversão:** um retorno cego à F4 é inseguro como plano: a versão F4 de `PrivateOfferConfidentiality::approvedHash` não verifica `OfferAvailability`, enquanto a F5 o faz. Uma retirada de ciclo zero pode conservar o mesmo hash editorial. A reversão deve preservar a recusa de leitura de ofertas retiradas/expiradas e os históricos; não assumir que repor código antigo mantém as novas protecções.
Não foram corrigidas estas lacunas porque a instrução autoriza exclusivamente auditoria e preparação do plano. O teste falhado fica explícito como critério de aceitação por satisfazer; não foi convertido numa aprovação por caracterização da falha.
## Plano de instalação controlada — não executado
1. **Antes de pedir autorização:** resolver/aceitar expressamente os bloqueantes aplicáveis ao âmbito a instalar, rever o novo delta, repetir os testes afectados e fechar um manifesto único com SHA da candidata e provas. Para avaliação estritamente privada, conservar NDA nulo, publicação indisponível, prazo pendente e agendamento desactivado; declarar as limitações. A candidata actual não tem aceitação integral para instalação.
2. **Destino e processo:** confirmar `teste-investidor.tempo.co.mz`, revisão efectivamente instalada, ramo, árvore limpa, recibo do executor Git existente e diferença exacta desde o activo. Não usar o destino antigo do guia genérico como evidência do destino actual. Não inventar comandos/identidade de executor nem retomar uma sessão SSH histórica. Esta auditoria não consultou o servidor.
3. **Estrutura:** o delta completo `1f2990e4^..528a2597` contém **zero ficheiros de migração**. As fases reutilizam Opportunity/JSON, recursos/documentos, perfis, editorial, tarefas e auditoria. Antes de instalar, comparar em leitura o schema e o registo de migrações do destino com estas dependências. Ausência de nova migração no pacote não prova que o destino já possui todas as estruturas; qualquer falta exige plano aditivo concreto e autorização. Não executar `migrate:fresh`, seeders ou alterações a `mne`.
4. **Cópia e ensaio:** backup consistente da base, ficheiros privados, configuração e chaves existentes sob protecção; registar revisão e manifestos sem segredos. Ensaiar restauro isolado, integridade de anexos cifrados e leitura de histórico, comparando contagens/hash e identificadores com o backup. Preservar integralmente instituições, demografia, dossiers e integrações; não presumir contagens actuais a partir de recibos antigos.
5. **Instalação após autorização expressa:** executar apenas o manifesto revisto pelo processo Git existente; compilar com versões fixadas, preservar segredos/configuração, rever caches/workers segundo o executor aceite. Sem publicação automática, concessões de perfis, pagamento, destaque comercial ou activação do agendamento. Não copiar exemplares NDA sintéticos para configuração operacional.
6. **Verificações posteriores:** conferir SHA/schema/delta, prontidão e logs sem segredos; acessos válidos e inválidos, recuperação de oferta e versões, ausência no catálogo nativo/público, ficheiros reservados, denúncias e tarefas, regressão dos destinos afectados. Testes de escrita apenas em contexto sintético autorizado e identificado; não submeter processos reais. Conferência desktop/móvel e, se autorizado, email de teste com destinatário controlado. Registar quem verificou, versão e resultado.
7. **Reversão:** guardar revisão anterior e artefactos, mas condicionar reversão de código à compatibilidade com v2/v3/v4 e novas decisões operacionais. Usar uma revisão de recuperação que conserve as guardas F5; se indisponível, não abrir os percursos afectados. Não remover JSON, tarefas, denúncias, decisões ou grants para acomodar código antigo. Não restaurar indiscriminadamente a base sobre novos registos; reconciliação/restauro de dados exige decisão própria e preservação do histórico.
8. **Aprovação separada:** apresentar resultado e recibo pós-instalação. Qualquer futura publicação efectiva requer desenvolvimento/provas e autorização próprios. Este plano não é autorização para instalar nem para activar.
## Resultado e próxima acção
Auditoria documental, hashes e regressões delimitadas concluídos com achados. **O módulo não está integralmente aceite para o percurso de investimento solicitado.** Próxima acção recomendada: autorizar a correcção da continuidade Oferta → contexto/dossier e a sua prova de viabilidade, mantendo os bloqueios públicos; depois concluir as verificações dependentes e submeter o pacote à autorização de homologação. Nenhuma instalação efectuada.
@@ -0,0 +1 @@
{"tool":"phpunit","result":"failed","tests":2,"passed":1,"assertions":11,"duration_ms":23058,"failed":1,"failures":[{"test":"Tests\\Feature\\OfferInvestmentAcceptanceAuditTest::test_saved_offer_reuses_its_description_in_the_existing_investment_context","file":"C:\\laragon\\www\\portal-investidor-qa-editorial-20261113\\tests\\Feature\\OfferInvestmentAcceptanceAuditTest.php","line":43,"message":"Write once/read always: the existing description must not disappear at the investment-context boundary.\nFailed asserting that null is identical to 'Existing company description to reuse once in investment preparation.'."}]}
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
<testsuite name="Tests\Feature\OfferInvestmentAcceptanceAuditTest" file="C:\laragon\www\portal-investidor-qa-editorial-20261113\tests\Feature\OfferInvestmentAcceptanceAuditTest.php" tests="2" assertions="11" errors="0" failures="1" skipped="0" time="23.049038">
<testcase name="test_saved_offer_reuses_its_description_in_the_existing_investment_context" file="C:\laragon\www\portal-investidor-qa-editorial-20261113\tests\Feature\OfferInvestmentAcceptanceAuditTest.php" line="37" class="Tests\Feature\OfferInvestmentAcceptanceAuditTest" classname="Tests.Feature.OfferInvestmentAcceptanceAuditTest" assertions="4" time="17.378447">
<failure type="PHPUnit\Framework\ExpectationFailedException">Tests\Feature\OfferInvestmentAcceptanceAuditTest::test_saved_offer_reuses_its_description_in_the_existing_investment_context&#13;
Write once/read always: the existing description must not disappear at the investment-context boundary.
Failed asserting that null is identical to 'Existing company description to reuse once in investment preparation.'.
&#13;
C:\laragon\www\portal-investidor-qa-editorial-20261113\tests\Feature\OfferInvestmentAcceptanceAuditTest.php:43</failure>
</testcase>
<testcase name="test_native_context_options_do_not_disclose_another_owners_private_offer" file="C:\laragon\www\portal-investidor-qa-editorial-20261113\tests\Feature\OfferInvestmentAcceptanceAuditTest.php" line="47" class="Tests\Feature\OfferInvestmentAcceptanceAuditTest" classname="Tests.Feature.OfferInvestmentAcceptanceAuditTest" assertions="7" time="5.670591"/>
</testsuite>
</testsuites>
@@ -0,0 +1,7 @@
{
"format_failures": 61,
"baseline_paths": 61,
"intersection_with_module_delta": [],
"all_git_files_unchanged_since_phase1_closure": true,
"only_matrix_row_changed": "RF-MN-003"
}
@@ -0,0 +1 @@
{"tool":"phpunit","result":"passed","tests":14,"passed":14,"assertions":227,"duration_ms":22125}
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
<testsuite name="CLI Arguments" tests="14" assertions="227" errors="0" failures="0" skipped="0" time="22.109963">
<testsuite name="Tests\Feature\OpportunityManagementTest" file="C:\laragon\www\portal-investidor-qa-editorial-20261113\tests\Feature\OpportunityManagementTest.php" tests="3" assertions="46" errors="0" failures="0" skipped="0" time="11.864628">
<testcase name="test_installation_requires_management_permission_and_current_proofs" file="C:\laragon\www\portal-investidor-qa-editorial-20261113\tests\Feature\OpportunityManagementTest.php" line="20" class="Tests\Feature\OpportunityManagementTest" classname="Tests.Feature.OpportunityManagementTest" assertions="6" time="9.826727"/>
<testcase name="test_incomplete_draft_recovers_and_soft_delete_restore_preserves_identity" file="C:\laragon\www\portal-investidor-qa-editorial-20261113\tests\Feature\OpportunityManagementTest.php" line="32" class="Tests\Feature\OpportunityManagementTest" classname="Tests.Feature.OpportunityManagementTest" assertions="21" time="1.154303"/>
<testcase name="test_closed_version_edits_do_not_change_public_values_classification_or_relations" file="C:\laragon\www\portal-investidor-qa-editorial-20261113\tests\Feature\OpportunityManagementTest.php" line="60" class="Tests\Feature\OpportunityManagementTest" classname="Tests.Feature.OpportunityManagementTest" assertions="19" time="0.883598"/>
</testsuite>
<testsuite name="Tests\Feature\OpportunitySearchTest" file="C:\laragon\www\portal-investidor-qa-editorial-20261113\tests\Feature\OpportunitySearchTest.php" tests="6" assertions="112" errors="0" failures="0" skipped="0" time="6.293386">
<testcase name="test_one_park_selector_matches_public_associations_not_proximity_or_shared_territory" file="C:\laragon\www\portal-investidor-qa-editorial-20261113\tests\Feature\OpportunitySearchTest.php" line="26" class="Tests\Feature\OpportunitySearchTest" classname="Tests.Feature.OpportunitySearchTest" assertions="9" time="1.627457"/>
<testcase name="test_historical_and_current_preparation_with_equal_numbers_are_not_equivalent" file="C:\laragon\www\portal-investidor-qa-editorial-20261113\tests\Feature\OpportunitySearchTest.php" line="38" class="Tests\Feature\OpportunitySearchTest" classname="Tests.Feature.OpportunitySearchTest" assertions="6" time="0.509868"/>
<testcase name="test_amount_requires_currency_and_combines_with_sector_location_and_modality" file="C:\laragon\www\portal-investidor-qa-editorial-20261113\tests\Feature\OpportunitySearchTest.php" line="47" class="Tests\Feature\OpportunitySearchTest" classname="Tests.Feature.OpportunitySearchTest" assertions="10" time="0.710202"/>
<testcase name="test_zone_options_are_not_filtered_by_opportunity_text_or_modality" file="C:\laragon\www\portal-investidor-qa-editorial-20261113\tests\Feature\OpportunitySearchTest.php" line="58" class="Tests\Feature\OpportunitySearchTest" classname="Tests.Feature.OpportunitySearchTest" assertions="7" time="0.531233"/>
<testcase name="test_list_map_and_export_keep_the_same_public_park_results" file="C:\laragon\www\portal-investidor-qa-editorial-20261113\tests\Feature\OpportunitySearchTest.php" line="67" class="Tests\Feature\OpportunitySearchTest" classname="Tests.Feature.OpportunitySearchTest" assertions="16" time="1.179871"/>
<testcase name="test_public_catalogue_consumers_keep_their_own_result_collections" file="C:\laragon\www\portal-investidor-qa-editorial-20261113\tests\Feature\OpportunitySearchTest.php" line="80" class="Tests\Feature\OpportunitySearchTest" classname="Tests.Feature.OpportunitySearchTest" assertions="64" time="1.734754"/>
</testsuite>
<testsuite name="Tests\Feature\OpportunityResourcesTest" file="C:\laragon\www\portal-investidor-qa-editorial-20261113\tests\Feature\OpportunityResourcesTest.php" tests="5" assertions="69" errors="0" failures="0" skipped="0" time="3.951950">
<testcase name="test_encrypted_original_scan_pending_recovery_replacement_and_logical_lifecycle" file="C:\laragon\www\portal-investidor-qa-editorial-20261113\tests\Feature\OpportunityResourcesTest.php" line="47" class="Tests\Feature\OpportunityResourcesTest" classname="Tests.Feature.OpportunityResourcesTest" assertions="24" time="0.989391"/>
<testcase name="test_multiple_locations_without_coordinates_and_invalid_hierarchy_and_geometry" file="C:\laragon\www\portal-investidor-qa-editorial-20261113\tests\Feature\OpportunityResourcesTest.php" line="82" class="Tests\Feature\OpportunityResourcesTest" classname="Tests.Feature.OpportunityResourcesTest" assertions="14" time="0.667104"/>
<testcase name="test_foreign_country_rejects_mozambican_hierarchy_without_changing_the_saved_draft" file="C:\laragon\www\portal-investidor-qa-editorial-20261113\tests\Feature\OpportunityResourcesTest.php" line="100" class="Tests\Feature\OpportunityResourcesTest" classname="Tests.Feature.OpportunityResourcesTest" assertions="20" time="1.242190"/>
<testcase name="test_invalid_upload_and_cross_opportunity_links_do_not_create_resources" file="C:\laragon\www\portal-investidor-qa-editorial-20261113\tests\Feature\OpportunityResourcesTest.php" line="132" class="Tests\Feature\OpportunityResourcesTest" classname="Tests.Feature.OpportunityResourcesTest" assertions="7" time="0.641653"/>
<testcase name="test_image_processing_is_explicit_and_every_derivative_requires_its_own_scan" file="C:\laragon\www\portal-investidor-qa-editorial-20261113\tests\Feature\OpportunityResourcesTest.php" line="141" class="Tests\Feature\OpportunityResourcesTest" classname="Tests.Feature.OpportunityResourcesTest" assertions="4" time="0.411613"/>
</testsuite>
</testsuite>
</testsuites>
@@ -0,0 +1,187 @@
{
"candidate": "528a2597",
"phase_ancestry": {
"1f2990e4": true,
"6e302db6": true,
"91c18573": true,
"7691cab3": true,
"e431de8b": true,
"528a2597": true
},
"hash_checks": [
{
"path": "app\\Console\\Commands\\ExpirePrivateOffers.php",
"matches": true
},
{
"path": "app\\Http\\Controllers\\OfferPublicCatalogueController.php",
"matches": true
},
{
"path": "app\\Http\\Controllers\\PortalEditorialWorkflowController.php",
"matches": true
},
{
"path": "app\\Http\\Controllers\\PrivateOfferOperationsController.php",
"matches": true
},
{
"path": "app\\Http\\Controllers\\PrivateOfferPreparationController.php",
"matches": true
},
{
"path": "app\\Services\\OfferAvailability.php",
"matches": true
},
{
"path": "app\\Services\\OfferPublicCatalogue.php",
"matches": true
},
{
"path": "app\\Services\\OpportunityProfileWorkflow.php",
"matches": true
},
{
"path": "app\\Services\\PrivateOfferConfidentiality.php",
"matches": true
},
{
"path": "app\\Services\\PrivateOfferOperations.php",
"matches": true
},
{
"path": "app\\Services\\PrivateOfferPreparation.php",
"matches": true
},
{
"path": "config\\private_offers.php",
"matches": true
},
{
"path": "docs\\investmoz\\unification\\traceability-and-manual.md",
"matches": true
},
{
"path": "docs\\investmoz\\v2\\auditoria-portal\\matriz-requisitos.csv",
"matches": true
},
{
"path": "docs\\investmoz\\v2\\auditoria-portal\\ofertas-fase5-fecho-qa-20261114.md",
"matches": true
},
{
"path": "resources\\js\\components\\offer-operations.tsx",
"matches": true
},
{
"path": "resources\\js\\components\\public-offer-card.tsx",
"matches": true
},
{
"path": "resources\\js\\lib\\public-offer.ts",
"matches": true
},
{
"path": "resources\\js\\pages\\management\\editorial-workflow.tsx",
"matches": true
},
{
"path": "resources\\js\\pages\\offers\\preparation-index.tsx",
"matches": true
},
{
"path": "resources\\js\\pages\\offers\\preparation.tsx",
"matches": true
},
{
"path": "resources\\js\\pages\\offers\\public-detail.tsx",
"matches": true
},
{
"path": "resources\\js\\pages\\offers\\report.tsx",
"matches": true
},
{
"path": "routes\\console.php",
"matches": true
},
{
"path": "routes\\web.php",
"matches": true
},
{
"path": "tests\\Feature\\PrivateOfferOperationsTest.php",
"matches": true
},
{
"path": "outputs\\offers-private-phase5-20261114\\focused.log",
"matches": true
},
{
"path": "outputs\\offers-private-phase5-20261114\\focused.xml",
"matches": true
},
{
"path": "outputs\\offers-private-phase5-20261114\\regression.log",
"matches": true
},
{
"path": "outputs\\offers-private-phase5-20261114\\regression.xml",
"matches": true
},
{
"path": "outputs\\offers-private-phase5-20261114\\global-format.log",
"matches": true
},
{
"path": "outputs\\offers-private-phase5-20261114\\build-final.log",
"matches": true
}
],
"all_hashes_match": true,
"junit_suites": [
{
"name": "Tests\\Feature\\PrivateOfferOperationsTest",
"tests": "13",
"assertions": "180",
"failures": "0",
"errors": "0"
},
{
"name": "Tests\\Feature\\PrivateOfferConfidentialityTest",
"tests": "11",
"assertions": "160",
"failures": "0",
"errors": "0"
},
{
"name": "Tests\\Feature\\OfferPublicCatalogueTest",
"tests": "10",
"assertions": "133",
"failures": "0",
"errors": "0"
},
{
"name": "Tests\\Feature\\PrivateOfferPreparationTest",
"tests": "14",
"assertions": "146",
"failures": "0",
"errors": "0"
},
{
"name": "Tests\\Feature\\PrivateOfferEditorialTest",
"tests": "11",
"assertions": "161",
"failures": "0",
"errors": "0"
}
],
"totals": {
"tests": 59,
"assertions": 780,
"failures": 0,
"errors": 0
},
"migration_delta": [],
"execution": "Read-only verification of accepted receipts; no operational connection or installation."
}
@@ -0,0 +1,58 @@
<?php
namespace Tests\Feature;
use App\Models\Opportunity;
use App\Models\User;
use App\Services\InvestorContext;
use App\Services\PortalProfileAdministration;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Str;
use Tests\TestCase;
/** Acceptance audit only: no change to the product's contracts or access rules. */
final class OfferInvestmentAcceptanceAuditTest extends TestCase
{
use RefreshDatabase;
private function prepare(): array
{
$this->withoutVite();
config(['portal.profile_access_enabled' => true]);
$owner = User::factory()->create();
app(PortalProfileAdministration::class)->insertGrant((string) Str::uuid(), User::factory()->create(), [
'user_id' => $owner->id, 'role' => 'promoter', 'organization_id' => null,
'opportunity_types' => ['business_sale'], 'source' => 'synthetic-acceptance-audit',
]);
$payload = ['creation_key' => (string) Str::uuid(), 'opportunity_category' => 'business_sale',
'title' => 'Synthetic acquisition acceptance', 'description' => 'Existing company description to reuse once in investment preparation.',
'private_offer' => ['price_mode' => 'amount', 'amount' => '120000.00', 'currency' => 'MZN',
'business' => ['revenue' => '60000.00', 'cash_flow' => '20000.00', 'currency' => 'MZN']],
'reason' => 'Synthetic final acceptance integration audit only.'];
$record = $this->actingAs($owner)->postJson('/minhas-oportunidades/ofertas/rascunhos', $payload)->assertCreated()->json('record');
return [$owner, $record, $payload];
}
public function test_saved_offer_reuses_its_description_in_the_existing_investment_context(): void
{
[$owner, $record, $payload] = $this->prepare();
$reference = app(InvestorContext::class)->reference('opportunity:'.$record['id']);
$this->assertSame('private', $reference['visibility']);
$this->assertSame($payload['title'], $reference['fields']['project_title']);
$this->assertSame($payload['description'], $reference['fields']['project_summary'] ?? null,
'Write once/read always: the existing description must not disappear at the investment-context boundary.');
}
public function test_native_context_options_do_not_disclose_another_owners_private_offer(): void
{
[$owner, $record, $payload] = $this->prepare();
$this->getJson('/minhas-oportunidades/ofertas/'.$record['id'])->assertOk()
->assertJsonPath('record.values.private_offer.business.revenue', '60000.00')
->assertJsonPath('record.values.private_offer.business.cash_flow', '20000.00');
$this->assertFalse(Opportunity::published()->whereKey($record['id'])->exists());
$this->actingAs(User::factory()->create());
$this->assertNotContains('opportunity:'.$record['id'], array_column(app(InvestorContext::class)->options(), 'key'));
$this->getJson('/minhas-oportunidades/ofertas/'.$record['id'])->assertForbidden();
}
}