baseQuery($tenant, $filters); $tickets = (clone $query) ->with([ ...TicketValidityResolver::RELATIONS, ...TicketPresentationResolver::RELATIONS, 'user', 'scannerUser', 'sourceCatalogItem.category', 'sourcePurchase.items', ]) ->orderByDesc('id') ->paginateFromRequest() ->withQueryString(); return new AdminAppTicketResult( tickets: $tickets, scannedTickets: (clone $query)->whereNotNull('used_at')->count(), totalTickets: $tickets->total(), ); } /** * @param array{q?: string|null, category?: string|null, product?: string|null, type?: string|null, date?: string|null, status?: string|null, page?: int, per_page?: int} $filters * @return Builder */ private function baseQuery(Tenant $tenant, array $filters): Builder { $search = trim((string) ($filters['q'] ?? '')); $query = Ticket::query() ->where('tenant_code', $tenant->codigo) ->when($search !== '', function (Builder $query) use ($search): void { $query->when( ctype_digit($search), fn (Builder $searchQuery): Builder => $searchQuery ->where('tickets.id', (int) $search), fn (Builder $searchQuery): Builder => $searchQuery ->where('ticket', 'like', "%{$search}%"), ); }) ->when($filters['category'] ?? null, function (Builder $query, string $category): void { $query->whereHas('sourceCatalogItem.category', fn (Builder $categoryQuery): Builder => $categoryQuery ->whereRaw('LOWER(nombre) = ?', [mb_strtolower(trim($category))])); }) ->when($filters['product'] ?? null, function (Builder $query, string $product) use ($filters): void { $this->applyProductFilter($query, (string) ($filters['category'] ?? ''), $product); }) ->when($filters['type'] ?? null, function (Builder $query, string $type) use ($filters): void { $this->applyTypeFilter($query, (string) ($filters['category'] ?? ''), $type); }) ->when($filters['date'] ?? null, fn (Builder $query, string $date): Builder => $query ->whereHas('sourcePurchase', fn (Builder $purchaseQuery): Builder => $purchaseQuery ->whereDate('created_at', $date))); $this->applyStatusFilter($query, $filters['status'] ?? null); return $query; } /** @param Builder $query */ private function applyProductFilter(Builder $query, string $category, string $product): void { $category = $this->normalizedCategory($category); if (in_array($category, ['alojamientos', 'camping'], true)) { $this->whereVariantDefinition($query, 'tipo_alojamiento', $product); return; } if (in_array($category, ['comidas', 'comida'], true)) { $query->whereHas('sourceVariant', function (Builder $variantQuery) use ($product): void { $variantQuery->where(function (Builder $dateQuery) use ($product): void { $dateQuery ->where('event_date_id', $product) ->orWhereHas('eventDates', fn (Builder $eventDateQuery): Builder => $eventDateQuery ->whereKey($product)); }); }); return; } $query->whereHas('sourceCatalogItem', fn (Builder $itemQuery): Builder => $itemQuery ->where('slug', $product)); } /** @param Builder $query */ private function applyTypeFilter(Builder $query, string $category, string $type): void { $attribute = match ($this->normalizedCategory($category)) { 'comidas', 'comida' => 'horario', 'merchandising' => 'color', default => null, }; if ($attribute !== null) { $this->whereVariantDefinition($query, $attribute, $type); } } /** @param Builder $query */ private function whereVariantDefinition(Builder $query, string $attribute, string $value): void { $query->whereHas('sourceVariant.definitions', fn (Builder $definitionQuery): Builder => $definitionQuery ->where('value', $value) ->whereHas('itemAttribute.attribute', fn (Builder $attributeQuery): Builder => $attributeQuery ->where('codigo', $attribute))); } /** @param Builder $query */ private function applyStatusFilter(Builder $query, ?string $status): void { if ($status === null || $status === '') { return; } if ($status === Ticket::STATUS_USED) { $query->whereNotNull('used_at'); return; } $matchingIds = (clone $query) ->whereNull('used_at') ->with(TicketValidityResolver::RELATIONS) ->get() ->filter(fn (Ticket $ticket): bool => $ticket->status === $status) ->pluck('id'); $query->whereIn('tickets.id', $matchingIds); } private function normalizedCategory(string $category): string { return mb_strtolower(trim($category)); } }