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:
@@ -178,6 +178,7 @@ class CatalogController extends Controller
|
||||
'catalogItem.attachments',
|
||||
'catalogItem.variants.inventory',
|
||||
'catalogItem.variants.attachments',
|
||||
'catalogItem.variants.eventDate',
|
||||
'catalogItem.variants.definitions.itemAttribute.attribute',
|
||||
'catalogItem.bundleComponents.catalogItem',
|
||||
'catalogItem.bundleComponents.variant.catalogItem',
|
||||
|
||||
15
app/Domains/Catalog/Enums/EventProductType.php
Normal file
15
app/Domains/Catalog/Enums/EventProductType.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Catalog\Enums;
|
||||
|
||||
enum EventProductType: string
|
||||
{
|
||||
case Entry = 'entrada';
|
||||
case Product = 'producto';
|
||||
|
||||
/** @return list<string> */
|
||||
public static function values(): array
|
||||
{
|
||||
return array_column(self::cases(), 'value');
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,12 @@ namespace App\Domains\Catalog\Models;
|
||||
|
||||
use App\Domains\Attachable\Models\Attachment;
|
||||
use App\Domains\Catalog\Enums\CatalogItemType;
|
||||
use App\Domains\Catalog\Enums\EventProductType;
|
||||
use App\Domains\Catalog\Enums\InventoryPolicy;
|
||||
use App\Domains\Catalog\Services\CatalogInventoryService;
|
||||
use App\Domains\Event\Models\Event;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use App\Domains\Ticket\Models\Ticket;
|
||||
use Carbon\CarbonInterface;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
@@ -17,6 +20,8 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
#[Fillable([
|
||||
'tenant_code',
|
||||
'event_id',
|
||||
'event_product_type',
|
||||
'category_id',
|
||||
'brand_id',
|
||||
'inventory_id',
|
||||
@@ -50,6 +55,8 @@ class CatalogItem extends Model
|
||||
'category_id' => 'integer',
|
||||
'brand_id' => 'integer',
|
||||
'inventory_id' => 'integer',
|
||||
'event_id' => 'integer',
|
||||
'event_product_type' => EventProductType::class,
|
||||
'type' => CatalogItemType::class,
|
||||
'precio' => 'decimal:2',
|
||||
'inventory_policy' => InventoryPolicy::class,
|
||||
@@ -65,6 +72,12 @@ class CatalogItem extends Model
|
||||
return $this->belongsTo(Tenant::class, 'tenant_code', 'codigo');
|
||||
}
|
||||
|
||||
/** @return BelongsTo<Event, $this> */
|
||||
public function event(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Event::class);
|
||||
}
|
||||
|
||||
/** @return BelongsTo<Category, $this> */
|
||||
public function category(): BelongsTo
|
||||
{
|
||||
@@ -101,6 +114,12 @@ class CatalogItem extends Model
|
||||
return $this->hasMany(Variant::class);
|
||||
}
|
||||
|
||||
/** @return HasMany<Ticket, $this> */
|
||||
public function sourceTickets(): HasMany
|
||||
{
|
||||
return $this->hasMany(Ticket::class, 'source_catalog_item_id');
|
||||
}
|
||||
|
||||
/** @return BelongsToMany<Attribute, $this> */
|
||||
public function attributes(): BelongsToMany
|
||||
{
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
namespace App\Domains\Catalog\Models;
|
||||
|
||||
use App\Domains\Attachable\Models\Attachment;
|
||||
use App\Domains\Event\Models\EventDate;
|
||||
use App\Domains\Ticket\Models\Ticket;
|
||||
use Carbon\CarbonInterface;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
@@ -13,6 +15,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
#[Fillable([
|
||||
'catalog_item_id',
|
||||
'event_date_id',
|
||||
'inventory_id',
|
||||
'minimum_use_date',
|
||||
'maximum_use_date',
|
||||
@@ -29,6 +32,7 @@ class Variant extends Model
|
||||
{
|
||||
return [
|
||||
'catalog_item_id' => 'integer',
|
||||
'event_date_id' => 'integer',
|
||||
'inventory_id' => 'integer',
|
||||
'minimum_use_date' => 'datetime',
|
||||
'maximum_use_date' => 'datetime',
|
||||
@@ -41,6 +45,18 @@ class Variant extends Model
|
||||
return $this->belongsTo(CatalogItem::class);
|
||||
}
|
||||
|
||||
/** @return BelongsTo<EventDate, $this> */
|
||||
public function eventDate(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(EventDate::class);
|
||||
}
|
||||
|
||||
/** @return HasMany<Ticket, $this> */
|
||||
public function sourceTickets(): HasMany
|
||||
{
|
||||
return $this->hasMany(Ticket::class, 'source_variant_id');
|
||||
}
|
||||
|
||||
/** @return BelongsTo<Inventory, $this> */
|
||||
public function inventory(): BelongsTo
|
||||
{
|
||||
@@ -80,7 +96,7 @@ class Variant extends Model
|
||||
public function getName(): string
|
||||
{
|
||||
$name = $this->catalogItem->nombre;
|
||||
$this->loadMissing('definitions.itemAttribute.attribute');
|
||||
$this->loadMissing(['definitions.itemAttribute.attribute', 'eventDate']);
|
||||
$definitions = $this->definitions
|
||||
->map(function (VariantDefinition $definition): ?string {
|
||||
$attributeName = $definition->itemAttribute?->attribute?->nombre;
|
||||
@@ -89,21 +105,28 @@ class Variant extends Model
|
||||
? "{$attributeName}: {$definition->value}"
|
||||
: $definition->value;
|
||||
})
|
||||
->filter()
|
||||
->implode(', ');
|
||||
->filter();
|
||||
|
||||
return $definitions === '' ? $name : "{$name} ({$definitions})";
|
||||
if ($this->eventDate !== null) {
|
||||
$definitions->push('Fecha: '.$this->eventDate->date->format('Y-m-d'));
|
||||
}
|
||||
|
||||
$description = $definitions->implode(', ');
|
||||
|
||||
return $description === '' ? $name : "{$name} ({$description})";
|
||||
}
|
||||
|
||||
public function getMinimumUseDate(): ?CarbonInterface
|
||||
{
|
||||
return $this->minimum_use_date
|
||||
return $this->eventDate?->startsAt()
|
||||
?? $this->minimum_use_date
|
||||
?? $this->catalogItem->getMinimumUseDate();
|
||||
}
|
||||
|
||||
public function getMaximumUseDate(): ?CarbonInterface
|
||||
{
|
||||
return $this->maximum_use_date
|
||||
return $this->eventDate?->endsAt()
|
||||
?? $this->maximum_use_date
|
||||
?? $this->catalogItem->getMaximumUseDate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Domains\Catalog\Requests;
|
||||
|
||||
use App\Domains\Catalog\Enums\CatalogItemType;
|
||||
use App\Domains\Catalog\Enums\EventProductType;
|
||||
use App\Domains\Catalog\Enums\InventoryPolicy;
|
||||
use App\Domains\Shared\Rules\ImageOrBase64Rule;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
@@ -25,6 +26,20 @@ class StoreCatalogItemRequest extends FormRequest
|
||||
return [
|
||||
'tenant_code' => ['prohibited'],
|
||||
'type' => ['sometimes', Rule::enum(CatalogItemType::class)],
|
||||
'event_id' => [
|
||||
'sometimes',
|
||||
'nullable',
|
||||
'required_with:event_product_type',
|
||||
Rule::exists('events', 'id')->where(
|
||||
fn ($query) => $query->where('tenant_code', $tenantCode)
|
||||
),
|
||||
],
|
||||
'event_product_type' => [
|
||||
'sometimes',
|
||||
'nullable',
|
||||
'required_with:event_id',
|
||||
Rule::enum(EventProductType::class),
|
||||
],
|
||||
'category_id' => [
|
||||
'sometimes',
|
||||
'nullable',
|
||||
@@ -73,6 +88,14 @@ class StoreCatalogItemRequest extends FormRequest
|
||||
'images.*' => ['required', new ImageOrBase64Rule],
|
||||
'variants' => [Rule::prohibitedIf($isBundle), 'sometimes', 'array'],
|
||||
'variants.*.real_stock' => ['sometimes', 'integer', 'min:0'],
|
||||
'variants.*.event_date_id' => [
|
||||
'sometimes',
|
||||
'nullable',
|
||||
'integer',
|
||||
Rule::exists('event_dates', 'id')->where(
|
||||
fn ($query) => $query->where('event_id', $this->input('event_id'))
|
||||
),
|
||||
],
|
||||
'variants.*.inventory_id' => ['prohibited'],
|
||||
'variants.*.reserved_stock' => ['prohibited'],
|
||||
'variants.*.sold_units' => ['prohibited'],
|
||||
|
||||
@@ -32,6 +32,8 @@ class CatalogFeaturedItemResource extends JsonResource
|
||||
'variants' => $catalogItem->variants
|
||||
->map(fn (Variant $variant): array => [
|
||||
'id' => $variant->id,
|
||||
'event_date_id' => $variant->event_date_id,
|
||||
'event_date' => $variant->eventDate?->date?->format('Y-m-d'),
|
||||
'stock_tecnico' => $catalogItem->inventory_policy === InventoryPolicy::Unlimited
|
||||
? null
|
||||
: $variant->inventory->availableStock(),
|
||||
|
||||
@@ -22,6 +22,8 @@ class CatalogItemDetailResource extends JsonResource
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'type' => $this->type->value,
|
||||
'event_id' => $this->event_id,
|
||||
'event_product_type' => $this->event_product_type?->value,
|
||||
'category_id' => $this->category_id,
|
||||
'brand_id' => $this->brand_id,
|
||||
'slug' => $this->slug,
|
||||
@@ -110,12 +112,16 @@ class CatalogItemDetailResource extends JsonResource
|
||||
{
|
||||
return [
|
||||
'id' => $variant->id,
|
||||
'event_date_id' => $variant->event_date_id,
|
||||
'event_date' => $variant->eventDate?->date?->format('Y-m-d'),
|
||||
'stock_tecnico' => $this->variantStock($variant),
|
||||
'minimum_use_date' => $variant->minimum_use_date,
|
||||
'maximum_use_date' => $variant->maximum_use_date,
|
||||
'effective_minimum_use_date' => $variant->minimum_use_date
|
||||
'effective_minimum_use_date' => $variant->eventDate?->startsAt()
|
||||
?? $variant->minimum_use_date
|
||||
?? $this->minimum_use_date,
|
||||
'effective_maximum_use_date' => $variant->maximum_use_date
|
||||
'effective_maximum_use_date' => $variant->eventDate?->endsAt()
|
||||
?? $variant->maximum_use_date
|
||||
?? $this->maximum_use_date,
|
||||
'values' => $variant->definitions
|
||||
->mapWithKeys(fn ($definition) => [
|
||||
|
||||
@@ -15,6 +15,8 @@ class CatalogItemResource extends JsonResource
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'type' => $this->type->value,
|
||||
'event_id' => $this->event_id,
|
||||
'event_product_type' => $this->event_product_type?->value,
|
||||
'category_id' => $this->category_id,
|
||||
'brand_id' => $this->brand_id,
|
||||
'slug' => $this->slug,
|
||||
@@ -32,12 +34,16 @@ class CatalogItemResource extends JsonResource
|
||||
'variants' => $this->whenLoaded('variants', fn () => $this->variants
|
||||
->map(fn ($variant) => [
|
||||
'id' => $variant->id,
|
||||
'event_date_id' => $variant->event_date_id,
|
||||
'event_date' => $variant->eventDate?->date?->format('Y-m-d'),
|
||||
'real_stock' => $variant->inventory?->real_stock,
|
||||
'minimum_use_date' => $variant->minimum_use_date,
|
||||
'maximum_use_date' => $variant->maximum_use_date,
|
||||
'effective_minimum_use_date' => $variant->minimum_use_date
|
||||
'effective_minimum_use_date' => $variant->eventDate?->startsAt()
|
||||
?? $variant->minimum_use_date
|
||||
?? $this->minimum_use_date,
|
||||
'effective_maximum_use_date' => $variant->maximum_use_date
|
||||
'effective_maximum_use_date' => $variant->eventDate?->endsAt()
|
||||
?? $variant->maximum_use_date
|
||||
?? $this->maximum_use_date,
|
||||
'values' => $variant->definitions
|
||||
->mapWithKeys(fn ($definition) => [
|
||||
|
||||
@@ -30,6 +30,8 @@ class CatalogSearchItemResource extends JsonResource
|
||||
'variants' => $this->variants
|
||||
->map(fn (Variant $variant): array => [
|
||||
'id' => $variant->id,
|
||||
'event_date_id' => $variant->event_date_id,
|
||||
'event_date' => $variant->eventDate?->date?->format('Y-m-d'),
|
||||
'stock_tecnico' => $this->inventory_policy === InventoryPolicy::Unlimited
|
||||
? null
|
||||
: $variant->inventory?->availableStock(),
|
||||
|
||||
@@ -5,12 +5,15 @@ namespace App\Domains\Catalog\Services;
|
||||
use App\Domains\Attachable\Models\Attachment;
|
||||
use App\Domains\Attachable\Services\AttachmentService;
|
||||
use App\Domains\Catalog\Enums\CatalogItemType;
|
||||
use App\Domains\Catalog\Enums\EventProductType;
|
||||
use App\Domains\Catalog\Models\Attribute;
|
||||
use App\Domains\Catalog\Models\CatalogItem;
|
||||
use App\Domains\Catalog\Models\Category;
|
||||
use App\Domains\Catalog\Models\Inventory;
|
||||
use App\Domains\Catalog\Models\ItemAttribute;
|
||||
use App\Domains\Catalog\Models\Variant;
|
||||
use App\Domains\Event\Models\Event;
|
||||
use App\Domains\Event\Models\EventDate;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
@@ -39,7 +42,12 @@ class CatalogService
|
||||
$hasDirectStock = array_key_exists('real_stock', $data);
|
||||
$realStock = (int) ($data['real_stock'] ?? 0);
|
||||
|
||||
$hasVariants = $attributeCodes !== [];
|
||||
$hasEventDateVariants = $variants !== [] && collect($variants)->every(
|
||||
fn (array $variant): bool => ! empty($variant['event_date_id'])
|
||||
);
|
||||
$hasVariants = $attributeCodes !== [] || $hasEventDateVariants;
|
||||
|
||||
$this->validateEventData($data);
|
||||
|
||||
if ($type === CatalogItemType::Bundle) {
|
||||
$this->validateBundleData($data, $components);
|
||||
@@ -121,9 +129,11 @@ class CatalogService
|
||||
'inventory',
|
||||
'category',
|
||||
'brand',
|
||||
'event',
|
||||
'itemAttributes.attribute',
|
||||
'variants.inventory',
|
||||
'variants.attachments',
|
||||
'variants.eventDate',
|
||||
'variants.definitions.itemAttribute.attribute',
|
||||
'bundleComponents.catalogItem',
|
||||
'bundleComponents.variant.catalogItem',
|
||||
@@ -138,10 +148,12 @@ class CatalogService
|
||||
'inventory',
|
||||
'category',
|
||||
'brand',
|
||||
'event',
|
||||
'itemAttributes.attribute.options',
|
||||
'variants' => fn ($query) => $query->orderBy('id'),
|
||||
'variants.inventory',
|
||||
'variants.attachments',
|
||||
'variants.eventDate',
|
||||
'variants.definitions' => fn ($query) => $query->orderBy('id'),
|
||||
'variants.definitions.itemAttribute.attribute',
|
||||
'bundleComponents.catalogItem.inventory',
|
||||
@@ -195,6 +207,7 @@ class CatalogService
|
||||
'inventory',
|
||||
'variants.inventory',
|
||||
'variants.attachments',
|
||||
'variants.eventDate',
|
||||
'variants.definitions.itemAttribute.attribute',
|
||||
'bundleComponents.catalogItem',
|
||||
'bundleComponents.variant.catalogItem',
|
||||
@@ -226,6 +239,7 @@ class CatalogService
|
||||
'inventory',
|
||||
'variants.inventory',
|
||||
'variants.attachments',
|
||||
'variants.eventDate',
|
||||
'variants.definitions.itemAttribute.attribute',
|
||||
'bundleComponents.catalogItem',
|
||||
'bundleComponents.variant.catalogItem',
|
||||
@@ -472,8 +486,28 @@ class CatalogService
|
||||
}
|
||||
|
||||
$inventory = $this->createInventory((int) ($data['real_stock'] ?? 0));
|
||||
$eventDateId = $data['event_date_id'] ?? null;
|
||||
|
||||
if (
|
||||
$eventDateId !== null
|
||||
&& (
|
||||
$catalogItem->event_id === null
|
||||
|| ! EventDate::query()
|
||||
->whereKey($eventDateId)
|
||||
->where('event_id', $catalogItem->event_id)
|
||||
->exists()
|
||||
)
|
||||
) {
|
||||
throw ValidationException::withMessages([
|
||||
"variants.{$index}.event_date_id" => [
|
||||
'The event date must belong to the catalog item event.',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
$variant = $catalogItem->variants()->create([
|
||||
'inventory_id' => $inventory->id,
|
||||
'event_date_id' => $eventDateId,
|
||||
'minimum_use_date' => $data['minimum_use_date'] ?? null,
|
||||
'maximum_use_date' => $data['maximum_use_date'] ?? null,
|
||||
]);
|
||||
@@ -501,6 +535,38 @@ class CatalogService
|
||||
return $variant;
|
||||
}
|
||||
|
||||
/** @param array<string, mixed> $data */
|
||||
private function validateEventData(array $data): void
|
||||
{
|
||||
$eventId = $data['event_id'] ?? null;
|
||||
$eventProductType = $data['event_product_type'] ?? null;
|
||||
|
||||
if (($eventId === null) !== ($eventProductType === null)) {
|
||||
throw ValidationException::withMessages([
|
||||
'event_id' => ['Event and event product type must be provided together.'],
|
||||
]);
|
||||
}
|
||||
|
||||
if ($eventId === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (! Event::query()
|
||||
->whereKey($eventId)
|
||||
->where('tenant_code', $data['tenant_code'] ?? null)
|
||||
->exists()) {
|
||||
throw ValidationException::withMessages([
|
||||
'event_id' => ['The event must belong to the catalog item tenant.'],
|
||||
]);
|
||||
}
|
||||
|
||||
if (! in_array($eventProductType, EventProductType::values(), true)) {
|
||||
throw ValidationException::withMessages([
|
||||
'event_product_type' => ['The event product type is invalid.'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private function validateVariantUseDates(Variant $variant, int $index): void
|
||||
{
|
||||
$minimumUseDate = $variant->getMinimumUseDate();
|
||||
|
||||
41
app/Domains/Event/Models/Event.php
Normal file
41
app/Domains/Event/Models/Event.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Event\Models;
|
||||
|
||||
use App\Domains\Catalog\Models\CatalogItem;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
#[Fillable([
|
||||
'tenant_code',
|
||||
'name',
|
||||
'address',
|
||||
])]
|
||||
class Event extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
public $timestamps = false;
|
||||
|
||||
/** @return BelongsTo<Tenant, $this> */
|
||||
public function tenant(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Tenant::class, 'tenant_code', 'codigo');
|
||||
}
|
||||
|
||||
/** @return HasMany<EventDate, $this> */
|
||||
public function dates(): HasMany
|
||||
{
|
||||
return $this->hasMany(EventDate::class)->orderBy('date')->orderBy('time_start');
|
||||
}
|
||||
|
||||
/** @return HasMany<CatalogItem, $this> */
|
||||
public function catalogItems(): HasMany
|
||||
{
|
||||
return $this->hasMany(CatalogItem::class);
|
||||
}
|
||||
}
|
||||
55
app/Domains/Event/Models/EventDate.php
Normal file
55
app/Domains/Event/Models/EventDate.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Event\Models;
|
||||
|
||||
use App\Domains\Catalog\Models\Variant;
|
||||
use Carbon\CarbonInterface;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
#[Fillable([
|
||||
'event_id',
|
||||
'date',
|
||||
'time_start',
|
||||
'time_end',
|
||||
])]
|
||||
class EventDate extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
public $timestamps = false;
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'event_id' => 'integer',
|
||||
'date' => 'date:Y-m-d',
|
||||
];
|
||||
}
|
||||
|
||||
/** @return BelongsTo<Event, $this> */
|
||||
public function event(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Event::class);
|
||||
}
|
||||
|
||||
/** @return HasMany<Variant, $this> */
|
||||
public function variants(): HasMany
|
||||
{
|
||||
return $this->hasMany(Variant::class);
|
||||
}
|
||||
|
||||
public function startsAt(): CarbonInterface
|
||||
{
|
||||
return Carbon::parse($this->date->format('Y-m-d').' '.$this->time_start);
|
||||
}
|
||||
|
||||
public function endsAt(): CarbonInterface
|
||||
{
|
||||
return Carbon::parse($this->date->format('Y-m-d').' '.$this->time_end);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ use App\Domains\Catalog\Enums\GroupLayout;
|
||||
use App\Domains\Catalog\Enums\ProductLayout;
|
||||
use App\Domains\Catalog\Models\CatalogItem;
|
||||
use App\Domains\Catalog\Models\Category;
|
||||
use App\Domains\Event\Models\Event;
|
||||
use App\Domains\Menu\Models\Menu;
|
||||
use App\Domains\Menu\Models\TenantMenu;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
@@ -32,6 +33,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
'search_product_layout',
|
||||
'search_group_layout',
|
||||
'search_items_per_page',
|
||||
'active_event_id',
|
||||
])]
|
||||
class Tenant extends Model
|
||||
{
|
||||
@@ -59,6 +61,7 @@ class Tenant extends Model
|
||||
'search_product_layout' => ProductLayout::class,
|
||||
'search_group_layout' => GroupLayout::class,
|
||||
'search_items_per_page' => 'integer',
|
||||
'active_event_id' => 'integer',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -91,6 +94,18 @@ class Tenant extends Model
|
||||
return $this->hasMany(CatalogItem::class, 'tenant_code', 'codigo');
|
||||
}
|
||||
|
||||
/** @return HasMany<Event, $this> */
|
||||
public function events(): HasMany
|
||||
{
|
||||
return $this->hasMany(Event::class, 'tenant_code', 'codigo');
|
||||
}
|
||||
|
||||
/** @return BelongsTo<Event, $this> */
|
||||
public function activeEvent(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Event::class, 'active_event_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasMany<Category, $this>
|
||||
*/
|
||||
|
||||
@@ -32,6 +32,20 @@ class TenantResource extends JsonResource
|
||||
'header_bg_color' => $this->header_bg_color,
|
||||
'footer_bg_color' => $this->footer_bg_color,
|
||||
'website_type_code' => $this->website_type_code,
|
||||
'active_event_id' => $this->active_event_id,
|
||||
'active_event' => $this->whenLoaded('activeEvent', fn () => $this->activeEvent === null
|
||||
? null
|
||||
: [
|
||||
'id' => $this->activeEvent->id,
|
||||
'name' => $this->activeEvent->name,
|
||||
'address' => $this->activeEvent->address,
|
||||
'dates' => $this->activeEvent->dates->map(fn ($eventDate): array => [
|
||||
'id' => $eventDate->id,
|
||||
'date' => $eventDate->date->format('Y-m-d'),
|
||||
'time_start' => $eventDate->time_start,
|
||||
'time_end' => $eventDate->time_end,
|
||||
])->values(),
|
||||
]),
|
||||
'extras' => $this->whenLoaded(
|
||||
'websiteExtras',
|
||||
fn () => $this->websiteExtras
|
||||
|
||||
@@ -15,6 +15,7 @@ class TenantInformationService
|
||||
'footerLogo',
|
||||
'socialMedia',
|
||||
'websiteExtras.websiteTypeExtra',
|
||||
'activeEvent.dates',
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user