varias alteracoes
tests / ci (push) Canceled after 0s

This commit is contained in:
2026-10-06 14:50:12 +02:00
parent 4f35313d06
commit e65cb26901
333 changed files with 54656 additions and 2058 deletions
@@ -4,9 +4,13 @@ namespace App\Http\Controllers;
use App\Models\InvestmentDossier;
use App\Models\Opportunity;
use App\Models\Project;
use App\Models\InvestmentSubmission;
use App\Services\InvestmentZones;
use App\Services\InvestmentGeography;
use App\Services\PortalAudit;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Query\Builder as QueryBuilder;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
@@ -19,7 +23,7 @@ class InvestmentPortfolioController extends Controller
{
foreach ([['corridor_projects', 'corridor_id', 'project_id'], ['corridor_clusters', 'corridor_id', 'cluster_id'], ['cluster_projects', 'cluster_id', 'project_id']] as [$table, $left, $right]) {
foreach ([[$first, $second], [$second, $first]] as [$a, $b]) {
if ($a->$left && $b->$right && DB::table($table)->where($left, $a->$left)->where($right, $b->$right)->whereNull('deleted_at')->exists()) {
if ($a->$left && $b->$right && self::currentRelation(DB::table($table)->where($left, $a->$left)->where($right, $b->$right))->exists()) {
return true;
}
}
@@ -28,6 +32,13 @@ class InvestmentPortfolioController extends Controller
return false;
}
private static function currentRelation(QueryBuilder $query): QueryBuilder
{
return $query->whereNull('deleted_at')->where('validation_status', 'validated')
->whereDate('valid_from', '<=', today()->toDateString())
->where(fn ($q) => $q->whereNull('valid_until')->orWhereDate('valid_until', '>=', today()->toDateString()));
}
/** @return Builder<InvestmentDossier> */
public static function visible(): Builder
{
@@ -42,7 +53,8 @@ class InvestmentPortfolioController extends Controller
public function index(Request $request): Response
{
$management = $request->routeIs('management.portfolio*');
$filters = $request->validate(['q' => ['nullable', 'string', 'max:120'], 'kind' => ['nullable', 'in:corridor,cluster,project']]);
$filters = $request->validate(['q' => ['nullable', 'string', 'max:120'], 'kind' => ['nullable', 'in:corridor,cluster,project'],
'province_id' => ['nullable', 'uuid'], 'district_id' => ['nullable', 'uuid'], 'admin_post_id' => ['nullable', 'uuid'], 'locality_id' => ['nullable', 'uuid']]);
$query = $management ? InvestmentDossier::query() : self::visible();
if (! empty($filters['kind'])) {
$query->whereNotNull($filters['kind'].'_id');
@@ -50,9 +62,14 @@ class InvestmentPortfolioController extends Controller
if (! empty($filters['q'])) {
$query->where('title', 'ilike', '%'.$filters['q'].'%');
}
$territorySelection = app(InvestmentGeography::class)->selection($filters);
if ($territorySelection !== []) {
$territoryIds = app(InvestmentGeography::class)->descendants($territorySelection[array_key_last($territorySelection)]->id);
app(InvestmentGeography::class)->constrainDossiersToTerritories($query, $territoryIds);
}
return Inertia::render('portfolio/index', ['records' => $query->orderBy('title')->paginate(30)->withQueryString(),
'management' => $management, 'filters' => $filters]);
'management' => $management, 'filters' => $filters, 'territorySelection' => $territorySelection]);
}
public function show(Request $request, string $id): Response
@@ -63,8 +80,8 @@ class InvestmentPortfolioController extends Controller
foreach ([['corridor_projects', 'corridor_id', 'project_id'], ['corridor_clusters', 'corridor_id', 'cluster_id'], ['cluster_projects', 'cluster_id', 'project_id']] as [$table, $left, $right]) {
foreach ([[$left, $right], [$right, $left]] as [$origin, $destination]) {
if ($record->$origin) {
$relatedIds[$destination] = array_merge($relatedIds[$destination] ?? [], DB::table($table)
->where($origin, $record->$origin)->whereNull('deleted_at')->pluck($destination)->all());
$relatedIds[$destination] = array_merge($relatedIds[$destination] ?? [], self::currentRelation(DB::table($table)
->where($origin, $record->$origin))->pluck($destination)->all());
}
}
}
@@ -98,7 +115,16 @@ class InvestmentPortfolioController extends Controller
}
}
$anchorProject = $record->project_id
? Project::whereKey($record->project_id)->whereNull('deleted_at')->where('status', '!=', 'draft')->first(['id', 'code', 'name', 'status', 'budget_amount', 'budget_currency', 'start_date', 'end_date'])
: null;
$existingSubmissions = ! $management && $request->user()
? InvestmentSubmission::where('user_id', $request->user()->id)->where('dossier_id', $record->id)->where('status', 'draft')
->orderByDesc('updated_at')->limit(10)->get(['id', 'version', 'updated_at'])
: collect();
return Inertia::render('portfolio/show', ['record' => $record, 'related' => $related, 'management' => $management, 'zones' => $zones,
'anchorProject' => $anchorProject, 'existingSubmissions' => $existingSubmissions,
'origin' => $origin ? ['id' => $origin->id, 'title' => $origin->title] : null, 'opportunities' => $opportunities, 'chains' => $chains]);
}
@@ -114,7 +140,7 @@ class InvestmentPortfolioController extends Controller
'publication_status' => 'draft', 'published_at' => null, 'approval_evidence' => null, 'approval_basis' => 'manager'], $request->user()->id);
});
return back()->with('success', 'Rascunho guardado. Pode aprovar e publicar a ficha.');
return back()->with('success', 'Rascunho guardado. A publicacao aguarda um revisor diferente do editor.');
}
public function publish(Request $request, InvestmentDossier $dossier): RedirectResponse
@@ -123,6 +149,8 @@ class InvestmentPortfolioController extends Controller
DB::transaction(function () use ($request, $dossier, $data) {
$dossier = InvestmentDossier::whereKey($dossier->id)->lockForUpdate()->firstOrFail();
abort_if($dossier->version_no !== (int) $data['version_no'], 409, 'A ficha foi alterada. Recarregue a página e reveja a versão actual antes de publicar.');
abort_unless($dossier->editor_id !== null, 422, 'A ficha deve ser assumida e revista antes da publicacao.');
abort_if((int) $dossier->editor_id === (int) $request->user()->id, 403, 'A publicacao exige um revisor diferente do editor.');
PortalAudit::save($dossier, [...$data, 'version_no' => $dossier->version_no + 1, 'approval_basis' => 'manager', 'reviewer_id' => $request->user()->id, 'publication_status' => 'published', 'published_at' => now()], $request->user()->id);
});