Files
Investidor-V2/app/Services/BusinessPointPublication.php
T

126 lines
7.0 KiB
PHP

<?php
namespace App\Services;
use App\Models\ExistingBusiness;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
/** Optional supplement of the approved listing; never grants publication authority. */
final class BusinessPointPublication
{
public function available(): bool
{
return Schema::hasColumn('existing_business_locations', 'public_source_reference');
}
/** @param array<string,mixed> $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<string,mixed> $payload
* @return list<array<string,mixed>> */
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<string,mixed> $path */
public function pathKey(array $path): string
{
return implode('|', array_map(fn ($field) => (string) ($path[$field] ?? ''), array_keys(InvestmentGeography::FIELDS)));
}
/** @param array<string,mixed> $payload
* @return array<string,mixed>|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<string,mixed> */
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<string,mixed>|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;
}
}