Files
Investidor-V2/app/Http/Requests/StoreLandAssistanceCaseRequest.php
T
nala c13f250b87
tests / ci (push) Canceled after 0s
versao compilada
2026-10-15 17:37:00 +02:00

199 lines
14 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Http\Requests;
use App\Services\CountryCatalogue;
use App\Services\InvestmentGeography;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Validator;
class StoreLandAssistanceCaseRequest extends FormRequest
{
/** @return array<string, mixed> */
public function payload(): array
{
$payload = $this->validated('payload');
return is_array($payload) ? $payload : [];
}
public function authorize(): bool
{
return true;
}
/** @return array<string,list<mixed>> */
public function rules(): array
{
return [
'version' => [$this->isMethod('post') && ! $this->routeIs('land-assistance.support') ? 'nullable' : 'required', 'integer', 'min:1'],
'payload' => ['required', 'array:support_kind,request_description,project_reference,investor_path_case_id,applicant_category,full_name,contact_person,email,phone,preferred_contact,country,declared_origin_country_code,nationality,representative_name,representative_role,representation_basis,nuit,constitution_or_residency_note,area_status,parcels,purpose,activity,known_occupants,occupant_description,known_improvements,improvements_description,prior_right_status,prior_right_description,conflict_status,conflict_description,official_process,attachments_to_follow,exploration_plan,consultation,additional_information,truth_declaration,contact_consent'],
'payload.support_kind' => ['nullable', 'in:investment,home,family_exploitation,recognition,regularization,delimitation,change_of_use,transfer,existing_process,conflict_guidance,other'],
'payload.request_description' => ['nullable', 'string', 'max:4000'],
'payload.project_reference' => ['nullable', 'string', 'max:160'],
'payload.investor_path_case_id' => ['nullable', 'uuid'],
'payload.applicant_category' => ['nullable', 'in:individual,entity,community,other'],
'payload.full_name' => ['nullable', 'string', 'max:150'],
'payload.contact_person' => ['nullable', 'string', 'max:200'],
'payload.email' => ['nullable', 'email:rfc', 'max:150'],
'payload.phone' => ['nullable', 'string', 'max:30'],
'payload.preferred_contact' => ['nullable', 'in:email,phone,any'],
'payload.country' => ['nullable', 'string', 'max:100', app(CountryCatalogue::class)->legacyCountryRule()],
'payload.declared_origin_country_code' => ['nullable', 'string', app(CountryCatalogue::class)->rule()],
'payload.nationality' => ['nullable', 'string', 'max:120'],
'payload.representative_name' => ['nullable', 'string', 'max:200'],
'payload.representative_role' => ['nullable', 'string', 'max:120'],
'payload.representation_basis' => ['nullable', 'string', 'max:500'],
'payload.nuit' => ['nullable', 'string', 'max:40'],
'payload.constitution_or_residency_note' => ['nullable', 'string', 'max:1000'],
'payload.area_status' => ['nullable', 'in:identified,seeking,location_support,unknown'],
'payload.parcels' => ['nullable', 'array', 'max:10'],
'payload.parcels.*' => ['array:province_id,district_id,admin_post_id,locality_id,place_description,area,area_unit,cadastral_reference,coordinates,geometry_confidence'],
'payload.parcels.*.province_id' => ['nullable', 'uuid', 'exists:geo_locations,id'],
'payload.parcels.*.district_id' => ['nullable', 'uuid', 'exists:geo_locations,id'],
'payload.parcels.*.admin_post_id' => ['nullable', 'uuid', 'exists:geo_locations,id'],
'payload.parcels.*.locality_id' => ['nullable', 'uuid', 'exists:geo_locations,id'],
'payload.parcels.*.place_description' => ['nullable', 'string', 'max:1000'],
'payload.parcels.*.area' => ['nullable', 'numeric', 'min:0', 'max:999999999999'],
'payload.parcels.*.area_unit' => ['nullable', 'in:m2,hectares,other,unknown'],
'payload.parcels.*.cadastral_reference' => ['nullable', 'string', 'max:160'],
'payload.parcels.*.coordinates' => ['nullable', 'string', 'max:500'],
'payload.parcels.*.geometry_confidence' => ['nullable', 'in:declared,approximate,confirmed_by_authority,unknown'],
'payload.purpose' => ['nullable', 'string', 'max:1000'],
'payload.activity' => ['nullable', 'string', 'max:500'],
'payload.known_occupants' => ['nullable', 'in:yes,no_as_far_as_known,unknown'],
'payload.occupant_description' => ['nullable', 'string', 'max:2000'],
'payload.known_improvements' => ['nullable', 'in:yes,unknown,none_identified'],
'payload.improvements_description' => ['nullable', 'string', 'max:2000'],
'payload.prior_right_status' => ['nullable', 'in:yes,unknown,no_documents'],
'payload.prior_right_description' => ['nullable', 'string', 'max:2000'],
'payload.conflict_status' => ['nullable', 'in:yes,unknown,no_knowledge'],
'payload.conflict_description' => ['nullable', 'string', 'max:2000'],
'payload.official_process' => ['nullable', 'array:official_reference,receiving_entity,presented_at,reported_status,pending_items,proof_available'],
'payload.official_process.official_reference' => ['nullable', 'string', 'max:160'],
'payload.official_process.receiving_entity' => ['nullable', 'string', 'max:200'],
'payload.official_process.presented_at' => ['nullable', 'date_format:Y-m-d'],
'payload.official_process.reported_status' => ['nullable', 'string', 'max:1000'],
'payload.official_process.pending_items' => ['nullable', 'string', 'max:2000'],
'payload.official_process.proof_available' => ['nullable', 'in:yes,no,unknown'],
'payload.attachments_to_follow' => ['nullable', 'array', 'max:10'],
'payload.attachments_to_follow.*' => ['string', 'in:sketch,prior_right,exploration_plan,official_receipt,consultation_minutes,photo,other'],
'payload.exploration_plan' => ['nullable', 'array:activity_rows,notes'],
'payload.exploration_plan.activity_rows' => ['nullable', 'array', 'max:30'],
'payload.exploration_plan.activity_rows.*' => ['array:period,activity,area,area_unit,stage,targets,structures,investment'],
'payload.exploration_plan.activity_rows.*.period' => ['nullable', 'string', 'max:80'],
'payload.exploration_plan.activity_rows.*.activity' => ['nullable', 'string', 'max:300'],
'payload.exploration_plan.activity_rows.*.area' => ['nullable', 'numeric', 'min:0'],
'payload.exploration_plan.activity_rows.*.area_unit' => ['nullable', 'in:m2,hectares,other,unknown'],
'payload.exploration_plan.activity_rows.*.stage' => ['nullable', 'in:existing,planned,unknown'],
'payload.exploration_plan.activity_rows.*.targets' => ['nullable', 'string', 'max:1000'],
'payload.exploration_plan.activity_rows.*.structures' => ['nullable', 'string', 'max:1000'],
'payload.exploration_plan.activity_rows.*.investment' => ['nullable', 'numeric', 'min:0'],
'payload.exploration_plan.notes' => ['nullable', 'string', 'max:2000'],
'payload.consultation' => ['nullable', 'array:status,community,location,date,facilitators,language,participants,interventions,opposition,agreements,minutes_reference,opinions_received'],
'payload.consultation.status' => ['nullable', 'in:not_started,preparing,reported_completed,unknown'],
'payload.consultation.community' => ['nullable', 'string', 'max:200'],
'payload.consultation.location' => ['nullable', 'string', 'max:500'],
'payload.consultation.date' => ['nullable', 'date_format:Y-m-d'],
'payload.consultation.facilitators' => ['nullable', 'string', 'max:1000'],
'payload.consultation.language' => ['nullable', 'string', 'max:100'],
'payload.consultation.participants' => ['nullable', 'array', 'max:200'],
'payload.consultation.participants.*' => ['array:name,role'],
'payload.consultation.participants.*.name' => ['nullable', 'string', 'max:200'],
'payload.consultation.participants.*.role' => ['nullable', 'string', 'max:120'],
'payload.consultation.interventions' => ['nullable', 'string', 'max:4000'],
'payload.consultation.opposition' => ['nullable', 'string', 'max:2000'],
'payload.consultation.agreements' => ['nullable', 'string', 'max:2000'],
'payload.consultation.minutes_reference' => ['nullable', 'string', 'max:500'],
'payload.consultation.opinions_received' => ['nullable', 'string', 'max:2000'],
'payload.additional_information' => ['nullable', 'string', 'max:4000'],
'payload.truth_declaration' => ['nullable', 'boolean'],
'payload.contact_consent' => ['nullable', 'boolean'],
];
}
public function withValidator(Validator $validator): void
{
$validator->after(function (Validator $validator): void {
$payload = $this->input('payload', []);
$context = is_array($payload) ? ($payload['investor_path_case_id'] ?? null) : null;
$previous = $this->route('id') ? \App\Models\LandAssistanceCase::whereKey($this->route('id'))->where('user_id', $this->user()->id)->first() : null;
$bound = $previous?->payload['investor_path_case_id'] ?? null;
if ($context !== null) {
$case = is_string($context) && \Illuminate\Support\Str::isUuid($context)
? \Illuminate\Support\Facades\DB::table('desk_cases')->where('id', $context)->first() : null;
if ((! config('investor-path.enabled') && $bound !== $context) || ! $case || (int) $case->user_id !== $this->user()->id
|| ($case->closed_at && $bound !== $context) || ! $case->journey_id
|| ! \Illuminate\Support\Facades\DB::table('investor_journeys')->where('id', $case->journey_id)->whereNotNull('path_definition_id')->whereNull('deleted_at')->exists()) {
$validator->errors()->add('payload.investor_path_case_id', 'Use um dossier do seu percurso disponível para preparação. O acesso direto a Terra e DUAT continua disponível.');
}
}
if ($this->route('id')) {
if ($bound !== null && $context !== $bound) {
$validator->errors()->add('payload.investor_path_case_id', 'Preserve o vínculo deste pedido ao dossier original.');
}
}
if (is_array($payload) && is_array($payload['parcels'] ?? null)) {
$geography = app(InvestmentGeography::class);
$levels = ['province_id' => 'province', 'district_id' => 'district', 'admin_post_id' => 'admin_post', 'locality_id' => 'locality'];
foreach ($payload['parcels'] as $index => $parcel) {
if (! is_array($parcel)) {
continue;
}
$parent = null;
$gap = false;
foreach ($levels as $field => $expectedType) {
$id = $parcel[$field] ?? null;
if (! is_string($id) || $id === '') {
$gap = true;
continue;
}
$record = $geography->publicReferences()->where('id', $id)->first(['id', 'parent_id', 'location_type']);
// The populated workbook hierarchy has country -> province. The public
// selector deliberately starts at province; it is not a root in storage.
if ($gap || ! $record || $record->location_type !== $expectedType || ($expectedType !== 'province' && (string) $record->parent_id !== $parent)) {
$validator->errors()->add("payload.parcels.$index.$field", 'A hierarquia territorial declarada é incoerente ou não está publicada.');
$gap = true;
}
$parent = (string) $id;
}
}
}
if (! $this->routeIs('land-assistance.support')) {
return;
}
if (! is_array($payload)) {
return;
}
foreach (['support_kind', 'request_description', 'applicant_category', 'full_name', 'preferred_contact', 'area_status', 'purpose'] as $required) {
if (! isset($payload[$required]) || trim((string) $payload[$required]) === '') {
$validator->errors()->add('payload.'.$required, 'Este campo é necessário para solicitar apoio.');
}
}
if (empty($payload['email']) && empty($payload['phone'])) {
$validator->errors()->add('payload.email', 'Indique pelo menos um contacto utilizável.');
}
if (! empty($payload['email']) && ! filter_var($payload['email'], FILTER_VALIDATE_EMAIL)) {
$validator->errors()->add('payload.email', 'Indique um email válido para registar o pedido no balcão de apoio.');
}
if (($payload['applicant_category'] ?? null) === 'community' && empty($payload['contact_person'])) {
$validator->errors()->add('payload.contact_person', 'Indique um contacto para a comunidade, se conhecido.');
}
if (($payload['applicant_category'] ?? null) === 'entity' && empty($payload['representative_role'])) {
$validator->errors()->add('payload.representative_role', 'Indique a qualidade da representação conhecida.');
}
if (empty($payload['contact_consent'])) {
$validator->errors()->add('payload.contact_consent', 'A autorização de contacto é necessária para solicitar apoio.');
}
if (empty($payload['truth_declaration'])) {
$validator->errors()->add('payload.truth_declaration', 'Confirme que as respostas refletem a informação de que dispõe.');
}
});
}
}