feat(events): authorize event editing and date changes by assigned event
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Domains\Ticketing\Event\Controllers\AdminApp;
|
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\Models\EventDate;
|
||||||
use App\Domains\Ticketing\Event\Requests\RescheduleEventDateRequest;
|
use App\Domains\Ticketing\Event\Requests\RescheduleEventDateRequest;
|
||||||
use App\Domains\Ticketing\Event\Requests\StoreEventDateRequest;
|
use App\Domains\Ticketing\Event\Requests\StoreEventDateRequest;
|
||||||
@@ -19,7 +20,8 @@ class EventController extends Controller
|
|||||||
public function show(Request $request): EventResource
|
public function show(Request $request): EventResource
|
||||||
{
|
{
|
||||||
return EventResource::make(
|
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(
|
return EventResource::make(
|
||||||
$this->eventService->updateForTenant(
|
$this->eventService->updateForTenant(
|
||||||
$request->user()->tenant()->firstOrFail(),
|
$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(
|
$this->eventService->createDateForTenant(
|
||||||
$request->user()->tenant()->firstOrFail(),
|
$request->user()->tenant()->firstOrFail(),
|
||||||
$request->validated(),
|
$request->validated(),
|
||||||
|
app(EventScopeService::class)->eventId($request->user()),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,18 +2,19 @@
|
|||||||
|
|
||||||
namespace App\Domains\Ticketing\Event\Services;
|
namespace App\Domains\Ticketing\Event\Services;
|
||||||
|
|
||||||
use App\Domains\Core\Auth\Models\User;
|
|
||||||
use App\Domains\Commerce\Cart\Services\InvalidateEventDateCartsService;
|
use App\Domains\Commerce\Cart\Services\InvalidateEventDateCartsService;
|
||||||
use App\Domains\Commerce\Catalog\Models\Variant;
|
use App\Domains\Commerce\Catalog\Models\Variant;
|
||||||
use App\Domains\Commerce\Catalog\Services\StockReservationService;
|
use App\Domains\Commerce\Catalog\Services\StockReservationService;
|
||||||
use App\Domains\Commerce\Catalog\Services\VariantReplacementService;
|
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\Enums\EventDateChangeType;
|
||||||
use App\Domains\Ticketing\Event\Events\EventDateRescheduled;
|
use App\Domains\Ticketing\Event\Events\EventDateRescheduled;
|
||||||
use App\Domains\Ticketing\Event\Events\EventDateSuspended;
|
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\EventDate;
|
||||||
use App\Domains\Ticketing\Event\Models\EventDateChange;
|
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 App\Domains\Ticketing\Ticket\Models\Ticket;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
@@ -34,16 +35,16 @@ class EventService
|
|||||||
private readonly InvalidateEventDateCartsService $invalidateEventDateCarts,
|
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<string, mixed> $data */
|
/** @param array<string, mixed> $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 {
|
return DB::transaction(function () use ($tenant, $data, $eventId): Event {
|
||||||
$event = $tenant->activeEvent;
|
$event = $this->resolveEvent($tenant, $eventId);
|
||||||
$event->update([
|
$event->update([
|
||||||
'title' => $data['title'],
|
'title' => $data['title'],
|
||||||
'location' => $data['location'],
|
'location' => $data['location'],
|
||||||
@@ -69,10 +70,10 @@ class EventService
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @param array{date: string, start_time: string, end_time: string} $data */
|
/** @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 {
|
return DB::transaction(function () use ($tenant, $data, $eventId): EventDate {
|
||||||
$event = $tenant->activeEvent;
|
$event = $this->resolveEvent($tenant, $eventId);
|
||||||
$attributes = $this->dateAttributes($data);
|
$attributes = $this->dateAttributes($data);
|
||||||
|
|
||||||
if ($event->dates()->where($attributes)->exists()) {
|
if ($event->dates()->where($attributes)->exists()) {
|
||||||
@@ -93,7 +94,7 @@ class EventService
|
|||||||
?User $createdBy = null,
|
?User $createdBy = null,
|
||||||
): EventDate {
|
): EventDate {
|
||||||
return DB::transaction(function () use ($tenant, $eventDate, $data, $createdBy): 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) {
|
if ($source->suspended_at !== null) {
|
||||||
throw ValidationException::withMessages([
|
throw ValidationException::withMessages([
|
||||||
@@ -172,7 +173,7 @@ class EventService
|
|||||||
?User $createdBy = null,
|
?User $createdBy = null,
|
||||||
): EventDate {
|
): EventDate {
|
||||||
return DB::transaction(function () use ($tenant, $eventDate, $createdBy): 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) {
|
if ($date->rescheduled_to_event_date_id !== null) {
|
||||||
throw ValidationException::withMessages([
|
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()
|
return $tenant->eventDates()
|
||||||
->whereKey($eventDate->getKey())
|
->whereKey($eventDate->getKey())
|
||||||
|
->when($actor !== null && ! $actor->isTenantAdministrator(),
|
||||||
|
fn ($query) => $query->where('event_id', app(EventScopeService::class)->eventId($actor)))
|
||||||
->lockForUpdate()
|
->lockForUpdate()
|
||||||
->firstOrFail();
|
->firstOrFail();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,10 +37,10 @@ se guardan en el evento. Sin evento activo se conservan las redes propias del te
|
|||||||
|
|
||||||
## API
|
## 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
|
- `GET/PUT /v1/adminapp/tenant/event`: acceso al evento asociado al usuario con scope de evento;
|
||||||
storefront de evento único.
|
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`: crea una fecha para ese evento.
|
||||||
- `POST /v1/adminapp/tenant/event-dates/{eventDate}/reschedule` y
|
- `POST /v1/adminapp/tenant/event-dates/{eventDate}/reschedule` y
|
||||||
`POST /v1/adminapp/tenant/event-dates/{eventDate}/suspend`: cambios de fecha.
|
`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
|
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
|
`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.
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use App\Domains\Ticketing\Event\Controllers\AdminApp\EventController;
|
|||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
Route::prefix('v1/adminapp/tenant')
|
Route::prefix('v1/adminapp/tenant')
|
||||||
->middleware(['auth:sanctum', 'adminapp.tenant'])
|
->middleware(['auth:sanctum', 'adminapp.tenant:event'])
|
||||||
->group(function (): void {
|
->group(function (): void {
|
||||||
Route::get('event', [EventController::class, 'show']);
|
Route::get('event', [EventController::class, 'show']);
|
||||||
Route::put('event', [EventController::class, 'update']);
|
Route::put('event', [EventController::class, 'update']);
|
||||||
|
|||||||
@@ -2,9 +2,10 @@
|
|||||||
|
|
||||||
namespace App\Shared\Forms\Controllers\AdminApp;
|
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\Resources\EventFormResource;
|
||||||
use App\Shared\Forms\Services\EventFormService;
|
use App\Shared\Forms\Services\EventFormService;
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class EventFormController extends Controller
|
class EventFormController extends Controller
|
||||||
@@ -15,7 +16,8 @@ class EventFormController extends Controller
|
|||||||
{
|
{
|
||||||
return EventFormResource::make(
|
return EventFormResource::make(
|
||||||
$this->eventFormService->get(
|
$this->eventFormService->get(
|
||||||
$request->user('sanctum')->tenant()->firstOrFail()
|
$request->user('sanctum')->tenant()->firstOrFail(),
|
||||||
|
app(EventScopeService::class)->eventId($request->user())
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,14 +4,17 @@ namespace App\Shared\Forms\Services;
|
|||||||
|
|
||||||
use App\Domains\Core\Tenant\Models\SocialMedia;
|
use App\Domains\Core\Tenant\Models\SocialMedia;
|
||||||
use App\Domains\Core\Tenant\Models\Tenant;
|
use App\Domains\Core\Tenant\Models\Tenant;
|
||||||
|
use App\Domains\Ticketing\Event\Models\Event;
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
|
|
||||||
class EventFormService
|
class EventFormService
|
||||||
{
|
{
|
||||||
/** @return array{social_media: Collection<int, SocialMedia>} */
|
/** @return array{social_media: Collection<int, SocialMedia>} */
|
||||||
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()
|
$urls = $event?->socialMedia()
|
||||||
->pluck('event_social_media.url', 'social_media.code') ?? collect();
|
->pluck('event_social_media.url', 'social_media.code') ?? collect();
|
||||||
|
|
||||||
|
|||||||
@@ -12,33 +12,33 @@ use App\Shared\Forms\Controllers\AdminApp\TicketFormController;
|
|||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
Route::prefix('v1/adminapp/forms')
|
Route::prefix('v1/adminapp/forms')
|
||||||
->middleware(['auth:sanctum', 'adminapp.tenant'])
|
->middleware(['auth:sanctum', 'adminapp.tenant:event'])
|
||||||
->group(function (): void {
|
->group(function (): void {
|
||||||
Route::get('event', EventFormController::class);
|
Route::get('event', EventFormController::class);
|
||||||
Route::get(
|
Route::get(
|
||||||
'desfile/entry-reservation',
|
'desfile/entry-reservation',
|
||||||
DesfileEntryReservationFormController::class
|
DesfileEntryReservationFormController::class
|
||||||
)->middleware('tenant.menu:adminapp.desfile.reservas')
|
)->middleware('adminapp.tenant')->middleware('tenant.menu:adminapp.desfile.reservas')
|
||||||
->name('adminapp.forms.desfile.entry-reservation');
|
->name('adminapp.forms.desfile.entry-reservation');
|
||||||
Route::get('sale', SaleFormController::class);
|
Route::get('sale', SaleFormController::class)->middleware('adminapp.tenant');
|
||||||
Route::get('staff', StaffFormController::class);
|
Route::get('staff', StaffFormController::class)->middleware('adminapp.tenant');
|
||||||
Route::get('tickets-filter', TicketFilterFormController::class)
|
Route::get('tickets-filter', TicketFilterFormController::class)->middleware('adminapp.tenant')
|
||||||
->middleware('tenant.menu:adminapp.tickets')
|
->middleware('tenant.menu:adminapp.tickets')
|
||||||
->name('adminapp.forms.tickets-filter');
|
->name('adminapp.forms.tickets-filter');
|
||||||
Route::get(
|
Route::get(
|
||||||
'fiesta-futbol-infantil/ticket',
|
'fiesta-futbol-infantil/ticket',
|
||||||
TicketFormController::class
|
TicketFormController::class
|
||||||
);
|
)->middleware('adminapp.tenant');
|
||||||
Route::get(
|
Route::get(
|
||||||
'fiesta-futbol-infantil/entry',
|
'fiesta-futbol-infantil/entry',
|
||||||
EntryFormController::class
|
EntryFormController::class
|
||||||
);
|
)->middleware('adminapp.tenant');
|
||||||
Route::get(
|
Route::get(
|
||||||
'fiesta-futbol-infantil/merchandise',
|
'fiesta-futbol-infantil/merchandise',
|
||||||
MerchandiseFormController::class
|
MerchandiseFormController::class
|
||||||
);
|
)->middleware('adminapp.tenant');
|
||||||
Route::get(
|
Route::get(
|
||||||
'fiesta-futbol-infantil/food',
|
'fiesta-futbol-infantil/food',
|
||||||
FoodFormController::class
|
FoodFormController::class
|
||||||
);
|
)->middleware('adminapp.tenant');
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user