feat(tickets): group user tickets by event

This commit is contained in:
2026-09-22 12:26:16 -03:00
parent 6efe8439c7
commit 3401c934b2
12 changed files with 289 additions and 20 deletions

View File

@@ -5,6 +5,7 @@ namespace App\Domains\Ticketing\Event\Models;
use App\Domains\Commerce\Catalog\Models\CatalogItem;
use App\Domains\Core\Tenant\Models\SocialMedia;
use App\Domains\Core\Tenant\Models\Tenant;
use App\Domains\Ticketing\Ticket\Models\Ticket;
use App\Shared\Attachable\Models\Attachment;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
@@ -53,6 +54,12 @@ class Event extends Model
return $this->hasMany(CatalogItem::class)->whereAvailable()->orderBy('group_order')->orderBy('id');
}
/** @return HasMany<Ticket, $this> */
public function tickets(): HasMany
{
return $this->hasMany(Ticket::class);
}
/** @return BelongsToMany<SocialMedia, $this> */
public function socialMedia(): BelongsToMany
{

View File

@@ -6,31 +6,47 @@ use App\Domains\Core\Tenant\Models\Tenant;
use App\Domains\Ticketing\Ticket\Exceptions\TicketNotAvailableException;
use App\Domains\Ticketing\Ticket\Models\Ticket;
use App\Domains\Ticketing\Ticket\Requests\DownloadTicketsPdfRequest;
use App\Domains\Ticketing\Ticket\Requests\ListTicketsRequest;
use App\Domains\Ticketing\Ticket\Resources\TicketEventResource;
use App\Domains\Ticketing\Ticket\Resources\TicketResource;
use App\Domains\Ticketing\Ticket\Services\TicketPdfService;
use App\Domains\Ticketing\Ticket\Services\TicketPresentationResolver;
use App\Domains\Ticketing\Ticket\Services\TicketValidityResolver;
use App\Domains\Ticketing\Ticket\Services\UserTicketQueryService;
use App\Http\Controllers\Controller;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Illuminate\Http\Response;
class TicketController extends Controller
{
public function __construct(private readonly TicketPdfService $ticketPdfService) {}
public function __construct(
private readonly TicketPdfService $ticketPdfService,
private readonly UserTicketQueryService $userTickets,
) {}
public function index(Request $request, Tenant $tenant): JsonResponse
public function index(ListTicketsRequest $request, Tenant $tenant): JsonResponse
{
$tickets = Ticket::query()
->where('tenant_code', $tenant->codigo)
->where('user_id', $request->user()->getKey())
->with([...TicketValidityResolver::RELATIONS, ...TicketPresentationResolver::RELATIONS])
->orderByDesc('id')
->get();
$eventId = $request->validated('event_id');
$tickets = $this->userTickets->tickets(
$tenant,
$request->user(),
$eventId === null ? null : (int) $eventId,
);
return TicketResource::collection($tickets)->response();
}
public function events(Request $request, Tenant $tenant): AnonymousResourceCollection
{
abort_unless($tenant->storefront_website_type_code === 'onticket_multi_event', 404);
return TicketEventResource::collection(
$this->userTickets->events($tenant, $request->user())
);
}
public function downloadPdf(DownloadTicketsPdfRequest $request, Tenant $tenant): Response
{
$ticketIds = $request->validated('ticket_ids');

View File

@@ -2,16 +2,17 @@
namespace App\Domains\Ticketing\Ticket\Models;
use App\Domains\Core\Auth\Models\User;
use App\Domains\Commerce\Catalog\Models\CatalogItem;
use App\Domains\Commerce\Catalog\Models\Variant;
use App\Shared\Logging\Models\Concerns\LogsValueChanges;
use App\Domains\Commerce\Purchase\Models\PurchaseItem;
use App\Domains\Core\Auth\Models\User;
use App\Domains\Core\Tenant\Models\Tenant;
use App\Domains\Ticketing\Event\Models\Event;
use App\Domains\Ticketing\Ticket\Services\ResolvedTicketValidity;
use App\Domains\Ticketing\Ticket\Services\ResolvedValidityGroup;
use App\Domains\Ticketing\Ticket\Services\TicketPresentationResolver;
use App\Domains\Ticketing\Ticket\Services\TicketValidityResolver;
use App\Shared\Logging\Models\Concerns\LogsValueChanges;
use Carbon\CarbonInterface;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
@@ -24,6 +25,7 @@ use Illuminate\Validation\ValidationException;
#[Fillable([
'tenant_code',
'event_id',
'ticket',
'source_purchase_item_id',
'source_catalog_item_id',
@@ -75,6 +77,7 @@ class Ticket extends Model
protected function casts(): array
{
return [
'event_id' => 'integer',
'source_catalog_item_id' => 'integer',
'source_variant_id' => 'integer',
'source_purchase_item_id' => 'integer',
@@ -136,6 +139,12 @@ class Ticket extends Model
return $this->belongsTo(Tenant::class, 'tenant_code', 'codigo');
}
/** @return BelongsTo<Event, $this> */
public function event(): BelongsTo
{
return $this->belongsTo(Event::class);
}
public function allow_refund(): bool
{
return $this->tenant?->allow_refund() ?? false;

View File

@@ -0,0 +1,30 @@
<?php
namespace App\Domains\Ticketing\Ticket\Requests;
use App\Domains\Core\Tenant\Models\Tenant;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class ListTicketsRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
/** @return array<string, mixed> */
public function rules(): array
{
/** @var Tenant $tenant */
$tenant = $this->route('tenant');
return [
'event_id' => [
'sometimes',
'integer',
Rule::exists('events', 'id')->where('tenant_code', $tenant->codigo),
],
];
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace App\Domains\Ticketing\Ticket\Resources;
use App\Domains\Ticketing\Event\Models\Event;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
/** @mixin Event */
class TicketEventResource extends JsonResource
{
/** @return array<string, mixed> */
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'title' => $this->title,
'image' => $this->attachment?->getTemporaryUrl(1440),
];
}
}

View File

@@ -15,6 +15,7 @@ class TicketResource extends JsonResource
return [
'id' => $this->id,
'tenant_code' => $this->tenant_code,
'event_id' => $this->event_id,
'ticket' => $this->ticket,
'status' => $this->status,
'status_label' => $this->status_label,

View File

@@ -2,9 +2,9 @@
namespace App\Domains\Ticketing\Ticket\Services;
use App\Domains\Core\Auth\Models\User;
use App\Domains\Commerce\Catalog\Models\CatalogItem;
use App\Domains\Commerce\Catalog\Models\Variant;
use App\Domains\Core\Auth\Models\User;
use App\Domains\Ticketing\Ticket\Exceptions\TicketGenerationException;
use App\Domains\Ticketing\Ticket\Models\Ticket;
use Illuminate\Support\Collection;
@@ -44,6 +44,7 @@ class TicketGeneratorService
$variant = $target['variant'];
$ticket = Ticket::query()->create([
'tenant_code' => $item->tenant_code,
'event_id' => $item->event_id,
'ticket' => (string) Str::uuid(),
'source_purchase_item_id' => $sourcePurchaseItemId,
'source_catalog_item_id' => $item->getKey(),

View File

@@ -0,0 +1,45 @@
<?php
namespace App\Domains\Ticketing\Ticket\Services;
use App\Domains\Core\Auth\Models\User;
use App\Domains\Core\Tenant\Models\Tenant;
use App\Domains\Ticketing\Event\Models\Event;
use App\Domains\Ticketing\Ticket\Models\Ticket;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
class UserTicketQueryService
{
/** @return Collection<int, Ticket> */
public function tickets(Tenant $tenant, User $user, ?int $eventId = null): Collection
{
return $this->ticketQuery($tenant, $user)
->when($eventId !== null, fn (Builder $query): Builder => $query
->where('event_id', $eventId))
->with([...TicketValidityResolver::RELATIONS, ...TicketPresentationResolver::RELATIONS])
->orderByDesc('id')
->get();
}
/** @return Collection<int, Event> */
public function events(Tenant $tenant, User $user): Collection
{
return Event::query()
->where('tenant_code', $tenant->codigo)
->whereHas('tickets', fn (Builder $query): Builder => $query
->where('tenant_code', $tenant->codigo)
->where('user_id', $user->getKey()))
->with('attachment')
->orderByDesc('id')
->get();
}
/** @return Builder<Ticket> */
private function ticketQuery(Tenant $tenant, User $user): Builder
{
return Ticket::query()
->where('tenant_code', $tenant->codigo)
->where('user_id', $user->getKey());
}
}

View File

@@ -6,6 +6,7 @@ use Illuminate\Support\Facades\Route;
Route::prefix('tenants/{tenant:codigo}')
->middleware('auth:sanctum')
->group(function (): void {
Route::get('tickets/events', [TicketController::class, 'events']);
Route::get('tickets', [TicketController::class, 'index']);
Route::post('tickets/pdf', [TicketController::class, 'downloadPdf']);
});