Files
Investidor-V2/app/Http/Controllers/InvestmentZoneController.php
T
2026-09-29 08:41:57 +02:00

109 lines
7.5 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Models\InvestmentDossier;
use App\Models\InvestmentZone;
use App\Models\Opportunity;
use App\Services\InvestmentCatalogue;
use App\Services\InvestmentGeography;
use App\Services\InvestmentZoneAccess;
use App\Services\InvestmentZones;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
use Inertia\Inertia;
use Inertia\Response;
class InvestmentZoneController extends Controller
{
public function show(Request $request, InvestmentZones $zones, string $id): Response|JsonResponse
{
$management = $request->is('management/*', 'api/v2/investment/admin/*');
$zone = ($management ? InvestmentZone::query() : $zones->visible())->findOrFail($id);
$publicFields = ['id', 'code', 'category', 'name', 'name_en', 'description', 'description_en', 'province', 'district', 'validation_status', 'updated_at'];
$source = DB::table('investment_legal_sources')->where('id', $zone->legal_source_id)->first(['title', 'url', 'diploma', 'article', 'competent_entity', 'valid_from', 'valid_until', 'version_no']);
$operator = DB::table('organizations')->where('id', $zone->operator_id)->whereNull('deleted_at')
->when(! $management, fn ($q) => $q->where('data_classification', 'public'))->value('name');
$field = ['zee' => 'zee_id', 'zfi' => 'zfi_id', 'industrial_park' => 'park_id'][$zone->category] ?? null;
$opportunities = $field && ! $management ? app(InvestmentCatalogue::class)->query([$field => $id])->paginate(12)->withQueryString() : null;
$related = collect();
foreach (['cluster_id', 'corridor_id', 'project_id'] as $foreign) {
$relations = $zones->links(true)->where('zl.zone_id', $id)->whereNotNull('zl.'.$foreign);
$related = $related->merge(InvestmentPortfolioController::visible()->whereIn($foreign, $relations->select('zl.'.$foreign))->get());
}
$props = ['record' => $management ? $zone : $zone->only($publicFields), 'source' => $source, 'operator' => $operator, 'management' => $management,
'related' => $related->unique('id')->values(),
'relations' => $management ? DB::table('investment_zone_relations')->where('zone_id', $id)->orderByDesc('created_at')->limit(100)->get() : [],
'geometries' => $management ? DB::table('investment_zone_geometries')->where('zone_id', $id)->orderByDesc('created_at')->limit(100)->get() : [],
'targets' => $management ? ['opportunities' => Opportunity::orderBy('title')->limit(200)->get(['id', 'title']),
'dossiers' => InvestmentDossier::orderBy('title')->limit(200)->get(['id', 'title', 'project_id', 'cluster_id', 'corridor_id'])] : null,
'opportunities' => $opportunities,
'legalSources' => $management ? DB::table('investment_legal_sources')->orderByDesc('created_at')->limit(100)->get() : [],
'operators' => $management ? DB::table('organizations')->whereNull('deleted_at')->orderBy('name')->limit(200)->get(['id', 'name']) : [],
'links' => $management ? DB::table('investment_zone_links')->where('zone_id', $id)->orderByDesc('created_at')->limit(100)->get() : [],
'revisions' => $management ? DB::table('investment_zone_revisions')->where('zone_id', $id)->orderByDesc('version_no')->limit(20)->get(['version_no', 'action', 'sha256', 'created_at']) : []];
return $request->is('api/*') ? response()->json($props) : Inertia::render('investment/zone', $props);
}
public function index(Request $request, InvestmentZones $zones): Response|JsonResponse
{
$filters = $request->validate(['category' => ['nullable', 'in:zee,zfi,industrial_park'], 'q' => ['nullable', 'string', 'max:120'], 'page' => ['nullable', 'integer', 'min:1'],
'province_id' => ['nullable', 'uuid'], 'district_id' => ['nullable', 'uuid'], 'admin_post_id' => ['nullable', 'uuid'], 'locality_id' => ['nullable', 'uuid']]);
$management = $request->is('management/*', 'api/v2/investment/admin/*');
$query = $management ? InvestmentZone::query() : $zones->visible();
$path = app(InvestmentGeography::class)->selection($filters);
if ($path !== []) {
$query->whereIn('primary_territory_id', app(InvestmentGeography::class)->descendants($path[array_key_last($path)]->id));
}
$rows = $query->when($filters['category'] ?? null, fn ($q, $v) => $q->where('category', $v))
->when($filters['q'] ?? null, fn ($q, $v) => $q->where(fn ($q) => $q->where('name', 'ilike', '%'.$v.'%')->orWhere('code', 'ilike', '%'.$v.'%')))
->orderBy('name')->orderBy('id')->paginate(12, $management ? ['*'] : ['id', 'code', 'name', 'name_en', 'category', 'province', 'district', 'description', 'description_en', 'validation_status', 'updated_at'])->withQueryString();
$props = ['records' => $rows, 'filters' => $filters, 'management' => $management, 'territorySelection' => $path];
return $request->is('api/*') ? response()->json($props) : Inertia::render('investment/zones', $props);
}
public function save(Request $request, InvestmentZones $zones, ?string $id = null): JsonResponse
{
$data = $request->validate(['version_no' => [$id ? 'required' : 'nullable', 'integer', 'min:1'],
'code' => ['required', 'string', 'max:80', Rule::unique('investment_zones')->ignore($id)],
'category' => ['required', 'in:zee,zfi,industrial_park,pending'], 'name' => ['required', 'string', 'max:255'],
'name_en' => ['nullable', 'string', 'max:255'], 'description' => ['nullable', 'string', 'max:15000'],
'description_en' => ['nullable', 'string', 'max:15000'], 'province' => ['nullable', 'string', 'max:100'], 'district' => ['nullable', 'string', 'max:100'],
'operator_id' => ['nullable', 'uuid', Rule::exists('organizations', 'id')->whereNull('deleted_at')],
'legal_source_id' => ['nullable', 'uuid', 'exists:investment_legal_sources,id']]);
if (! $id && $request->has('province_id')) {
$geo = $request->validate(['province_id' => ['nullable', 'uuid'], 'district_id' => ['nullable', 'uuid'], 'admin_post_id' => ['nullable', 'uuid'], 'locality_id' => ['nullable', 'uuid']]);
$path = app(InvestmentGeography::class)->selection($geo);
$data['primary_territory_id'] = $path === [] ? null : $path[array_key_last($path)]->id;
$data['territory_version'] = $path === [] ? 0 : 1;
$data['province'] = $path[0]->name ?? null;
$data['district'] = $path[1]->name ?? null;
}
return response()->json($zones->save($id, $data, $request->user()->id), $id ? 200 : 201);
}
public function transition(Request $request, InvestmentZones $zones, string $id, string $action): JsonResponse
{
if ($action === 'publish') {
InvestmentZoneAccess::requireRole($request->user(), 'publishers');
}
$data = $request->validate(['version_no' => ['required', 'integer', 'min:1']]);
return response()->json($action === 'publish' ? $zones->publish($id, $data['version_no'], $request->user()->id)
: $zones->suspend($id, $data['version_no'], $request->user()->id));
}
public function revisions(string $id): JsonResponse
{
InvestmentZone::findOrFail($id);
return response()->json(DB::table('investment_zone_revisions')->where('zone_id', $id)->orderByDesc('version_no')->paginate(20, ['id', 'version_no', 'actor_id', 'action', 'sha256', 'created_at']));
}
}