From 165c2f61712157925bb0e13ebd3235a6c4dfd98f Mon Sep 17 00:00:00 2001 From: nala Date: Fri, 23 Oct 2026 11:22:39 +0200 Subject: [PATCH] fix: keep internal agribusiness delivery notes out of portal presentation --- app/Models/Opportunity.php | 7 ++++ app/Services/OpportunityPromotion.php | 1 + app/Services/OpportunityPublicText.php | 32 +++++++++++++++++++ .../unification/traceability-and-manual.md | 8 +++++ .../investment-opportunity-choices.tsx | 5 ++- resources/js/pages/investment/detail.tsx | 10 +----- .../js/pages/investor-path/context-start.tsx | 2 +- .../management/corridor-agribusiness.tsx | 3 +- tests/Unit/OpportunityPublicTextTest.php | 28 ++++++++++++++++ 9 files changed, 81 insertions(+), 15 deletions(-) create mode 100644 app/Services/OpportunityPublicText.php create mode 100644 tests/Unit/OpportunityPublicTextTest.php diff --git a/app/Models/Opportunity.php b/app/Models/Opportunity.php index 5f18b14e..f902bfc6 100644 --- a/app/Models/Opportunity.php +++ b/app/Models/Opportunity.php @@ -31,6 +31,13 @@ class Opportunity extends Model protected $appends = ['maturity_catalogue_version']; + public function toArray(): array + { + $data = parent::toArray(); + if (isset($data['description'])) $data['description'] = \App\Services\OpportunityPublicText::description($this, $data['description']); + return $data; + } + public function getMaturityCatalogueVersionAttribute(): ?string { // Only published metadata is public; never expose the current private draft. diff --git a/app/Services/OpportunityPromotion.php b/app/Services/OpportunityPromotion.php index 623a46d5..608fc8c4 100644 --- a/app/Services/OpportunityPromotion.php +++ b/app/Services/OpportunityPromotion.php @@ -79,6 +79,7 @@ final class OpportunityPromotion 'Limitações e lacunas: '.($text('limitations') ?: 'Informação ainda por completar.'), 'Fonte: '.($text('source_reference') ?: 'Não indicada.'), 'Edição: '.($text('edition') ?: 'Não indicada.'), 'Data-base: '.($data['source_date'] ?? 'Não indicada.')])); + $body = OpportunityPublicText::description($record, $body); $projection = ['title' => $text('title'), 'summary' => $text('summary'), 'description' => $body, 'sector_id' => $data['sector_id'] ?? null, 'maturity_label' => 'Em preparação', 'notice' => self::NOTICE, 'territories' => array_map(fn ($p) => ['id' => end($p)->id, 'label' => implode(' / ', array_column($p, 'name'))], $paths)]; diff --git a/app/Services/OpportunityPublicText.php b/app/Services/OpportunityPublicText.php new file mode 100644 index 00000000..5e296a3a --- /dev/null +++ b/app/Services/OpportunityPublicText.php @@ -0,0 +1,32 @@ +workflow_context, 'corridor_agribusiness'); + if (! is_array($source) || ($source['source_sha256'] ?? null) !== CorridorAgribusinessOpportunities::SOURCE_HASH) return $text; + + $hash = CorridorAgribusinessOpportunities::SOURCE_HASH; + $sourceLine = 'Fonte: '.$source['source'].'; SHA-256 '.$hash; + $paragraphs = explode("\n\n", $text); + $result = []; + foreach ($paragraphs as $paragraph) { + $paragraph = trim($paragraph); + if (in_array($paragraph, [$sourceLine, $sourceLine.'.', 'Edição: Não indicada.', 'Data-base: Não indicada.', + 'Limitações e lacunas: '.CorridorAgribusinessOpportunities::LIMITATIONS, + OpportunityPromotion::NOTICE], true)) continue; + if ($paragraph === 'Enquadramento documental: '.$source['corridor'].'.') $paragraph = $source['corridor'].'.'; + if ($paragraph === CorridorAgribusinessOpportunities::LIMITATIONS) { + $paragraph = 'A localização exacta, a disponibilidade de terreno e as condições do investimento serão definidas durante a preparação do projecto.'; + } + if ($paragraph !== '') $result[] = $paragraph; + } + return implode("\n\n", $result); + } +} diff --git a/docs/investmoz/unification/traceability-and-manual.md b/docs/investmoz/unification/traceability-and-manual.md index 1f00ba61..c8d30645 100644 --- a/docs/investmoz/unification/traceability-and-manual.md +++ b/docs/investmoz/unification/traceability-and-manual.md @@ -1,5 +1,13 @@ # Rastreabilidade, utilização e formação +## Apresentação sem notas internas — 23/10/2026 + +Correcção solicitada pela Tempo: a interface deve comunicar a tarefa e o conteúdo, sem narrar decisões de implementação. Simplificados o cabeçalho da gestão do lote, a entrada no roteiro e as escolhas de oportunidades. Na ficha pública, removido o alerta genérico de falta de data de validação; mantém-se o estado **Em preparação** e a indicação de que a execução depende das condições e autorizações aplicáveis. + +A apresentação das 34 fichas elimina exclusivamente os parágrafos automáticos conhecidos de nome de ficheiro, hash e metadados vazios. Conserva a actividade, o corredor e a informação útil sobre definição de localização e condições. A mesma apresentação é usada na serialização HTML/API e na prévia. Não há alteração de dados, fontes, versões, permissões, auditoria ou publicação; textos posteriormente revistos pela equipa são preservados. As notas e originais continuam no cadastro interno. Esta correcção não declara revista a totalidade das páginas do portal. + +Verificação: teste novo de apresentação/preservação (7 asserções), quatro testes do ciclo promocional e controlo de acesso passaram; tipos e build passaram; ficha conferida no navegador QA. A execução alargada encontrou três testes históricos incompatíveis com o estado actual da fixture (lote já divulgado e directório de contas excluído da candidata anterior); não são declarados aprovados nesta passagem nem foram alterados para ocultar essas diferenças. + ## Orientação do lote de Agronegócio — catálogo e selecção múltipla, 23/10/2026 **Homologacao concluida, 23/10/2026:** 34/34 fichas cadastradas e divulgadas como **Em preparação**, pela interface da conta real, com previa, motivo, confirmacao e MFA recente. Revisao servida `e12ef6ad426ff6ebc4e7421a1ddbe7f184d77a55` (funcional `c641b267`), integrada pelo Git, sem migration. [Catalogo publico](https://teste-investidor.tempo.co.mz/oportunidades?sector=agronegocio), [gestao unica](https://teste-investidor.tempo.co.mz/gestao-conteudos) e [gestao do lote](https://teste-investidor.tempo.co.mz/minhas-oportunidades/lotes/agronegocio). A divulgacao nao autoriza execucao. [Recibo final](agribusiness-homologation-20261023.json). diff --git a/resources/js/components/investment-opportunity-choices.tsx b/resources/js/components/investment-opportunity-choices.tsx index 57ebe357..5099825f 100644 --- a/resources/js/components/investment-opportunity-choices.tsx +++ b/resources/js/components/investment-opportunity-choices.tsx @@ -10,10 +10,9 @@ export default function OpportunityChoices({choices, onChange, disabled = false}

Cada ficha conserva a sua identidade e fonte. Os dados gerais e a simulação têm como referência principal a primeira ficha; não somam investimentos ou benefícios de fichas diferentes.

{choices.map((c,i) =>

{i + 1}. {c.opportunity.title}

-

{c.opportunity.key} · versão {c.opportunity.version ?? 'não indicada'}

- {c.coverage &&

Abrangência documental: {c.coverage}

} + {c.coverage &&

Abrangência: {c.coverage}

}

Localização: {c.territory?.title ?? 'Por escolher — não existe implantação documentada nesta ficha.'}

- {c.source &&

Fonte: {c.source}

} + {c.source && !/SHA-256|\.docx/i.test(c.source) &&

Fonte: {c.source}

} {onChange && <>
Actividades que pretende realizar nesta oportunidade{Object.entries({production:'Produção agrícola',processing:'Transformação agroindustrial',trade:'Comercialização e exportação',transport:'Transporte e logística',water:'Captação/utilização de água',forestry:'Actividade florestal',animal:'Produção/serviços pecuários'}).map(([key,label])=>)}
update(i,{territory_path:path,territory:path.length ? {key:'territory:'+path.at(-1)!.id,kind:'territory',id:path.at(-1)!.id,title:path.map(p=>p.name).join(' / '),href:'/oportunidades',version:null,sha256:'',fields:{}} : null})}/> diff --git a/resources/js/pages/investment/detail.tsx b/resources/js/pages/investment/detail.tsx index 7bbd55f0..571af389 100644 --- a/resources/js/pages/investment/detail.tsx +++ b/resources/js/pages/investment/detail.tsx @@ -32,7 +32,7 @@ export default function Detail({ const [sharing, setSharing] = useState(''); return ( - {r.maturity_label === 'Em preparação' &&

Em preparação — divulgação promocional. A divulgação não autoriza a execução do investimento.

} + {r.maturity_label === 'Em preparação' &&

Em preparação. Consulte a oportunidade e prepare o seu investimento. A execução depende das condições e autorizações aplicáveis ao projecto.

} {r.reference} · {r.sector?.name} · {r.province} {r.district}

- {!r.last_validated_at && ( -

- Esta ficha não regista data de validação documental. - Confirme a origem e as condições antes de agir. Preparar uma - manifestação expressa interesse; não confirma a oferta nem - representa aprovação. -

- )}

{r.summary}

diff --git a/resources/js/pages/investor-path/context-start.tsx b/resources/js/pages/investor-path/context-start.tsx index bfde9b11..71332d11 100644 --- a/resources/js/pages/investor-path/context-start.tsx +++ b/resources/js/pages/investor-path/context-start.tsx @@ -103,7 +103,7 @@ export default function ContextStart({ >

Iniciar uma nova ideia

- Utiliza o cadastro e as cinco fases/19 etapas já existentes. + Organize a sua ideia e acompanhe a preparação do investimento.