feat: Add event management to tenant and catalog

- Introduced Event and EventDate models with relationships to Tenant and CatalogItem.
- Added active_event_id to Tenant model to track the currently active event.
- Updated TenantResource to include active event details in the response.
- Enhanced Ticket model to link to CatalogItem and Variant, allowing for event-specific ticketing.
- Implemented migrations to create events and link them to catalog items and variants.
- Updated seeders to populate events and their associated dates for the Fiesta Futbol Infantil tenant.
- Modified Ticket generation logic to respect event dates over standard ticket dates.
- Added tests for event and ticket functionalities, ensuring proper relationships and date handling.
This commit is contained in:
2026-08-03 12:01:20 -03:00
parent 4a51f3afde
commit c3713c62a6
30 changed files with 736 additions and 107 deletions

View File

@@ -22,6 +22,7 @@ class TicketController extends Controller
$tickets = Ticket::query()
->where('tenant_code', $tenant->codigo)
->where('user_id', $request->user()->getKey())
->with('sourceVariant.eventDate', 'sourceVariant.catalogItem')
->orderByDesc('id')
->get();
@@ -35,6 +36,7 @@ class TicketController extends Controller
->where('tenant_code', $tenant->codigo)
->where('user_id', $request->user()->getKey())
->whereIn('id', $ticketIds)
->with('sourceVariant.eventDate', 'sourceVariant.catalogItem')
->orderByDesc('id')
->get();

View File

@@ -3,8 +3,11 @@
namespace App\Domains\Ticket\Models;
use App\Domains\Auth\Models\User;
use App\Domains\Catalog\Models\CatalogItem;
use App\Domains\Catalog\Models\Variant;
use App\Domains\Purchase\Models\Purchase;
use App\Domains\Tenant\Models\Tenant;
use Carbon\CarbonInterface;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
@@ -66,13 +69,27 @@ class Ticket extends Model
return $this->belongsTo(Purchase::class, 'source_purchase_id');
}
/** @return BelongsTo<CatalogItem, $this> */
public function sourceCatalogItem(): BelongsTo
{
return $this->belongsTo(CatalogItem::class, 'source_catalog_item_id');
}
/** @return BelongsTo<Variant, $this> */
public function sourceVariant(): BelongsTo
{
return $this->belongsTo(Variant::class, 'source_variant_id');
}
public function isValid(): bool
{
$now = now();
$startsAt = $this->getEffectiveStartsAt();
$expiresAt = $this->getEffectiveExpiresAt();
return $this->used_at === null
&& ($this->starts_at === null || $this->starts_at->lessThanOrEqualTo($now))
&& ($this->expires_at === null || $this->expires_at->greaterThan($now));
&& ($startsAt === null || $startsAt->lessThanOrEqualTo($now))
&& ($expiresAt === null || $expiresAt->greaterThan($now));
}
public function getIsValidAttribute(): bool
@@ -82,13 +99,27 @@ class Ticket extends Model
public function getIsExpiredAttribute(): bool
{
$expiresAt = $this->getEffectiveExpiresAt();
return $this->used_at === null
&& $this->expires_at !== null
&& $this->expires_at->lessThanOrEqualTo(now());
&& $expiresAt !== null
&& $expiresAt->lessThanOrEqualTo(now());
}
public function getIsUsedAttribute(): bool
{
return $this->used_at !== null;
}
public function getEffectiveStartsAt(): ?CarbonInterface
{
return $this->sourceVariant?->getMinimumUseDate()
?? $this->starts_at;
}
public function getEffectiveExpiresAt(): ?CarbonInterface
{
return $this->sourceVariant?->getMaximumUseDate()
?? $this->expires_at;
}
}

View File

@@ -20,8 +20,8 @@ class TicketResource extends JsonResource
'description' => $this->description,
'source_catalog_item_id' => $this->source_catalog_item_id,
'source_variant_id' => $this->source_variant_id,
'starts_at' => $this->starts_at,
'expires_at' => $this->expires_at,
'starts_at' => $this->getEffectiveStartsAt(),
'expires_at' => $this->getEffectiveExpiresAt(),
'used_at' => $this->used_at,
'is_valid' => $this->is_valid,
'is_expired' => $this->is_expired,

View File

@@ -35,8 +35,6 @@ class TicketGeneratorService
);
return $targets->map(function (array $target) use (
$catalogItem,
$sourceVariantId,
$sourcePurchaseId,
$user,
): Ticket {
@@ -49,8 +47,8 @@ class TicketGeneratorService
'name' => $item->nombre,
'description' => (string) ($item->descripcion ?? ''),
'source_purchase_id' => $sourcePurchaseId,
'source_catalog_item_id' => $catalogItem->getKey(),
'source_variant_id' => $sourceVariantId,
'source_catalog_item_id' => $item->getKey(),
'source_variant_id' => $target['variant']?->getKey(),
'starts_at' => $selectedItem->getMinimumUseDate(),
'expires_at' => $selectedItem->getMaximumUseDate(),
'used_at' => null,