fix(sector): use effective creation access and preserve corridor presentation contract
tests / ci (push) Canceled after 0s
tests / ci (push) Canceled after 0s
This commit is contained in:
@@ -17,8 +17,8 @@ final class InvestmentCorridorContext
|
||||
/** Reuse Tempo's approved provincial context; never infer a district or master link. */
|
||||
public function catalogueProvinces(Opportunity $record): array
|
||||
{
|
||||
if ($record->primary_territory_id || $record->additionalTerritories()->exists()) return [];
|
||||
if (data_get($record->workflow_context, 'corridor_agribusiness.source_sha256') !== CorridorAgribusinessOpportunities::SOURCE_HASH) return [];
|
||||
if ($record->primary_territory_id || $record->additionalTerritories()->exists()) return [];
|
||||
$key = $this->key($record);
|
||||
return $key ? $this->provinces($key) : [];
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ final class SectorInvestment
|
||||
|
||||
public function publicView(Sector $sector): array
|
||||
{
|
||||
$creation = app(ContentManagement::class)->access(request()->user())['opportunities']['create'] ?? null;
|
||||
$query = SectorOrganization::opportunities($sector);
|
||||
$articles = $this->guides($sector);
|
||||
$relations = [];
|
||||
@@ -67,7 +68,8 @@ final class SectorInvestment
|
||||
return ['articles' => $articles, 'relations' => collect($relations)->unique('id')->values()->all(), 'locations' => $locations,
|
||||
'start_href' => app(InvestorPath::class)->available() ? '/investimentos/a-partir-do-setor/'.$sector->slug : null,
|
||||
'start_notice' => app(InvestorPath::class)->available() ? null : 'O percurso para guardar a ideia está temporariamente indisponível. A consulta e o apoio continuam disponíveis.',
|
||||
'register_href' => app(PortalProfileAccess::class)->available() ? '/minhas-oportunidades/create?sector_id='.$sector->id : null,
|
||||
'register_href' => app(PortalProfileAccess::class)->available() ? ($creation ? $creation.'?sector_id='.$sector->id : '/perfil/promotor') : null,
|
||||
'register_label' => $creation ? 'Cadastrar oportunidade — escolher modalidade' : 'Pedir acesso para cadastrar oportunidades',
|
||||
'register_notice' => app(PortalProfileAccess::class)->available() ? null : 'O cadastro por perfis está temporariamente indisponível; pode preparar a ideia e contactar o apoio.',
|
||||
'specialization_href' => in_array($sector->slug, ['tecnologias-de-informacao', 'mercado-financeiro'], true) ? SectorOrganization::href('fintech') : null,
|
||||
'content_notice' => $sector->slug === 'mercado-financeiro' ? 'O artigo geral de Mercado Financeiro permanece vazio e não foi publicado. A preparação de negócios neste setor está disponível.' : null];
|
||||
|
||||
@@ -1799,3 +1799,10 @@ Formulários: entradas directas para os campos já existentes da preparação do
|
||||
Provas delimitadas: 8 testes frontend de contrato/SSR e 3 testes isolados/12 verificações, zero falhas; tipos, compilação e sintaxe PHP passaram. Dados sintéticos revertidos em QA. Provas nacionais/selector/catálogo anteriores reutilizadas. Turismo: leitura anónima activa HTTP200 e QA visitante/conta sem Promotor HTTP200; a recusa da sessão real relatada não foi reproduzida, nem atribuída a uma permissão sem prova. A conferência autenticada e visual continua pendente. Este registo não instala o candidato nem encerra a implementação transversal. L8 permanece parcial.
|
||||
|
||||
Evidência: `investor-usability-qa-20261028.json`. Actualizados V2-01,V2-07,V2-10 na matriz existente; sem nova matriz ou backlog.
|
||||
|
||||
|
||||
### 28/10 — botão de cadastro e compatibilidade do pipeline
|
||||
|
||||
A Tempo esclareceu que a recusa em Turismo ocorreu depois de clicar num botão; não foi possível recuperar o nome. A rota pública continuou HTTP200. Conferência de forma pública/origem não identificou anexos privados nos documentos públicos de Turismo; não se atribui a recusa à perda de publicação. Encontrada uma falha delimitada no consumidor: o botão de cadastro dependia da disponibilidade do módulo, sem a capacidade efectiva da conta. Corrigido para reutilizar o destino autorizado de criação de ContentManagement; sem essa capacidade, mostra expressamente o pedido de acesso já existente. Não cria papéis nem restringe a consulta pública ou a preparação de ideias. A confirmação visual da acção real permanece pendente.
|
||||
|
||||
O candidato 502e3ec4 falhou na Unit de agrupamento de corredores antes da troca de release; 6e8de43a ficou servido. Causa: a simulação do catálogo não representava a nova apresentação de localização e uma consulta era feita antes de conferir se a fonte era aplicável. Correcção: conferir primeiro a fonte e simular a nova dependência no teste, conservando as verificações anteriores de identidade, grupos e contagens. Regressão Unit1/13 passou; não foi alterado o pipeline, o contrato de schema ou a exigência dos testes. Frontend9, Feature3/14, tipos e compilação passaram. Rótulos conhecidos de temas, categorias e idiomas do acervo traduzidos para apresentação; valores dos filtros preservados.
|
||||
|
||||
@@ -18,13 +18,14 @@
|
||||
"profile_request": "Portuguese visible representation/modality labels;owned clean-file criteria unchanged;file search and explicit selected proofs replace the unbounded list;availability does not prove evidentiary suitability",
|
||||
"zones": "Territory and other filter changes apply search;old records hidden while loading/error;all and filtered totals plus pagination explicit;existing public visibility preserved;validated legal coverage includes its child area,while influence is not treated as a site",
|
||||
"my_investments": "Next action remains prominent;other phases/stages expandable;source/code history and internal follow-up remain consultable rather than filling the main page",
|
||||
"forms": "Direct entries to existing private investment preparation fields and Terra/DUAT;support request separately labelled;originals/guides optional;no law is converted into a form or executable calculation"
|
||||
"forms": "Direct entries to existing private investment preparation fields and Terra/DUAT;support request separately labelled;originals/guides optional;no law is converted into a form or executable calculation",
|
||||
"document_taxonomy": "Known topic/category/language codes have Portuguese display labels;original query values preserved"
|
||||
},
|
||||
"QA": {
|
||||
"frontend_contract_and_SSR_tests": 8,
|
||||
"frontend_contract_and_SSR_tests": 9,
|
||||
"frontend_failures": 0,
|
||||
"isolated_feature_tests": 3,
|
||||
"feature_assertions": 12,
|
||||
"feature_assertions": 14,
|
||||
"feature_failures": 0,
|
||||
"database": "proactive_investment_20261027_test",
|
||||
"port": 55441,
|
||||
@@ -32,7 +33,9 @@
|
||||
"types": "passed",
|
||||
"build": "passed",
|
||||
"PHP_syntax": "passed",
|
||||
"accepted_national_selector_and_catalogue_proofs": "reused"
|
||||
"accepted_national_selector_and_catalogue_proofs": "reused",
|
||||
"unit_regression_tests": 1,
|
||||
"unit_regression_assertions": 13
|
||||
},
|
||||
"Tourism_incident": {
|
||||
"reported_by_Tempo": "authenticated session saw access denial at /sectores/turismo",
|
||||
@@ -40,7 +43,11 @@
|
||||
"isolated_guest_and_no_promoter_account_HTTP": 200,
|
||||
"exact_real_session_cause": "not demonstrated;do not infer missing role or claim correction",
|
||||
"private_permissions_changed": false,
|
||||
"next": "Confer the actual authenticated session and related logs;public read is not real-session proof"
|
||||
"next": "Confer the actual authenticated session and related logs;public read is not real-session proof",
|
||||
"reported_action": "Tempo clarified denial followed a button click;exact label could not be recalled",
|
||||
"bounded_code_finding": "Sector registration action depended on module availability rather than effective account creation capability",
|
||||
"QA_correction": "Reuse ContentManagement effective create destination;otherwise explicitly link to existing promoter request with a readable label;public consultation and idea preparation remain separate",
|
||||
"real_session_reconference": "pending;the exact original button is not demonstrated"
|
||||
},
|
||||
"limits": [
|
||||
"Not installed in this QA report",
|
||||
@@ -54,5 +61,13 @@
|
||||
"data_and_permissions_preservation": "permanent project instructions",
|
||||
"grouping_and_collapsed_details": "implementation choice,not new organisational requirement",
|
||||
"document_ownership_scan_and_existing_workflows": "prior contracts preserved"
|
||||
},
|
||||
"delivery_gate": {
|
||||
"candidate": "502e3ec4",
|
||||
"result": "failed before runtime switch;6e8de43a preserved",
|
||||
"reason": "Unit mock did not represent new public location presentation;unnecessary relation query on non-source records",
|
||||
"correction": "Short-circuit source applicability before reading relations;mock the new presentation dependency and retain prior identity/count assertions",
|
||||
"unit_regression": "1 test/13 assertions passed",
|
||||
"pipeline_guardrails": "unchanged;no schema migration or test-gate weakening"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7357,3 +7357,10 @@ Formulários: entradas directas para os campos já existentes da preparação do
|
||||
Provas delimitadas: 8 testes frontend de contrato/SSR e 3 testes isolados/12 verificações, zero falhas; tipos, compilação e sintaxe PHP passaram. Dados sintéticos revertidos em QA. Provas nacionais/selector/catálogo anteriores reutilizadas. Turismo: leitura anónima activa HTTP200 e QA visitante/conta sem Promotor HTTP200; a recusa da sessão real relatada não foi reproduzida, nem atribuída a uma permissão sem prova. A conferência autenticada e visual continua pendente. Este registo não instala o candidato nem encerra a implementação transversal. L8 permanece parcial.
|
||||
|
||||
Evidência: `investor-usability-qa-20261028.json`. Actualizados V2-01,V2-07,V2-10 na matriz existente; sem nova matriz ou backlog.
|
||||
|
||||
|
||||
### 28/10 — botão de cadastro e compatibilidade do pipeline
|
||||
|
||||
A Tempo esclareceu que a recusa em Turismo ocorreu depois de clicar num botão; não foi possível recuperar o nome. A rota pública continuou HTTP200. Conferência de forma pública/origem não identificou anexos privados nos documentos públicos de Turismo; não se atribui a recusa à perda de publicação. Encontrada uma falha delimitada no consumidor: o botão de cadastro dependia da disponibilidade do módulo, sem a capacidade efectiva da conta. Corrigido para reutilizar o destino autorizado de criação de ContentManagement; sem essa capacidade, mostra expressamente o pedido de acesso já existente. Não cria papéis nem restringe a consulta pública ou a preparação de ideias. A confirmação visual da acção real permanece pendente.
|
||||
|
||||
O candidato 502e3ec4 falhou na Unit de agrupamento de corredores antes da troca de release; 6e8de43a ficou servido. Causa: a simulação do catálogo não representava a nova apresentação de localização e uma consulta era feita antes de conferir se a fonte era aplicável. Correcção: conferir primeiro a fonte e simular a nova dependência no teste, conservando as verificações anteriores de identidade, grupos e contagens. Regressão Unit1/13 passou; não foi alterado o pipeline, o contrato de schema ou a exigência dos testes. Frontend9, Feature3/14, tipos e compilação passaram. Rótulos conhecidos de temas, categorias e idiomas do acervo traduzidos para apresentação; valores dos filtros preservados.
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -19,6 +19,7 @@ export type SectorEntryData = {
|
||||
}[];
|
||||
start_href: string | null;
|
||||
register_href: string | null;
|
||||
register_label?: string;
|
||||
start_notice: string | null;
|
||||
register_notice: string | null;
|
||||
specialization_href: string | null;
|
||||
@@ -58,7 +59,7 @@ export default function SectorEntry({
|
||||
className="rounded border px-4 py-3"
|
||||
href={data.register_href}
|
||||
>
|
||||
Cadastrar oportunidade — escolher modalidade
|
||||
{data.register_label ?? 'Cadastrar oportunidade — escolher modalidade'}
|
||||
</Link>
|
||||
) : (
|
||||
<p role="status">{data.register_notice}</p>
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
/** Presentation only. Original taxonomy identifiers remain the form/query values. */
|
||||
const names:Record<string,string>={
|
||||
agronegocio:'Agronegócio','bens-de-consumo-e-franquias':'Bens de consumo e franquias',energia:'Energia e mineração (catálogo histórico)',
|
||||
'energia-especifica':'Energia',fintech:'Fintech',fiscalidade:'Fiscalidade',imobiliario:'Imobiliário',infraestruturas:'Infraestruturas',
|
||||
iva:'IVA',mineracao:'Mineração','território':'Território',territorio:'Território',turismo:'Turismo','mercado-financeiro':'Mercado financeiro',
|
||||
education:'Formação e competências',media:'Imagens e conteúdos de apoio',pt:'Português',en:'Inglês',
|
||||
};
|
||||
export function documentFacetLabel(value:string):string{return names[value]??value;}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { PublicSectorCards } from '@/components/public-sector-cards';
|
||||
import { documentFacetLabel } from '@/lib/document-facet-label';
|
||||
import { PORTAL_MORE_LINK } from '@/lib/portal-presentation';
|
||||
import {
|
||||
PublicDocumentContent,
|
||||
@@ -579,7 +580,7 @@ export default function Services(p: Props) {
|
||||
>
|
||||
<option value="">Todos</option>
|
||||
{p.topics?.map((topic) => (
|
||||
<option key={topic}>{topic}</option>
|
||||
<option key={topic} value={topic}>{documentFacetLabel(topic)}</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
@@ -606,7 +607,7 @@ export default function Services(p: Props) {
|
||||
value={category}
|
||||
>
|
||||
{documentTypeLabels[category] ??
|
||||
category}
|
||||
documentFacetLabel(category)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
@@ -809,8 +810,8 @@ export default function Services(p: Props) {
|
||||
>
|
||||
<option value="">Todos</option>
|
||||
{p.languages?.map((language) => (
|
||||
<option key={language}>
|
||||
{language}
|
||||
<option key={language} value={language}>
|
||||
{documentFacetLabel(language)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
@@ -917,7 +918,7 @@ export default function Services(p: Props) {
|
||||
className="rounded-full border bg-white px-3 py-1 underline"
|
||||
href={`${p.section === 'documents' ? '/documentos' : '/legislacao'}?${new URLSearchParams({ topic }).toString()}`}
|
||||
>
|
||||
{topic}
|
||||
{documentFacetLabel(topic)}
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
@@ -20,6 +20,10 @@ final class InvestorUsabilityScopeTest extends TestCase
|
||||
$actor=User::factory()->create();
|
||||
$this->actingAs($actor)->get('/sectores/turismo')->assertOk();
|
||||
$this->assertSame(0,DB::table('portal_role_assignments')->where('user_id',$actor->id)->count());
|
||||
config(['portal.profile_access_enabled'=>true]);
|
||||
$entry=app(\App\Services\SectorInvestment::class)->publicView(\App\Models\Sector::where('slug','turismo')->firstOrFail());
|
||||
$this->assertSame('/perfil/promotor',$entry['register_href']);
|
||||
$this->assertSame('Pedir acesso para cadastrar oportunidades',$entry['register_label']);
|
||||
}
|
||||
|
||||
public function test_zones_all_equals_visible_set_and_province_and_district_never_fall_back_to_all():void
|
||||
|
||||
@@ -24,6 +24,10 @@ class OpportunityCorridorExplorerTest extends TestCase
|
||||
$catalogue->shouldReceive('query')->with($filters)->once()->andReturn($query);
|
||||
$catalogue->shouldReceive('context')->with(101)->once()->andReturn(['corridors' => collect([(object)['id'=>'a','name'=>'Corredor A'],(object)['id'=>'b','name'=>'Corredor B']])]);
|
||||
$catalogue->shouldReceive('context')->with(102)->once()->andReturn(['corridors'=>collect()]);
|
||||
$first->setAttribute('territorial_summary','Sofala / Dondo');
|
||||
$other->setAttribute('territorial_summary','Localização não indicada');
|
||||
$catalogue->shouldReceive('presentLocation')->with($first)->once()->andReturn($first);
|
||||
$catalogue->shouldReceive('presentLocation')->with($other)->once()->andReturn($other);
|
||||
$this->app->instance(InvestmentCatalogue::class, $catalogue);
|
||||
$groups = app(OpportunityCorridorExplorer::class)->groups($filters);
|
||||
$this->assertSame(['Corredor A','Corredor B','Outras localizações'], array_column($groups,'name'));
|
||||
@@ -32,5 +36,6 @@ class OpportunityCorridorExplorerTest extends TestCase
|
||||
$this->assertSame(['Sofala / Dondo'], $groups[0]['locations']);
|
||||
$this->assertSame([], $groups[2]['locations']);
|
||||
$this->assertSame([], $groups[0]['opportunities'][0]['stages']);
|
||||
$this->assertSame('Sofala / Dondo',$groups[0]['opportunities'][0]['territorial_summary']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import {opportunityFillGroups,opportunityErrorGroup,opportunityMissingGroups} from '../../resources/js/lib/opportunity-fill-flow.ts';
|
||||
import {territorialSearchParams} from '../../resources/js/lib/territorial-search-params.ts';
|
||||
import {documentFacetLabel} from '../../resources/js/lib/document-facet-label.ts';
|
||||
|
||||
test('adaptive groups keep every applicable schema field once, including new specialized fields',()=>{
|
||||
const keys=['official_title','locations','sector_id','documents','currency','real_estate','future_schema_field','currency'];
|
||||
@@ -26,3 +27,9 @@ test('territorial search replaces old descendants while retaining all selected n
|
||||
const all=territorialSearchParams(entries,[]);assert.equal(all.has('province_id'),false);assert.equal(all.get('category'),'zee');
|
||||
const unchanged=territorialSearchParams(entries);assert.equal(unchanged.get('district_id'),'old-child');
|
||||
});
|
||||
test('document facets have readable labels while original query identifiers remain unchanged',()=>{
|
||||
const original=['agronegocio','education','media','pt','turismo'];
|
||||
assert.deepEqual(original.map(documentFacetLabel),['Agronegócio','Formação e competências','Imagens e conteúdos de apoio','Português','Turismo']);
|
||||
assert.deepEqual(original,['agronegocio','education','media','pt','turismo']);
|
||||
assert.equal(documentFacetLabel('Tema definido pela Tempo'),'Tema definido pela Tempo');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user