diff --git a/app/Domains/Ticketing/Event/Controllers/AdminApp/EventController.php b/app/Domains/Ticketing/Event/Controllers/AdminApp/EventController.php index 30d31b56..25c5a881 100644 --- a/app/Domains/Ticketing/Event/Controllers/AdminApp/EventController.php +++ b/app/Domains/Ticketing/Event/Controllers/AdminApp/EventController.php @@ -2,6 +2,7 @@ namespace App\Domains\Ticketing\Event\Controllers\AdminApp; +use App\Domains\Core\Auth\Services\EventScopeService; use App\Domains\Ticketing\Event\Models\EventDate; use App\Domains\Ticketing\Event\Requests\RescheduleEventDateRequest; use App\Domains\Ticketing\Event\Requests\StoreEventDateRequest; @@ -19,7 +20,8 @@ class EventController extends Controller public function show(Request $request): EventResource { return EventResource::make( - $this->eventService->forTenant($request->user()->tenant()->firstOrFail()) + $this->eventService->forTenant($request->user()->tenant()->firstOrFail(), + app(EventScopeService::class)->eventId($request->user())) ); } @@ -28,7 +30,8 @@ class EventController extends Controller return EventResource::make( $this->eventService->updateForTenant( $request->user()->tenant()->firstOrFail(), - $request->validated() + $request->validated(), + app(EventScopeService::class)->eventId($request->user()) ) ); } @@ -39,6 +42,7 @@ class EventController extends Controller $this->eventService->createDateForTenant( $request->user()->tenant()->firstOrFail(), $request->validated(), + app(EventScopeService::class)->eventId($request->user()), ) ); } diff --git a/app/Domains/Ticketing/Event/Services/EventService.php b/app/Domains/Ticketing/Event/Services/EventService.php index 212dee28..abd0325a 100644 --- a/app/Domains/Ticketing/Event/Services/EventService.php +++ b/app/Domains/Ticketing/Event/Services/EventService.php @@ -2,18 +2,19 @@ namespace App\Domains\Ticketing\Event\Services; -use App\Domains\Core\Auth\Models\User; use App\Domains\Commerce\Cart\Services\InvalidateEventDateCartsService; use App\Domains\Commerce\Catalog\Models\Variant; use App\Domains\Commerce\Catalog\Services\StockReservationService; use App\Domains\Commerce\Catalog\Services\VariantReplacementService; +use App\Domains\Core\Auth\Models\User; +use App\Domains\Core\Auth\Services\EventScopeService; +use App\Domains\Core\Tenant\Models\Tenant; use App\Domains\Ticketing\Event\Enums\EventDateChangeType; use App\Domains\Ticketing\Event\Events\EventDateRescheduled; use App\Domains\Ticketing\Event\Events\EventDateSuspended; +use App\Domains\Ticketing\Event\Models\Event; use App\Domains\Ticketing\Event\Models\EventDate; use App\Domains\Ticketing\Event\Models\EventDateChange; -use App\Domains\Ticketing\Event\Models\Event; -use App\Domains\Core\Tenant\Models\Tenant; use App\Domains\Ticketing\Ticket\Models\Ticket; use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; @@ -34,16 +35,16 @@ class EventService private readonly InvalidateEventDateCartsService $invalidateEventDateCarts, ) {} - public function forTenant(Tenant $tenant): Event + public function forTenant(Tenant $tenant, ?int $eventId = null): Event { - return $tenant->activeEvent->load(['dates.validityTime', 'socialMedia']); + return $this->resolveEvent($tenant, $eventId)->load(['dates.validityTime', 'socialMedia']); } /** @param array $data */ - public function updateForTenant(Tenant $tenant, array $data): Event + public function updateForTenant(Tenant $tenant, array $data, ?int $eventId = null): Event { - return DB::transaction(function () use ($tenant, $data): Event { - $event = $tenant->activeEvent; + return DB::transaction(function () use ($tenant, $data, $eventId): Event { + $event = $this->resolveEvent($tenant, $eventId); $event->update([ 'title' => $data['title'], 'location' => $data['location'], @@ -69,10 +70,10 @@ class EventService } /** @param array{date: string, start_time: string, end_time: string} $data */ - public function createDateForTenant(Tenant $tenant, array $data): EventDate + public function createDateForTenant(Tenant $tenant, array $data, ?int $eventId = null): EventDate { - return DB::transaction(function () use ($tenant, $data): EventDate { - $event = $tenant->activeEvent; + return DB::transaction(function () use ($tenant, $data, $eventId): EventDate { + $event = $this->resolveEvent($tenant, $eventId); $attributes = $this->dateAttributes($data); if ($event->dates()->where($attributes)->exists()) { @@ -93,7 +94,7 @@ class EventService ?User $createdBy = null, ): EventDate { return DB::transaction(function () use ($tenant, $eventDate, $data, $createdBy): EventDate { - $source = $this->lockedDateForTenant($tenant, $eventDate); + $source = $this->lockedDateForTenant($tenant, $eventDate, $createdBy); if ($source->suspended_at !== null) { throw ValidationException::withMessages([ @@ -172,7 +173,7 @@ class EventService ?User $createdBy = null, ): EventDate { return DB::transaction(function () use ($tenant, $eventDate, $createdBy): EventDate { - $date = $this->lockedDateForTenant($tenant, $eventDate); + $date = $this->lockedDateForTenant($tenant, $eventDate, $createdBy); if ($date->rescheduled_to_event_date_id !== null) { throw ValidationException::withMessages([ @@ -216,10 +217,18 @@ class EventService }); } - private function lockedDateForTenant(Tenant $tenant, EventDate $eventDate): EventDate + private function resolveEvent(Tenant $tenant, ?int $eventId): Event + { + return Event::query()->where('tenant_code', $tenant->codigo) + ->findOrFail($eventId ?? $tenant->active_event_id); + } + + private function lockedDateForTenant(Tenant $tenant, EventDate $eventDate, ?User $actor = null): EventDate { return $tenant->eventDates() ->whereKey($eventDate->getKey()) + ->when($actor !== null && ! $actor->isTenantAdministrator(), + fn ($query) => $query->where('event_id', app(EventScopeService::class)->eventId($actor))) ->lockForUpdate() ->firstOrFail(); } diff --git a/app/Domains/Ticketing/Event/documentacion/README.md b/app/Domains/Ticketing/Event/documentacion/README.md index 9ce3af94..fb94f845 100644 --- a/app/Domains/Ticketing/Event/documentacion/README.md +++ b/app/Domains/Ticketing/Event/documentacion/README.md @@ -37,10 +37,10 @@ se guardan en el evento. Sin evento activo se conservan las redes propias del te ## API -Los endpoints de AdminApp usan `auth:sanctum` y `adminapp.tenant`: +Los endpoints de AdminApp usan `auth:sanctum` y `adminapp.tenant:event`: -- `GET/PUT /v1/adminapp/tenant/event`: acceso al evento seleccionado para el - storefront de evento único. +- `GET/PUT /v1/adminapp/tenant/event`: acceso al evento asociado al usuario con scope de evento; + para admins de tenant se conserva el evento activo. - `POST /v1/adminapp/tenant/event-dates`: crea una fecha para ese evento. - `POST /v1/adminapp/tenant/event-dates/{eventDate}/reschedule` y `POST /v1/adminapp/tenant/event-dates/{eventDate}/suspend`: cambios de fecha. @@ -56,4 +56,5 @@ después de excluir los cambios que el usuario ya vio tres veces. La configuración de devoluciones se persiste en `events`, se entrega en `EventResource` y se aplica al evento de cada ticket. La migración inicial copia -los valores anteriores del tenant a sus eventos. +los valores anteriores del tenant a sus eventos. Las fechas se autorizan por +tenant y por evento antes de cualquier suspensión o reprogramación. diff --git a/app/Domains/Ticketing/Event/routes/adminapp.php b/app/Domains/Ticketing/Event/routes/adminapp.php index a975e983..b99b3421 100644 --- a/app/Domains/Ticketing/Event/routes/adminapp.php +++ b/app/Domains/Ticketing/Event/routes/adminapp.php @@ -4,7 +4,7 @@ use App\Domains\Ticketing\Event\Controllers\AdminApp\EventController; use Illuminate\Support\Facades\Route; Route::prefix('v1/adminapp/tenant') - ->middleware(['auth:sanctum', 'adminapp.tenant']) + ->middleware(['auth:sanctum', 'adminapp.tenant:event']) ->group(function (): void { Route::get('event', [EventController::class, 'show']); Route::put('event', [EventController::class, 'update']); diff --git a/app/Shared/Forms/Controllers/AdminApp/EventFormController.php b/app/Shared/Forms/Controllers/AdminApp/EventFormController.php index 55514d85..f735811e 100644 --- a/app/Shared/Forms/Controllers/AdminApp/EventFormController.php +++ b/app/Shared/Forms/Controllers/AdminApp/EventFormController.php @@ -2,9 +2,10 @@ namespace App\Shared\Forms\Controllers\AdminApp; +use App\Domains\Core\Auth\Services\EventScopeService; +use App\Http\Controllers\Controller; use App\Shared\Forms\Resources\EventFormResource; use App\Shared\Forms\Services\EventFormService; -use App\Http\Controllers\Controller; use Illuminate\Http\Request; class EventFormController extends Controller @@ -15,7 +16,8 @@ class EventFormController extends Controller { return EventFormResource::make( $this->eventFormService->get( - $request->user('sanctum')->tenant()->firstOrFail() + $request->user('sanctum')->tenant()->firstOrFail(), + app(EventScopeService::class)->eventId($request->user()) ) ); } diff --git a/app/Shared/Forms/Services/EventFormService.php b/app/Shared/Forms/Services/EventFormService.php index 33e44135..d290b173 100644 --- a/app/Shared/Forms/Services/EventFormService.php +++ b/app/Shared/Forms/Services/EventFormService.php @@ -4,14 +4,17 @@ namespace App\Shared\Forms\Services; use App\Domains\Core\Tenant\Models\SocialMedia; use App\Domains\Core\Tenant\Models\Tenant; +use App\Domains\Ticketing\Event\Models\Event; use Illuminate\Database\Eloquent\Collection; class EventFormService { /** @return array{social_media: Collection} */ - public function get(Tenant $tenant): array + public function get(Tenant $tenant, ?int $eventId = null): array { - $event = $tenant->activeEvent; + $event = $eventId === null ? $tenant->activeEvent + : Event::query() + ->where('tenant_code', $tenant->codigo)->findOrFail($eventId); $urls = $event?->socialMedia() ->pluck('event_social_media.url', 'social_media.code') ?? collect(); diff --git a/app/Shared/Forms/routes/adminapp.php b/app/Shared/Forms/routes/adminapp.php index 6517d639..6e46f3a6 100644 --- a/app/Shared/Forms/routes/adminapp.php +++ b/app/Shared/Forms/routes/adminapp.php @@ -12,33 +12,33 @@ use App\Shared\Forms\Controllers\AdminApp\TicketFormController; use Illuminate\Support\Facades\Route; Route::prefix('v1/adminapp/forms') - ->middleware(['auth:sanctum', 'adminapp.tenant']) + ->middleware(['auth:sanctum', 'adminapp.tenant:event']) ->group(function (): void { Route::get('event', EventFormController::class); Route::get( 'desfile/entry-reservation', DesfileEntryReservationFormController::class - )->middleware('tenant.menu:adminapp.desfile.reservas') + )->middleware('adminapp.tenant')->middleware('tenant.menu:adminapp.desfile.reservas') ->name('adminapp.forms.desfile.entry-reservation'); - Route::get('sale', SaleFormController::class); - Route::get('staff', StaffFormController::class); - Route::get('tickets-filter', TicketFilterFormController::class) + Route::get('sale', SaleFormController::class)->middleware('adminapp.tenant'); + Route::get('staff', StaffFormController::class)->middleware('adminapp.tenant'); + Route::get('tickets-filter', TicketFilterFormController::class)->middleware('adminapp.tenant') ->middleware('tenant.menu:adminapp.tickets') ->name('adminapp.forms.tickets-filter'); Route::get( 'fiesta-futbol-infantil/ticket', TicketFormController::class - ); + )->middleware('adminapp.tenant'); Route::get( 'fiesta-futbol-infantil/entry', EntryFormController::class - ); + )->middleware('adminapp.tenant'); Route::get( 'fiesta-futbol-infantil/merchandise', MerchandiseFormController::class - ); + )->middleware('adminapp.tenant'); Route::get( 'fiesta-futbol-infantil/food', FoodFormController::class - ); + )->middleware('adminapp.tenant'); });