Refactor entry reservation handling by generalizing ticket reservations
- Renamed `desfile_entry_reservations` to `entry_reservations` and `desfile_reservation_batches` to `reservation_batches`. - Updated models, services, and requests to use the new `EntryReservation` model from the `Ticket` domain instead of the deleted `Desfile` model. - Introduced a new `EntryReservationPaymentType` enum to manage payment types. - Modified database migrations to ensure data integrity during the transition. - Updated tests to reflect changes in the database schema and model relationships. - Ensured that existing functionality remains intact while improving code organization and clarity.
This commit is contained in:
@@ -8,6 +8,7 @@ use App\Domains\Commerce\Catalog\Enums\InventorySubject;
|
||||
use App\Domains\Commerce\Catalog\Services\CatalogInventoryService;
|
||||
use App\Domains\Core\Tenant\Models\Tenant;
|
||||
use App\Domains\Ticketing\Event\Models\Event;
|
||||
use App\Domains\Ticketing\Ticket\Models\EntryReservation;
|
||||
use App\Domains\Ticketing\Ticket\Models\Ticket;
|
||||
use App\Shared\Attachable\Models\Attachment;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
@@ -147,6 +148,12 @@ class CatalogItem extends Model
|
||||
return $this->hasMany(Ticket::class, 'source_catalog_item_id');
|
||||
}
|
||||
|
||||
/** @return HasMany<EntryReservation, $this> */
|
||||
public function entryReservations(): HasMany
|
||||
{
|
||||
return $this->hasMany(EntryReservation::class);
|
||||
}
|
||||
|
||||
/** @return BelongsToMany<Attribute, $this> */
|
||||
public function attributes(): BelongsToMany
|
||||
{
|
||||
@@ -266,13 +273,13 @@ class CatalogItem extends Model
|
||||
{
|
||||
$this->variants
|
||||
->filter(fn (Variant $variant): bool => $variant->exists)
|
||||
->loadMissing('desfileEntryReservations');
|
||||
->loadMissing('entryReservations');
|
||||
|
||||
return $this->variants
|
||||
->each(fn (Variant $variant) => $variant->setRelation('catalogItem', $this))
|
||||
->filter(fn (Variant $variant): bool => $variant->hasOnlyActiveEventDates()
|
||||
&& (! $variant->relationLoaded('desfileEntryReservations')
|
||||
|| $variant->desfileEntryReservations->isEmpty())
|
||||
&& (! $variant->relationLoaded('entryReservations')
|
||||
|| $variant->entryReservations->isEmpty())
|
||||
&& (($includedVariantId !== null && $variant->id === $includedVariantId)
|
||||
|| ($variant->isSellable() && (
|
||||
$this->inventory_policy === InventoryPolicy::Unlimited
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
namespace App\Domains\Commerce\Catalog\Models;
|
||||
|
||||
use App\Domains\Ticketing\Desfile\Models\EntryReservation;
|
||||
use App\Domains\Ticketing\Event\Models\EventDate;
|
||||
use App\Domains\Ticketing\Ticket\Models\EntryReservation;
|
||||
use App\Domains\Ticketing\Ticket\Models\Ticket;
|
||||
use App\Shared\Attachable\Models\Attachment;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
@@ -76,7 +76,7 @@ class Variant extends Model
|
||||
}
|
||||
|
||||
/** @return HasMany<EntryReservation, $this> */
|
||||
public function desfileEntryReservations(): HasMany
|
||||
public function entryReservations(): HasMany
|
||||
{
|
||||
return $this->hasMany(EntryReservation::class);
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ use App\Domains\Commerce\Catalog\Models\Inventory;
|
||||
use App\Domains\Commerce\Catalog\Models\StockReservation;
|
||||
use App\Domains\Commerce\Catalog\Models\StockReservationLine;
|
||||
use App\Domains\Commerce\Catalog\Models\Variant;
|
||||
use App\Domains\Ticketing\Desfile\Models\EntryReservation;
|
||||
use App\Domains\Ticketing\Event\Models\EventDate;
|
||||
use App\Domains\Ticketing\Ticket\Models\EntryReservation;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class VariantReplacementService
|
||||
|
||||
@@ -25,9 +25,8 @@ class TenantTransactionResetService
|
||||
'carts' => $scope['cart_ids']->count(),
|
||||
'cart_items' => $scope['cart_item_ids']->count(),
|
||||
'tickets' => DB::table('tickets')->where('tenant_code', $tenantCode)->count(),
|
||||
'entry_reservations' => DB::table('desfile_entry_reservations')
|
||||
->whereIn('variant_id', DB::table('variantes')->whereIn('catalog_item_id',
|
||||
DB::table('catalog_items')->where('tenant_code', $tenantCode)->select('id'))->select('id'))->count(),
|
||||
'entry_reservations' => DB::table('entry_reservations')
|
||||
->whereIn('catalog_item_id', DB::table('catalog_items')->where('tenant_code', $tenantCode)->select('id'))->count(),
|
||||
'stock_reservations' => $this->reservationQuery($scope)->count(),
|
||||
'purchase_changes' => DB::table('value_changes')
|
||||
->where('tenant_code', $tenantCode)
|
||||
@@ -50,10 +49,9 @@ class TenantTransactionResetService
|
||||
$telepagosQr = DB::table('telepagos_qr')->whereIn('compra_id', $scope['purchase_ids'])->count();
|
||||
$summary = [
|
||||
'stock_reservations_deleted' => $this->reservationQuery($scope)->delete(),
|
||||
'entry_reservations_deleted' => DB::table('desfile_entry_reservations')
|
||||
->whereIn('variant_id', DB::table('variantes')->whereIn('catalog_item_id',
|
||||
DB::table('catalog_items')->where('tenant_code', $tenantCode)->select('id'))->select('id'))->delete(),
|
||||
'entry_reservation_batches_deleted' => DB::table('desfile_reservation_batches')->where('tenant_code', $tenantCode)->delete(),
|
||||
'entry_reservations_deleted' => DB::table('entry_reservations')
|
||||
->whereIn('catalog_item_id', DB::table('catalog_items')->where('tenant_code', $tenantCode)->select('id'))->delete(),
|
||||
'entry_reservation_batches_deleted' => DB::table('reservation_batches')->where('tenant_code', $tenantCode)->delete(),
|
||||
'tickets_deleted' => DB::table('tickets')->where('tenant_code', $tenantCode)->delete(),
|
||||
'purchase_changes_deleted' => DB::table('value_changes')
|
||||
->where('tenant_code', $tenantCode)
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Ticketing\Desfile\Models;
|
||||
|
||||
use App\Domains\Commerce\Catalog\Models\Inventory;
|
||||
use App\Domains\Commerce\Catalog\Models\Variant;
|
||||
use App\Domains\Ticketing\Desfile\Enums\EntryReservationPaymentType;
|
||||
use App\Domains\Ticketing\Ticket\Models\Ticket;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
#[Fillable([
|
||||
'variant_id',
|
||||
'ticket_id',
|
||||
'inventory_id',
|
||||
'batch_id',
|
||||
'fecha_reserva',
|
||||
'importe',
|
||||
'tipo_pago',
|
||||
])]
|
||||
class EntryReservation extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected $table = 'desfile_entry_reservations';
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'variant_id' => 'integer',
|
||||
'fecha_reserva' => 'datetime',
|
||||
'importe' => 'decimal:2',
|
||||
'tipo_pago' => EntryReservationPaymentType::class,
|
||||
];
|
||||
}
|
||||
|
||||
/** @return BelongsTo<Variant, $this> */
|
||||
public function variant(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Variant::class)->withTrashed();
|
||||
}
|
||||
|
||||
public function ticket(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Ticket::class);
|
||||
}
|
||||
|
||||
public function inventory(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Inventory::class);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace App\Domains\Ticketing\Desfile\Requests;
|
||||
|
||||
use App\Domains\Ticketing\Desfile\Enums\EntryReservationPaymentType;
|
||||
use App\Domains\Ticketing\Ticket\Enums\EntryReservationPaymentType;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace App\Domains\Ticketing\Desfile\Requests;
|
||||
|
||||
use App\Domains\Ticketing\Desfile\Enums\EntryReservationPaymentType;
|
||||
use App\Domains\Ticketing\Ticket\Enums\EntryReservationPaymentType;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ class EntryReservationResource extends JsonResource
|
||||
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'catalog_item_id' => $this->catalog_item_id,
|
||||
'variant_id' => $this->variant_id,
|
||||
'ticket_id' => $this->ticket_id,
|
||||
'fecha_reserva' => $this->fecha_reserva->toIso8601String(),
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace App\Domains\Ticketing\Desfile\Services;
|
||||
|
||||
use App\Domains\Core\Tenant\Models\Tenant;
|
||||
use App\Domains\Ticketing\Desfile\Models\EntryReservation;
|
||||
use App\Domains\Ticketing\Ticket\Models\EntryReservation;
|
||||
use Illuminate\Support\Collection;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\DataType;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\Date;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace App\Domains\Ticketing\Desfile\Services;
|
||||
|
||||
use App\Domains\Core\Tenant\Models\Tenant;
|
||||
use App\Domains\Ticketing\Desfile\Models\EntryReservation;
|
||||
use App\Domains\Ticketing\Ticket\Models\EntryReservation;
|
||||
use Barryvdh\DomPDF\Facade\Pdf;
|
||||
use Barryvdh\DomPDF\PDF as DomPdf;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace App\Domains\Ticketing\Desfile\Services;
|
||||
|
||||
use App\Domains\Ticketing\Desfile\Models\EntryReservation;
|
||||
use App\Domains\Ticketing\Ticket\Models\EntryReservation;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class EntryReservationReportService
|
||||
|
||||
@@ -7,9 +7,10 @@ use App\Domains\Commerce\Catalog\Models\CatalogItem;
|
||||
use App\Domains\Commerce\Catalog\Models\Inventory;
|
||||
use App\Domains\Commerce\Catalog\Models\Variant;
|
||||
use App\Domains\Core\Auth\Models\User;
|
||||
use App\Domains\Ticketing\Desfile\Enums\EntryReservationPaymentType;
|
||||
use App\Domains\Ticketing\Desfile\Models\EntryReservation;
|
||||
use App\Domains\Ticketing\Ticket\Enums\EntryReservationPaymentType;
|
||||
use App\Domains\Ticketing\Ticket\Exceptions\TicketGenerationException;
|
||||
use App\Domains\Ticketing\Ticket\Models\EntryReservation;
|
||||
use App\Domains\Ticketing\Ticket\Models\ReservationBatch;
|
||||
use App\Domains\Ticketing\Ticket\Models\Ticket;
|
||||
use App\Domains\Ticketing\Ticket\Services\TicketGeneratorService;
|
||||
use App\Domains\Ticketing\Ticket\Services\TicketPresentationResolver;
|
||||
@@ -139,7 +140,7 @@ class EntryReservationService
|
||||
return DB::transaction(function () use ($user, $key, $rows, $hash): Collection {
|
||||
// Serialize retries by the same administrator, including the first insert.
|
||||
User::query()->whereKey($user->id)->lockForUpdate()->firstOrFail();
|
||||
$batch = DB::table('desfile_reservation_batches')
|
||||
$batch = ReservationBatch::query()
|
||||
->where('user_id', $user->id)->where('idempotency_key', $key)->lockForUpdate()->first();
|
||||
if ($batch !== null) {
|
||||
abort_unless($batch->tenant_code === $user->tenant_codigo && hash_equals($batch->request_hash, $hash), 409,
|
||||
@@ -157,7 +158,7 @@ class EntryReservationService
|
||||
->orderBy('id')->lockForUpdate()->get()->keyBy('id');
|
||||
$variants->load([
|
||||
'eventDates', 'eventDate',
|
||||
'desfileEntryReservations' => fn ($query) => $query->lockForUpdate(),
|
||||
'entryReservations' => fn ($query) => $query->lockForUpdate(),
|
||||
]);
|
||||
foreach ($variants as $variant) {
|
||||
$variant->setRelation('inventory', $inventories->get($variant->inventory_id));
|
||||
@@ -193,10 +194,9 @@ class EntryReservationService
|
||||
throw ValidationException::withMessages($errors);
|
||||
}
|
||||
|
||||
$batchId = DB::table('desfile_reservation_batches')->insertGetId([
|
||||
$batch = ReservationBatch::query()->create([
|
||||
'user_id' => $user->id, 'tenant_code' => $tenant->codigo,
|
||||
'idempotency_key' => $key, 'request_hash' => $hash,
|
||||
'created_at' => now(), 'updated_at' => now(),
|
||||
]);
|
||||
foreach ($requirements as $inventoryId => $quantity) {
|
||||
$inventories[$inventoryId]->reserveEntry($quantity, $tracked);
|
||||
@@ -213,7 +213,8 @@ class EntryReservationService
|
||||
]);
|
||||
}
|
||||
$reservation = EntryReservation::query()->create([
|
||||
'batch_id' => $batchId, 'ticket_id' => $ticket->id,
|
||||
'batch_id' => $batch->id, 'ticket_id' => $ticket->id,
|
||||
'catalog_item_id' => $entry->id,
|
||||
'variant_id' => $variant->id, 'inventory_id' => $variant->inventory_id,
|
||||
'fecha_reserva' => now(), 'tipo_pago' => $payment,
|
||||
'importe' => $payment === EntryReservationPaymentType::Free ? 0 : $variant->getPrice(),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Ticketing\Desfile\Enums;
|
||||
namespace App\Domains\Ticketing\Ticket\Enums;
|
||||
|
||||
enum EntryReservationPaymentType: string
|
||||
{
|
||||
87
app/Domains/Ticketing/Ticket/Models/EntryReservation.php
Normal file
87
app/Domains/Ticketing/Ticket/Models/EntryReservation.php
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Ticketing\Ticket\Models;
|
||||
|
||||
use App\Domains\Commerce\Catalog\Models\CatalogItem;
|
||||
use App\Domains\Commerce\Catalog\Models\Inventory;
|
||||
use App\Domains\Commerce\Catalog\Models\Variant;
|
||||
use App\Domains\Ticketing\Ticket\Enums\EntryReservationPaymentType;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
#[Fillable([
|
||||
'catalog_item_id',
|
||||
'variant_id',
|
||||
'ticket_id',
|
||||
'inventory_id',
|
||||
'batch_id',
|
||||
'fecha_reserva',
|
||||
'importe',
|
||||
'tipo_pago',
|
||||
])]
|
||||
class EntryReservation extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected $table = 'entry_reservations';
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'catalog_item_id' => 'integer',
|
||||
'variant_id' => 'integer',
|
||||
'fecha_reserva' => 'datetime',
|
||||
'importe' => 'decimal:2',
|
||||
'tipo_pago' => EntryReservationPaymentType::class,
|
||||
];
|
||||
}
|
||||
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::saving(function (self $reservation): void {
|
||||
if ($reservation->variant_id === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$variant = Variant::withTrashed()->findOrFail($reservation->variant_id);
|
||||
// Existing variant-based callers can keep creating reservations as before.
|
||||
$reservation->catalog_item_id ??= $variant->catalog_item_id;
|
||||
if ($reservation->catalog_item_id !== $variant->catalog_item_id) {
|
||||
throw ValidationException::withMessages([
|
||||
'variant_id' => 'La variante no pertenece al ítem del catálogo seleccionado.',
|
||||
]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** @return BelongsTo<CatalogItem, $this> */
|
||||
public function catalogItem(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(CatalogItem::class)->withTrashed();
|
||||
}
|
||||
|
||||
/** @return BelongsTo<ReservationBatch, $this> */
|
||||
public function batch(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(ReservationBatch::class, 'batch_id');
|
||||
}
|
||||
|
||||
/** @return BelongsTo<Variant, $this> */
|
||||
public function variant(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Variant::class)->withTrashed();
|
||||
}
|
||||
|
||||
public function ticket(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Ticket::class);
|
||||
}
|
||||
|
||||
public function inventory(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Inventory::class);
|
||||
}
|
||||
}
|
||||
37
app/Domains/Ticketing/Ticket/Models/ReservationBatch.php
Normal file
37
app/Domains/Ticketing/Ticket/Models/ReservationBatch.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Ticketing\Ticket\Models;
|
||||
|
||||
use App\Domains\Core\Auth\Models\User;
|
||||
use App\Domains\Core\Tenant\Models\Tenant;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
#[Fillable(['user_id', 'tenant_code', 'idempotency_key', 'request_hash'])]
|
||||
class ReservationBatch extends Model
|
||||
{
|
||||
protected function casts(): array
|
||||
{
|
||||
return ['user_id' => 'integer'];
|
||||
}
|
||||
|
||||
/** @return BelongsTo<User, $this> */
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class)->withTrashed();
|
||||
}
|
||||
|
||||
/** @return BelongsTo<Tenant, $this> */
|
||||
public function tenant(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Tenant::class, 'tenant_code', 'codigo');
|
||||
}
|
||||
|
||||
/** @return HasMany<EntryReservation, $this> */
|
||||
public function reservations(): HasMany
|
||||
{
|
||||
return $this->hasMany(EntryReservation::class, 'batch_id');
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ use App\Domains\Commerce\Catalog\Models\Variant;
|
||||
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\Desfile\Models\EntryReservation;
|
||||
use App\Domains\Ticketing\Event\Models\Event;
|
||||
use App\Domains\Ticketing\Ticket\Services\ResolvedTicketValidity;
|
||||
use App\Domains\Ticketing\Ticket\Services\ResolvedValidityGroup;
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace App\Shared\Forms\Services;
|
||||
use App\Domains\Commerce\Catalog\Models\CatalogItem;
|
||||
use App\Domains\Commerce\Catalog\Models\Variant;
|
||||
use App\Domains\Core\Tenant\Models\Tenant;
|
||||
use App\Domains\Ticketing\Desfile\Enums\EntryReservationPaymentType;
|
||||
use App\Domains\Ticketing\Ticket\Enums\EntryReservationPaymentType;
|
||||
|
||||
class DesfileEntryReservationFormService
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user