$payload */ public function applicable(array $payload): bool { return ($payload['location_point_enabled'] ?? null) === 'yes' && in_array($payload['geography_scope'] ?? null, ['specific', 'multiple'], true) && ($payload['disclosure_precision'] ?? null) === 'exact' && ($payload['location_unknown'] ?? null) === 'no'; } /** @param array $payload * @return list> */ public function paths(array $payload): array { if (($payload['geography_scope'] ?? null) === 'specific') { return [array_intersect_key($payload, InvestmentGeography::FIELDS)]; } if (($payload['geography_scope'] ?? null) !== 'multiple') { return []; } $rows = json_decode((string) ($payload['locations_json'] ?? ''), true); return is_array($rows) && array_is_list($rows) ? array_values(array_filter($rows, 'is_array')) : []; } /** @param array $path */ public function pathKey(array $path): string { return implode('|', array_map(fn ($field) => (string) ($path[$field] ?? ''), array_keys(InvestmentGeography::FIELDS))); } /** @param array $payload * @return array|null */ public function candidate(array $payload): ?array { if (! $this->applicable($payload)) { return null; } abort_unless($this->available(), 409, 'O suplemento de localização ainda não está instalado neste destino. O ponto não será publicado.'); $rules = [ 'location_unknown' => ['required', 'in:no'], 'location_latitude' => ['required', 'numeric', 'between:-90,90'], 'location_longitude' => ['required', 'numeric', 'between:-180,180'], 'location_point_source_mode' => ['required', 'in:listing,custom'], 'location_point_share_source' => ['nullable', 'in:yes,no'], 'location_point_source_reference' => ['required_if:location_point_source_mode,custom', 'nullable', 'string', 'max:2000'], 'location_point_source_date' => ($payload['location_point_source_mode'] ?? null) === 'custom' ? ['nullable', 'date_format:Y-m-d'] : ['nullable'], 'location_point_territory' => ['required_if:geography_scope,multiple', 'nullable', 'string', 'max:150'], ]; Validator::make($payload, $rules)->validate(); $paths = $this->paths($payload); $path = ($payload['geography_scope'] ?? null) === 'specific' ? ($paths[0] ?? null) : collect($paths)->first(fn ($row) => $this->pathKey($row) === $payload['location_point_territory']); if (! $path || empty($path['province_id'])) { throw ValidationException::withMessages(['location_point_territory' => 'Seleccione uma das localizações já declaradas para o ponto primário. Não é atribuída uma localização por ordem ou por nome.']); } $nodes = app(InvestmentGeography::class)->selection($path); $listingSource = $payload['location_point_source_mode'] === 'listing'; $source = $listingSource ? ($payload['source_reference'] ?? null) : ($payload['location_point_source_reference'] ?? null); $date = $listingSource ? ($payload['source_date'] ?? null) : ($payload['location_point_source_date'] ?? null); Validator::make(['location_point_source_reference' => $source, 'location_point_source_date' => $date], [ 'location_point_source_reference' => ['required', 'string', 'max:2000'], 'location_point_source_date' => ['nullable', 'date_format:Y-m-d'], ])->validate(); return ['path' => array_combine(array_keys(InvestmentGeography::FIELDS), array_map(fn ($field) => ($path[$field] ?? null) ?: null, array_keys(InvestmentGeography::FIELDS))), 'reference_snapshot' => array_map(fn ($node) => (array) $node, $nodes), 'latitude' => (float) $payload['location_latitude'], 'longitude' => (float) $payload['location_longitude'], 'source_reference' => $source, 'source_date' => $date ?: null, 'public_source_reference' => ($payload['location_point_share_source'] ?? null) === 'yes' ? $source : null]; } /** Called only within the existing authorized, locked publication transaction. * @return array */ public function publish(ExistingBusiness $business): array { $point = $this->candidate($business->payload); if ($point === null) { return []; } $previous = DB::table('existing_business_locations')->where('existing_business_id', $business->id)->where('is_primary', true) ->select('*')->selectRaw('ST_AsEWKT(geom) AS geometry_snapshot')->first(); $geometry = DB::selectOne('SELECT ST_AsEWKT(ST_SetSRID(ST_MakePoint(?::float8, ?::float8),4326)) AS value', [$point['longitude'], $point['latitude']])->value; $values = [...$point['path'], 'precision_level' => 'exact', 'is_public' => true, 'geom' => $geometry, 'public_source_reference' => $point['public_source_reference'], 'source_date' => $point['source_date'], 'source_system' => 'portal-business-form', 'source_payload_version' => $business->version_no + 1, 'updated_at' => now()]; if ($previous) { DB::table('existing_business_locations')->where('id', $previous->id)->update($values); } else { DB::table('existing_business_locations')->insert([...$values, 'id' => (string) Str::uuid(), 'existing_business_id' => $business->id, 'is_primary' => true, 'created_at' => now()]); } return ['point_publication' => ['previous' => $previous ? (array) $previous : null, 'used' => $point, 'business_version' => $business->version_no + 1, 'source' => 'explicit_listing_point_supplement']]; } /** Private view, only after the listing's normal view authorization. * @return array|null */ public function registered(ExistingBusiness $business): ?array { $row = DB::table('existing_business_locations')->where('existing_business_id', $business->id)->where('is_primary', true) ->select('id', 'precision_level', 'is_public', 'province_id', 'district_id', 'admin_post_id', 'locality_id') ->selectRaw("geom IS NOT NULL AS has_geometry, CASE WHEN GeometryType(geom)='POINT' THEN ST_Y(geom) END AS latitude, CASE WHEN GeometryType(geom)='POINT' THEN ST_X(geom) END AS longitude")->first(); return $row ? (array) $row : null; } }