fix: complete InvesteMoz validation and error corrections

This commit is contained in:
2026-10-29 21:47:33 +02:00
parent 63147312e2
commit daf8623441
718 changed files with 127308 additions and 20959 deletions
@@ -5,14 +5,17 @@ namespace App\Http\Controllers;
use App\Models\InvestmentDossier;
use App\Models\InvestmentZone;
use App\Models\Opportunity;
use App\Services\EconomicReferenceCatalog;
use App\Services\InvestmentCatalogue;
use App\Services\InvestmentGeography;
use App\Services\InvestmentTerritorialTraceability;
use App\Services\InvestmentZoneAccess;
use App\Services\InvestmentZones;
use App\Services\InvestmentZoneOperationalDetails;
use App\Services\InvestmentZoneOperationalDetailsInstallation;
use App\Services\InvestmentZoneOperationalProfile;
use App\Services\InvestmentZonePathContext;
use App\Services\InvestmentZoneProfileInstallation;
use App\Services\InvestmentZones;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
@@ -33,7 +36,7 @@ class InvestmentZoneController extends Controller
$zone->setAttribute('province', collect($territoryPath)->firstWhere('location_type', 'province')->name ?? $zone->province);
$zone->setAttribute('district', collect($territoryPath)->firstWhere('location_type', 'district')->name ?? $zone->district);
$zone->setAttribute('territory_path', $territoryPath);
$zone->setAttribute('operational_details_public',app(\App\Services\InvestmentZoneOperationalDetails::class)->publicData($zone));
$zone->setAttribute('operational_details_public', app(InvestmentZoneOperationalDetails::class)->publicData($zone));
$publicFields = ['operational_details_public', 'version_no', 'published_at', 'id', 'code', 'category', 'physical_category', 'legal_regime', 'operational_status', 'development_status', 'availability_status', 'area_value', 'area_unit', 'area_components', 'source_evidence', 'activities', 'resources', 'aliases', 'historical_territory', 'data_as_of', 'sources_consulted_at', 'name', 'name_en', 'description', 'description_en', 'province', 'district', 'territory_path', 'validation_status', 'updated_at'];
$locations = DB::table('investment_zone_locations as zl')->join('geo_locations as g', 'g.id', '=', 'zl.geo_location_id')
->where('zl.zone_id', $id)->when(! $management, fn ($q) => $q->where('zl.validation_status', 'validated')->where('g.data_classification', 'public'))
@@ -68,7 +71,7 @@ class InvestmentZoneController extends Controller
});
$props = ['record' => $management ? app(InvestmentZoneOperationalProfile::class)->management($zone) : app(InvestmentZoneOperationalProfile::class)->publicDetails($zone->only($publicFields)), 'source' => $source, 'operator' => $operator, 'management' => $management,
'profileAvailable' => $management && app(InvestmentZoneOperationalProfile::class)->installed(),
'zoneContext' => !$management && $zone->category === 'industrial_park' ? app(InvestmentZonePathContext::class)->context($id,(int)$zone->version_no) : null,
'zoneContext' => ! $management && $zone->category === 'industrial_park' ? app(InvestmentZonePathContext::class)->context($id, (int) $zone->version_no) : null,
'zoneExtensionsAvailable' => Schema::hasColumn('investment_zones', 'source_evidence'),
'related' => $related->unique('id')->values(),
'relations' => $management ? DB::table('investment_zone_relations')->where('zone_id', $id)->orderByDesc('created_at')->limit(100)->get() : [],
@@ -77,7 +80,7 @@ class InvestmentZoneController extends Controller
'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() : [],
'economicActivities' => $management ? app(\App\Services\EconomicReferenceCatalog::class)->activities() : [],
'economicActivities' => $management ? app(EconomicReferenceCatalog::class)->activities() : [],
'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() : [],
'locations' => $locations,
@@ -99,7 +102,9 @@ class InvestmentZoneController extends Controller
'Os filtros de desenvolvimento e disponibilidade estarão disponíveis após a migração do catálogo de zonas.');
$path = app(InvestmentGeography::class)->selection($filters);
$publicColumns = ['primary_territory_id', 'version_no', 'id', 'code', 'name', 'name_en', 'category', 'physical_category', 'legal_regime', 'operational_status', 'development_status', 'availability_status', 'area_value', 'area_unit', 'area_components', 'source_evidence', 'activities', 'resources', 'aliases', 'data_as_of', 'sources_consulted_at', 'province', 'district', 'description', 'description_en', 'validation_status', 'updated_at'];
if(app(\App\Services\InvestmentZoneOperationalDetails::class)->installed()){$publicColumns[]='operational_details';}
if (app(InvestmentZoneOperationalDetails::class)->installed()) {
$publicColumns[] = 'operational_details';
}
$publicColumns = array_values(array_filter($publicColumns, fn ($column) => Schema::hasColumn('investment_zones', $column)));
$rows = $zones->search($filters, $management)->orderBy('name')->orderBy('id')
->paginate(12, $management ? ['*'] : $publicColumns)->withQueryString();
@@ -113,11 +118,13 @@ class InvestmentZoneController extends Controller
->when(! $management, fn ($q) => $q->where('zl.validation_status', 'validated')->where('g.data_classification', 'public'))
->orderBy('g.location_type')->orderBy('g.name')->get(['g.id', 'g.name', 'g.location_type', 'zl.scope_type']));
if (! $management) { $zone->makeHidden('primary_territory_id'); }
if (! $management) {
$zone->makeHidden('primary_territory_id');
}
return $management ? $zone : [...app(InvestmentZoneOperationalProfile::class)->publicDetails($zone->toArray()),'operational_details_public'=>app(\App\Services\InvestmentZoneOperationalDetails::class)->publicData($zone)];
return $management ? $zone : [...app(InvestmentZoneOperationalProfile::class)->publicDetails($zone->toArray()), 'operational_details_public' => app(InvestmentZoneOperationalDetails::class)->publicData($zone)];
});
$props = ['detailsInstallation'=>$management ? app(\App\Services\InvestmentZoneOperationalDetailsInstallation::class)->preview($request->user()) : null, 'installation' => $management ? app(InvestmentZoneProfileInstallation::class)->preview($request->user()) : null, 'records' => $rows, 'filters' => $filters, 'management' => $management, 'territorySelection' => $path,
$props = ['detailsInstallation' => $management ? app(InvestmentZoneOperationalDetailsInstallation::class)->preview($request->user()) : null, 'installation' => $management ? app(InvestmentZoneProfileInstallation::class)->preview($request->user()) : null, 'records' => $rows, 'filters' => $filters, 'management' => $management, 'territorySelection' => $path,
'zoneExtensionsAvailable' => Schema::hasColumn('investment_zones', 'source_evidence')];
return $request->is('api/*') ? response()->json($props) : Inertia::render('investment/zones', $props);
@@ -193,35 +200,35 @@ class InvestmentZoneController extends Controller
'source_evidence.*.source_date' => ['nullable', 'date_format:Y-m-d'],
'area_value' => ['nullable', 'numeric', 'min:0'], 'area_unit' => ['nullable', 'string', 'max:24'],
'aliases' => ['sometimes', 'array', 'max:20'], 'aliases.*' => ['string', 'max:120'],
'operational_details' => ['sometimes','array'],
'operational_details' => ['sometimes', 'array'],
'activities' => ['sometimes', 'array', 'max:50'], 'activities.*' => ['array:id,name,status,source_reference,source_date,kind,scope,capacity_type,capacity_value,capacity_unit,focus,economic_activity_id,support_document_ids,private_note'],
'activities.*.id' => ['nullable','uuid'],
'activities.*.source_date' => ['nullable','date_format:Y-m-d'],
'activities.*.kind' => ['nullable','in:infrastructure,service,access,other'],
'activities.*.scope' => ['nullable','in:inside,regional,unknown'],
'activities.*.capacity_type' => ['nullable','string','max:120'],
'activities.*.capacity_value' => ['nullable','numeric','min:0'],
'activities.*.capacity_unit' => ['nullable','string','max:40'],
'activities.*.focus' => ['nullable','in:promotional,potential,installed,unknown'],
'activities.*.economic_activity_id' => ['nullable','uuid','exists:economic_activities,id'],
'activities.*.support_document_ids' => ['sometimes','array','max:10'],
'activities.*.id' => ['nullable', 'uuid'],
'activities.*.source_date' => ['nullable', 'date_format:Y-m-d'],
'activities.*.kind' => ['nullable', 'in:infrastructure,service,access,other'],
'activities.*.scope' => ['nullable', 'in:inside,regional,unknown'],
'activities.*.capacity_type' => ['nullable', 'string', 'max:120'],
'activities.*.capacity_value' => ['nullable', 'numeric', 'min:0'],
'activities.*.capacity_unit' => ['nullable', 'string', 'max:40'],
'activities.*.focus' => ['nullable', 'in:promotional,potential,installed,unknown'],
'activities.*.economic_activity_id' => ['nullable', 'uuid', 'exists:economic_activities,id'],
'activities.*.support_document_ids' => ['sometimes', 'array', 'max:10'],
'activities.*.support_document_ids.*' => ['uuid'],
'activities.*.private_note' => ['nullable','string','max:2000'],
'activities.*.private_note' => ['nullable', 'string', 'max:2000'],
'activities.*.name' => ['required', 'string', 'max:300'], 'activities.*.status' => ['required', 'in:proposed,authorized,conditional,excluded,potential'],
'activities.*.source_reference' => ['nullable', 'string', 'max:2000'],
'resources' => ['sometimes', 'array', 'max:50'], 'resources.*' => ['array:id,name,status,source_reference,source_date,kind,scope,capacity_type,capacity_value,capacity_unit,focus,economic_activity_id,support_document_ids,private_note'],
'resources.*.id' => ['nullable','uuid'],
'resources.*.source_date' => ['nullable','date_format:Y-m-d'],
'resources.*.kind' => ['nullable','in:infrastructure,service,access,other'],
'resources.*.scope' => ['nullable','in:inside,regional,unknown'],
'resources.*.capacity_type' => ['nullable','string','max:120'],
'resources.*.capacity_value' => ['nullable','numeric','min:0'],
'resources.*.capacity_unit' => ['nullable','string','max:40'],
'resources.*.focus' => ['nullable','in:promotional,potential,installed,unknown'],
'resources.*.economic_activity_id' => ['nullable','uuid','exists:economic_activities,id'],
'resources.*.support_document_ids' => ['sometimes','array','max:10'],
'resources.*.id' => ['nullable', 'uuid'],
'resources.*.source_date' => ['nullable', 'date_format:Y-m-d'],
'resources.*.kind' => ['nullable', 'in:infrastructure,service,access,other'],
'resources.*.scope' => ['nullable', 'in:inside,regional,unknown'],
'resources.*.capacity_type' => ['nullable', 'string', 'max:120'],
'resources.*.capacity_value' => ['nullable', 'numeric', 'min:0'],
'resources.*.capacity_unit' => ['nullable', 'string', 'max:40'],
'resources.*.focus' => ['nullable', 'in:promotional,potential,installed,unknown'],
'resources.*.economic_activity_id' => ['nullable', 'uuid', 'exists:economic_activities,id'],
'resources.*.support_document_ids' => ['sometimes', 'array', 'max:10'],
'resources.*.support_document_ids.*' => ['uuid'],
'resources.*.private_note' => ['nullable','string','max:2000'],
'resources.*.private_note' => ['nullable', 'string', 'max:2000'],
'resources.*.name' => ['required', 'string', 'max:120'], 'resources.*.status' => ['required', 'in:existing,planned,unconfirmed'],
'resources.*.source_reference' => ['nullable', 'string', 'max:2000'], 'data_as_of' => ['nullable', 'date_format:Y-m-d'],
'sources_consulted_at' => ['nullable', 'date_format:Y-m-d'], 'parent_zone_id' => ['nullable', 'uuid', 'exists:investment_zones,id'],
@@ -257,17 +264,26 @@ class InvestmentZoneController extends Controller
$data['district'] = $path[1]->name ?? null;
}
$saved=$zones->save($id, $data, $request->user()->id);
$saved = $zones->save($id, $data, $request->user()->id);
return response()->json(app(InvestmentZoneOperationalProfile::class)->management($saved), $id ? 200 : 201);
}
public function transition(Request $request, InvestmentZones $zones, string $id, string $action): JsonResponse
{
$data = $request->validate(['version_no' => ['required', 'integer', 'min:1'], 'base_public_version'=>['required_if:action,reconcile','nullable','integer','min:1']]);
if($action==='reconcile'){
$request->validate(['base_public_version'=>'required|integer|min:1']);
$saved=DB::transaction(function()use($id,$data,$request){$zone=InvestmentZone::whereKey($id)->lockForUpdate()->firstOrFail();abort_unless($zone->category==='industrial_park',422);return app(InvestmentZoneOperationalProfile::class)->reconcile($zone,(int)$data['version_no'],(int)$data['base_public_version'],$request->user()->id);});
}else{$saved=$action === 'publish' ? $zones->publish($id,$data['version_no'],$request->user()->id) : $zones->suspend($id,$data['version_no'],$request->user()->id);}
$data = $request->validate(['version_no' => ['required', 'integer', 'min:1'], 'base_public_version' => ['required_if:action,reconcile', 'nullable', 'integer', 'min:1']]);
if ($action === 'reconcile') {
$request->validate(['base_public_version' => 'required|integer|min:1']);
$saved = DB::transaction(function () use ($id, $data, $request) {
$zone = InvestmentZone::whereKey($id)->lockForUpdate()->firstOrFail();
abort_unless($zone->category === 'industrial_park', 422);
return app(InvestmentZoneOperationalProfile::class)->reconcile($zone, (int) $data['version_no'], (int) $data['base_public_version'], $request->user()->id);
});
} else {
$saved = $action === 'publish' ? $zones->publish($id, $data['version_no'], $request->user()->id) : $zones->suspend($id, $data['version_no'], $request->user()->id);
}
return response()->json(app(InvestmentZoneOperationalProfile::class)->management($saved));
}