commit de armazens
tests / ci (push) Canceled after 0s

This commit is contained in:
2026-11-04 09:40:26 +02:00
parent e753f7a0a4
commit 63d20b10fc
245 changed files with 10847 additions and 230 deletions
@@ -5,7 +5,9 @@ namespace App\Http\Controllers;
use App\Models\SupplierEvaluation;
use App\Models\SupplierImprovement;
use App\Models\User;
use App\Services\AdministrativeGeography;
use App\Services\SupplierEvaluationWorkflow;
use App\Services\SupplierGeography;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
@@ -16,6 +18,22 @@ use Symfony\Component\HttpFoundation\Response;
class SupplierImprovementReportController extends Controller
{
private function actor(Request $request): User
{
$actor = $request->user('web');
abort_unless($actor instanceof User, 403);
return $actor;
}
/** @param array<string,mixed> $filters
* @return array<string,mixed>|null
*/
private function territory(array $filters): ?array
{
return empty($filters['location']) ? null : app(AdministrativeGeography::class)->describe(['country' => 'MZ', 'catalogue' => config('geography.source_sha256'), 'record_id' => $filters['location']]);
}
private function snapshot(\Closure $read): mixed
{
if (DB::getDriverName() === 'pgsql' && DB::transactionLevel() !== 0) {
@@ -34,12 +52,12 @@ class SupplierImprovementReportController extends Controller
/** @return array<string,mixed> */
private function filters(Request $request): array
{
$actor = app(SupplierEvaluationWorkflow::class)->reader($request->user());
$actor = app(SupplierEvaluationWorkflow::class)->reader($this->actor($request));
$request->setUserResolver(fn () => $actor);
abort_unless(Schema::hasTable('supplier_improvements'), 503, 'A instalação das medidas de melhoria ainda não está concluída.');
$filters = $request->validate(['supplier_id' => 'nullable|integer|min:1', 'responsible_id' => 'nullable|integer|min:1',
'status' => 'nullable|in:open,review,returned,closed', 'from' => 'nullable|date_format:Y-m-d', 'to' => 'nullable|date_format:Y-m-d',
'overdue' => 'nullable|in:1', 'search' => 'nullable|string|max:200']);
'overdue' => 'nullable|in:1', 'search' => 'nullable|string|max:200', 'location' => 'nullable|string|regex:/^[0-9]+$/D|max:20']);
if (! empty($filters['from']) && ! empty($filters['to']) && $filters['to'] < $filters['from']) {
throw ValidationException::withMessages(['to' => 'O fim do intervalo deve ser igual ou posterior ao início.']);
}
@@ -52,7 +70,11 @@ class SupplierImprovementReportController extends Controller
*/
private function query(Request $request, array $filters, string $today): Builder
{
$query = SupplierImprovement::query()->visibleTo($request->user());
$query = SupplierImprovement::query()->visibleTo($this->actor($request));
if (! empty($filters['location'])) {
$suppliers = app(SupplierGeography::class)->matching($filters['location'])->select('id');
$query->whereHas('evaluation', fn ($evaluation) => $evaluation->whereIn('supplier_id', $suppliers));
}
if (! empty($filters['supplier_id'])) {
$query->whereHas('evaluation', fn ($q) => $q->where('supplier_id', $filters['supplier_id']));
}
@@ -81,7 +103,7 @@ class SupplierImprovementReportController extends Controller
*/
private function records(Builder $query, Request $request): Builder
{
return $query->with(['evaluation:id,supplier_id,supplier_snapshot,revision', 'responsible' => fn ($q) => $q->where('tenant_id', $request->user()->tenant_id)->select(['id', 'name'])]);
return $query->with(['evaluation:id,supplier_id,supplier_snapshot,revision', 'responsible' => fn ($q) => $q->where('tenant_id', $this->actor($request)->tenant_id)->select(['id', 'name'])]);
}
/** @return array<string,mixed> */
@@ -99,17 +121,17 @@ class SupplierImprovementReportController extends Controller
$props = $this->snapshot(function () use ($request): array {
$filters = $this->filters($request);
$today = today()->toDateString();
$all = SupplierImprovement::query()->visibleTo($request->user());
$all = SupplierImprovement::query()->visibleTo($this->actor($request));
$query = $this->query($request, $filters, $today);
$counts = (clone $query)->selectRaw('status, COUNT(*) AS count')->groupBy('status')->pluck('count', 'status')->map(fn ($n) => (int) $n);
$overdue = (clone $query)->where('status', '!=', 'closed')->where('due_on', '<', $today)->count();
// Choices derive only from visible measures, including prior revisions and former assignees.
$evaluations = SupplierEvaluation::query()->visibleTo($request->user())->whereIn('id', (clone $all)->select('supplier_evaluation_id'))->orderByDesc('id')->get(['supplier_id', 'supplier_snapshot']);
$evaluations = SupplierEvaluation::query()->visibleTo($this->actor($request))->whereIn('id', (clone $all)->select('supplier_evaluation_id'))->orderByDesc('id')->get(['supplier_id', 'supplier_snapshot']);
return ['filters' => $filters, 'today' => $today, 'generatedAt' => now()->toIso8601String(), 'counts' => $counts, 'overdue' => $overdue,
return ['filters' => $filters, 'geographyEnabled' => app(AdministrativeGeography::class)->enabled(), 'geographyFilter' => $this->territory($filters), 'today' => $today, 'generatedAt' => now()->toIso8601String(), 'counts' => $counts, 'overdue' => $overdue,
'records' => $this->records($query, $request)->orderBy('due_on')->orderBy('id')->paginate(25)->withQueryString()->through(fn (SupplierImprovement $m) => $this->row($m, $today)),
'suppliers' => $evaluations->map(fn (SupplierEvaluation $e) => ['id' => $e->supplier_id, 'name' => $e->supplier_snapshot['name']])->unique('id')->values(),
'responsibles' => User::query()->where('tenant_id', $request->user()->tenant_id)->whereIn('id', (clone $all)->select('responsible_id'))->orderBy('name')->get(['id', 'name'])];
'responsibles' => User::query()->where('tenant_id', $this->actor($request)->tenant_id)->whereIn('id', (clone $all)->select('responsible_id'))->orderBy('name')->get(['id', 'name'])];
});
Inertia::encryptHistory();
$response = Inertia::render('reports/supplier-improvements', $props)->toResponse($request);
@@ -121,6 +143,9 @@ class SupplierImprovementReportController extends Controller
public function csv(Request $request): Response
{
$filters = $this->filters($request);
if (! empty($filters['location'])) {
app(SupplierGeography::class)->matching($filters['location']);
}
$text = fn (?string $value) => preg_match('/^[\s]*[=+\-@]|^[\t\r\n]/u', $value ?? '') ? "'".$value : ($value ?? '');
return response()->streamDownload(fn () => $this->snapshot(function () use ($request, $filters, $text) {
@@ -133,7 +158,12 @@ class SupplierImprovementReportController extends Controller
}
fwrite($out, "\xEF\xBB\xBF");
fputcsv($out, ['Medidas de melhoria de fornecedores', 'Consulta', now()->toIso8601String(), 'Data de referência', $today], ';', '"', '');
fputcsv($out, ['Filtros', $text(json_encode($filters, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR))], ';', '"', '');
$exportFilters = $filters;
if (! empty($filters['location'])) {
$territory = $this->territory($filters);
$exportFilters['localizacao_actual_da_ficha'] = implode(' / ', array_column($territory['path'] ?? [], 'name'));
}
fputcsv($out, ['Filtros', $text(json_encode($exportFilters, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR))], ';', '"', '');
fputcsv($out, ['Medida', 'Fornecedor preservado', 'Avaliação', 'Revisão', 'Título', 'Objectivo', 'Responsável', 'Prazo', 'Estado', 'Prazo ultrapassado', 'Versão', 'Resultado', 'Evidência', 'Última conferência', 'Conferida em', 'Avaliação e acompanhamento', 'Relatório individual'], ';', '"', '');
foreach ($this->records($this->query($request, $filters, $today), $request)->lazyById(200) as $m) {
$row = $this->row($m, $today);