Files
compras/app/Http/Controllers/AdministrativeGeographyController.php
nala 63d20b10fc
tests / ci (push) Canceled after 0s
commit de armazens
2026-11-04 09:40:26 +02:00

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();
}
}