101 lines
5.5 KiB
PHP
101 lines
5.5 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\Tenant;
|
|
use App\Models\User;
|
|
use App\Services\AdministrativeGeography;
|
|
use App\Services\ConflictGuard;
|
|
use App\Services\OrganizationAccess;
|
|
use App\Services\OrganizationConfiguration;
|
|
use App\Services\RequisitionWorkflow;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\RedirectResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Validation\Rule;
|
|
use Illuminate\Validation\ValidationException;
|
|
use Inertia\Inertia;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
use Symfony\Component\HttpFoundation\StreamedResponse;
|
|
|
|
class AdministrativeGeographyController extends Controller
|
|
{
|
|
/** @return array<string,mixed> */
|
|
private function reviewFilters(Request $request, AdministrativeGeography $geography): array
|
|
{
|
|
$actor = $request->user('web');
|
|
abort_unless($actor instanceof User, 403);
|
|
app(OrganizationConfiguration::class)->administrator($actor);
|
|
abort_unless($geography->enabled(), 503);
|
|
$filters = $request->validate(['search' => 'nullable|string|max:120', 'issue' => ['nullable', Rule::in(array_keys(AdministrativeGeography::ISSUES))], 'page' => 'sometimes|integer|min:1|max:10000']);
|
|
|
|
return ['search' => trim($filters['search'] ?? ''), 'issue' => $filters['issue'] ?? '', 'page' => (int) ($filters['page'] ?? 1)];
|
|
}
|
|
|
|
public function review(Request $request, AdministrativeGeography $geography): Response
|
|
{
|
|
$filters = $this->reviewFilters($request, $geography);
|
|
$rows = $geography->inconsistencies($filters);
|
|
Inertia::encryptHistory();
|
|
$response = Inertia::render('company/geography-review', ['filters' => $filters, 'records' => array_slice($rows, ($filters['page'] - 1) * 50, 50), 'total' => count($rows), 'manifest' => $geography->catalogue()['manifest'], 'issues' => AdministrativeGeography::ISSUES])->toResponse($request);
|
|
$response->headers->set('Cache-Control', 'private, no-store, max-age=0');
|
|
|
|
return $response;
|
|
}
|
|
|
|
public function exportReview(Request $request, AdministrativeGeography $geography): StreamedResponse
|
|
{
|
|
$filters = $this->reviewFilters($request, $geography);
|
|
$rows = $geography->inconsistencies($filters);
|
|
|
|
return response()->streamDownload(function () use ($rows) {
|
|
$output = fopen('php://output', 'w');
|
|
if ($output === false) {
|
|
throw new \RuntimeException('Não foi possível preparar a exportação.');
|
|
}
|
|
$safe = fn ($value) => preg_match('/^[\s]*[=+@-]|^[\t\r\n]/u', $value) ? "'".$value : $value;
|
|
fputcsv($output, ['Linha', 'ID', 'Provincia', 'Distrito', 'Posto_Administrativo', 'Localidade', 'Bairro_Urbano', 'Aldeia_Quarteirao', 'Designacao', 'Diagnosticos'], ',', '"', '');
|
|
foreach ($rows as $record) {
|
|
$raw = $record['raw'];
|
|
fputcsv($output, array_map($safe, [(string) $record['source_row'], $record['id'], ...array_values(array_diff_key($raw, ['ID' => true])), implode('; ', array_map(fn ($issue) => AdministrativeGeography::ISSUES[$issue], $record['issues']))]), ',', '"', '');
|
|
}
|
|
fclose($output);
|
|
}, 'sgc-cartografia-pendencias.csv', ['Content-Type' => 'text/csv; charset=UTF-8', 'Cache-Control' => 'private, no-store, max-age=0', 'X-Content-Type-Options' => 'nosniff']);
|
|
}
|
|
|
|
public function index(Request $request, AdministrativeGeography $geography): JsonResponse
|
|
{
|
|
abort_unless($request->user('web') instanceof User && $request->user('web')->tenant_id !== null, 403);
|
|
abort_unless($geography->enabled(), 503);
|
|
$data = $request->validate(['parent' => 'nullable|string|regex:/^[0-9]+$/D|max:20', 'page' => 'sometimes|integer|min:1|max:10000']);
|
|
|
|
return response()->json($geography->children($data['parent'] ?? null, (int) ($data['page'] ?? 1)));
|
|
}
|
|
|
|
public function company(Request $request, AdministrativeGeography $geography): RedirectResponse
|
|
{
|
|
abort_unless($geography->enabled(), 503);
|
|
$data = $request->validate(['geography_reference' => 'present|nullable|array:country,catalogue,record_id', 'geography_reference.catalogue' => 'required_with:geography_reference|string|size:64', 'geography_reference.record_id' => 'required_with:geography_reference|string|regex:/^[0-9]+$/D|max:20', 'expected_reference' => 'present|nullable|array:country,catalogue,record_id']);
|
|
$actor = $request->user('web');
|
|
abort_unless($actor instanceof User, 403);
|
|
DB::transaction(function () use ($actor, $data, $geography) {
|
|
app(ConflictGuard::class)->lockCompany($actor);
|
|
$actor = app(OrganizationConfiguration::class)->administrator($actor);
|
|
abort_if(app(OrganizationAccess::class)->maintenanceOnly($actor), 403);
|
|
$company = Tenant::query()->lockForUpdate()->findOrFail($actor->tenant_id);
|
|
if ($company->geography_reference != $data['expected_reference']) {
|
|
throw ValidationException::withMessages(['geography_reference' => 'A localização foi alterada. Reabra a parametrização.']);
|
|
}
|
|
$before = $company->geography_reference;
|
|
$after = $geography->canonical($data['geography_reference']);
|
|
if ($before != $after) {
|
|
$company->update(['geography_reference' => $after]);
|
|
app(RequisitionWorkflow::class)->audit($actor, 'company.geography_updated', null, ['geography_reference' => $before], ['geography_reference' => $after]);
|
|
}
|
|
});
|
|
|
|
return back();
|
|
}
|
|
}
|