feat: add support for multiple event dates in variants
- Updated the FeaturedGroupService to include 'variants.eventDates' in the items query. - Introduced a BelongsToMany relationship in EventDate for selected variants. - Modified PurchaseItemResource and PurchaseItemSnapshotFactory to handle multiple event dates for variants. - Enhanced StartCheckoutService to load event dates for variants. - Updated TicketGeneratorService to accommodate event dates in ticket generation. - Created migrations to support multi-value event dates and allow multiple variant values per attribute. - Adjusted seeders to reflect new event date handling and added new attributes. - Added tests to ensure correct functionality for multi-date variants and their integration with ticket generation.
This commit is contained in:
@@ -104,6 +104,8 @@ class CartService
|
||||
'items.variant.attachments',
|
||||
'items.variant.inventory',
|
||||
'items.variant.definitions.itemAttribute.attribute',
|
||||
'items.variant.eventDates',
|
||||
'items.variant.eventDate',
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
#[Fillable([
|
||||
'catalog_item_id',
|
||||
'attribute_id',
|
||||
'allow_multi_select',
|
||||
])]
|
||||
class ItemAttribute extends Model
|
||||
{
|
||||
@@ -18,6 +19,15 @@ class ItemAttribute extends Model
|
||||
|
||||
protected $table = 'item_attributes';
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'catalog_item_id' => 'integer',
|
||||
'attribute_id' => 'integer',
|
||||
'allow_multi_select' => 'boolean',
|
||||
];
|
||||
}
|
||||
|
||||
/** @return BelongsTo<CatalogItem, $this> */
|
||||
public function catalogItem(): BelongsTo
|
||||
{
|
||||
|
||||
@@ -11,6 +11,7 @@ use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
#[Fillable([
|
||||
'catalog_item_id',
|
||||
@@ -46,6 +47,17 @@ class Variant extends Model
|
||||
return $this->belongsTo(EventDate::class);
|
||||
}
|
||||
|
||||
/** @return BelongsToMany<EventDate, $this> */
|
||||
public function eventDates(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(
|
||||
EventDate::class,
|
||||
'variant_event_dates',
|
||||
'variant_id',
|
||||
'event_date_id',
|
||||
)->orderBy('date')->orderBy('time_start');
|
||||
}
|
||||
|
||||
/** @return HasMany<Ticket, $this> */
|
||||
public function sourceTickets(): HasMany
|
||||
{
|
||||
@@ -92,4 +104,39 @@ class Variant extends Model
|
||||
{
|
||||
return $this->catalogItem->nombre;
|
||||
}
|
||||
|
||||
/** @return Collection<string, string|array<int, string>> */
|
||||
public function selectionValues(): Collection
|
||||
{
|
||||
$values = $this->definitions
|
||||
->mapWithKeys(fn ($definition) => [
|
||||
$definition->itemAttribute?->attribute?->codigo => $definition->value,
|
||||
])
|
||||
->filter(fn ($value, $key): bool => $key !== null);
|
||||
|
||||
$eventDateIds = $this->selectedEventDates()
|
||||
->pluck('id')
|
||||
->map(fn ($id): string => (string) $id)
|
||||
->values();
|
||||
|
||||
if ($eventDateIds->count() === 1) {
|
||||
$values->put('event_date', $eventDateIds->first());
|
||||
} elseif ($eventDateIds->isNotEmpty()) {
|
||||
$values->put('event_date', $eventDateIds->all());
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/** @return Collection<int, EventDate> */
|
||||
public function selectedEventDates(): Collection
|
||||
{
|
||||
$eventDates = $this->eventDates;
|
||||
|
||||
if ($eventDates->isEmpty() && $this->event_date_id !== null && $this->eventDate !== null) {
|
||||
return collect([$this->eventDate]);
|
||||
}
|
||||
|
||||
return $eventDates;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,15 @@ class StoreCatalogItemRequest extends FormRequest
|
||||
fn ($query) => $query->where('tenant_codigo', $tenantCode)
|
||||
),
|
||||
],
|
||||
'multi_select_attribute_codes' => [Rule::prohibitedIf($isBundle), 'sometimes', 'array'],
|
||||
'multi_select_attribute_codes.*' => [
|
||||
'required',
|
||||
'string',
|
||||
'distinct',
|
||||
Rule::exists('attribute', 'codigo')->where(
|
||||
fn ($query) => $query->where('tenant_codigo', $tenantCode)
|
||||
),
|
||||
],
|
||||
'images' => ['sometimes', 'array'],
|
||||
'images.*' => ['required', new ImageOrBase64Rule],
|
||||
'variants' => [Rule::prohibitedIf($isBundle), 'sometimes', 'array'],
|
||||
@@ -87,6 +96,15 @@ class StoreCatalogItemRequest extends FormRequest
|
||||
fn ($query) => $query->where('tenant_code', $tenantCode)
|
||||
),
|
||||
],
|
||||
'variants.*.event_date_ids' => ['sometimes', 'array', 'min:1'],
|
||||
'variants.*.event_date_ids.*' => [
|
||||
'required',
|
||||
'integer',
|
||||
'distinct',
|
||||
Rule::exists('event_dates', 'id')->where(
|
||||
fn ($query) => $query->where('tenant_code', $tenantCode)
|
||||
),
|
||||
],
|
||||
'variants.*.inventory_id' => ['prohibited'],
|
||||
'variants.*.reserved_stock' => ['prohibited'],
|
||||
'variants.*.sold_units' => ['prohibited'],
|
||||
|
||||
@@ -39,14 +39,12 @@ class CatalogFeaturedItemResource extends JsonResource
|
||||
'id' => $variant->id,
|
||||
'event_date_id' => $variant->event_date_id,
|
||||
'event_date' => $variant->eventDate?->date?->format('Y-m-d'),
|
||||
'event_date_ids' => $variant->selectedEventDates()->pluck('id')->values(),
|
||||
'event_dates' => $variant->selectedEventDates()->map(fn ($eventDate): string => $eventDate->date->format('Y-m-d'))->values(),
|
||||
'stock_tecnico' => $catalogItem->inventory_policy === InventoryPolicy::Unlimited
|
||||
? null
|
||||
: $variant->inventory->availableStock(),
|
||||
'values' => $variant->definitions
|
||||
->mapWithKeys(fn ($definition) => [
|
||||
$definition->itemAttribute?->attribute?->codigo => $definition->value,
|
||||
])
|
||||
->filter(fn ($value, $key): bool => $key !== null),
|
||||
'values' => $variant->selectionValues(),
|
||||
])
|
||||
->values(),
|
||||
];
|
||||
|
||||
@@ -88,6 +88,7 @@ class CatalogItemDetailResource extends JsonResource
|
||||
'codigo' => $attribute->codigo,
|
||||
'nombre' => $attribute->nombre,
|
||||
'is_required' => $attribute->is_required,
|
||||
'allow_multi_select' => $itemAttribute->allow_multi_select,
|
||||
'metadata_schema' => $attribute->metadata_schema,
|
||||
'type' => $attribute->type->value,
|
||||
'options' => $attribute->type === FieldType::EventDate
|
||||
@@ -155,20 +156,15 @@ class CatalogItemDetailResource extends JsonResource
|
||||
/** @return array<string, mixed> */
|
||||
private function variantData(Variant $variant): array
|
||||
{
|
||||
$values = $variant->definitions
|
||||
->mapWithKeys(fn ($definition) => [
|
||||
$definition->itemAttribute?->attribute?->codigo => $definition->value,
|
||||
])
|
||||
->filter(fn ($value, $key): bool => $key !== null);
|
||||
|
||||
if ($variant->event_date_id !== null) {
|
||||
$values->put('event_date', (string) $variant->event_date_id);
|
||||
}
|
||||
$values = $variant->selectionValues();
|
||||
$eventDates = $variant->selectedEventDates();
|
||||
|
||||
return [
|
||||
'id' => $variant->id,
|
||||
'event_date_id' => $variant->event_date_id,
|
||||
'event_date' => $variant->eventDate?->date?->format('Y-m-d'),
|
||||
'event_date_ids' => $eventDates->pluck('id')->values(),
|
||||
'event_dates' => $eventDates->map(fn ($eventDate): string => $eventDate->date->format('Y-m-d'))->values(),
|
||||
'stock_tecnico' => $this->variantStock($variant),
|
||||
'values' => $values,
|
||||
];
|
||||
|
||||
@@ -40,12 +40,10 @@ class CatalogItemResource extends JsonResource
|
||||
'id' => $variant->id,
|
||||
'event_date_id' => $variant->event_date_id,
|
||||
'event_date' => $variant->eventDate?->date?->format('Y-m-d'),
|
||||
'event_date_ids' => $variant->selectedEventDates()->pluck('id')->values(),
|
||||
'event_dates' => $variant->selectedEventDates()->map(fn ($eventDate): string => $eventDate->date->format('Y-m-d'))->values(),
|
||||
'real_stock' => $variant->inventory?->real_stock,
|
||||
'values' => $variant->definitions
|
||||
->mapWithKeys(fn ($definition) => [
|
||||
$definition->itemAttribute?->attribute?->codigo => $definition->value,
|
||||
])
|
||||
->filter(fn ($value, $key) => $key !== null),
|
||||
'values' => $variant->selectionValues(),
|
||||
'images' => $variant->attachments
|
||||
->map(fn ($attachment) => $attachment->getTemporaryUrl(1440))
|
||||
->values(),
|
||||
|
||||
@@ -35,14 +35,12 @@ class CatalogSearchItemResource extends JsonResource
|
||||
'id' => $variant->id,
|
||||
'event_date_id' => $variant->event_date_id,
|
||||
'event_date' => $variant->eventDate?->date?->format('Y-m-d'),
|
||||
'event_date_ids' => $variant->selectedEventDates()->pluck('id')->values(),
|
||||
'event_dates' => $variant->selectedEventDates()->map(fn ($eventDate): string => $eventDate->date->format('Y-m-d'))->values(),
|
||||
'stock_tecnico' => $this->inventory_policy === InventoryPolicy::Unlimited
|
||||
? null
|
||||
: $variant->inventory?->availableStock(),
|
||||
'values' => $variant->definitions
|
||||
->mapWithKeys(fn ($definition) => [
|
||||
$definition->itemAttribute?->attribute?->codigo => $definition->value,
|
||||
])
|
||||
->filter(fn ($value, $key): bool => $key !== null),
|
||||
'values' => $variant->selectionValues(),
|
||||
])
|
||||
->values(),
|
||||
];
|
||||
|
||||
@@ -37,12 +37,14 @@ class CatalogService
|
||||
$variants = $data['variants'] ?? [];
|
||||
$images = $data['images'] ?? [];
|
||||
$attributeCodes = $data['attribute_codes'] ?? [];
|
||||
$multiSelectAttributeCodes = $data['multi_select_attribute_codes'] ?? [];
|
||||
$components = $data['components'] ?? [];
|
||||
$hasDirectStock = array_key_exists('real_stock', $data);
|
||||
$realStock = (int) ($data['real_stock'] ?? 0);
|
||||
|
||||
$hasEventDateVariants = $variants !== [] && collect($variants)->every(
|
||||
fn (array $variant): bool => ! empty($variant['event_date_id'])
|
||||
|| ! empty($variant['event_date_ids'])
|
||||
);
|
||||
|
||||
if ($hasEventDateVariants && ! in_array('event_date', $attributeCodes, true)) {
|
||||
@@ -55,6 +57,14 @@ class CatalogService
|
||||
|
||||
$hasVariants = $attributeCodes !== [] || $hasEventDateVariants;
|
||||
|
||||
if (array_diff($multiSelectAttributeCodes, $attributeCodes) !== []) {
|
||||
throw ValidationException::withMessages([
|
||||
'multi_select_attribute_codes' => [
|
||||
'Multi-select attributes must also be present in attribute_codes.',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
$this->validateEventProductType($data);
|
||||
$this->validateUniqueVariantCombinations($variants, $attributeCodes);
|
||||
|
||||
@@ -79,6 +89,7 @@ class CatalogService
|
||||
$data['variants'],
|
||||
$data['images'],
|
||||
$data['attribute_codes'],
|
||||
$data['multi_select_attribute_codes'],
|
||||
$data['components'],
|
||||
$data['real_stock'],
|
||||
$data['reserved_stock'],
|
||||
@@ -100,7 +111,7 @@ class CatalogService
|
||||
|
||||
$catalogItem = CatalogItem::query()->create($data);
|
||||
$itemAttributes = $type === CatalogItemType::Standard
|
||||
? $this->createItemAttributes($catalogItem, $attributeCodes)
|
||||
? $this->createItemAttributes($catalogItem, $attributeCodes, $multiSelectAttributeCodes)
|
||||
: [];
|
||||
|
||||
if ($type === CatalogItemType::Bundle) {
|
||||
@@ -141,6 +152,7 @@ class CatalogService
|
||||
'variants.inventory',
|
||||
'variants.attachments',
|
||||
'variants.eventDate',
|
||||
'variants.eventDates',
|
||||
'variants.definitions.itemAttribute.attribute',
|
||||
'bundleComponents.catalogItem',
|
||||
'bundleComponents.variant.catalogItem',
|
||||
@@ -162,6 +174,7 @@ class CatalogService
|
||||
'variants.inventory',
|
||||
'variants.attachments',
|
||||
'variants.eventDate',
|
||||
'variants.eventDates',
|
||||
'variants.definitions' => fn ($query) => $query->orderBy('id'),
|
||||
'variants.definitions.itemAttribute.attribute',
|
||||
'bundleComponents.catalogItem.inventory',
|
||||
@@ -217,6 +230,7 @@ class CatalogService
|
||||
'variants.inventory',
|
||||
'variants.attachments',
|
||||
'variants.eventDate',
|
||||
'variants.eventDates',
|
||||
'variants.definitions.itemAttribute.attribute',
|
||||
'bundleComponents.catalogItem',
|
||||
'bundleComponents.variant.catalogItem',
|
||||
@@ -250,6 +264,7 @@ class CatalogService
|
||||
'variants.inventory',
|
||||
'variants.attachments',
|
||||
'variants.eventDate',
|
||||
'variants.eventDates',
|
||||
'variants.definitions.itemAttribute.attribute',
|
||||
'bundleComponents.catalogItem',
|
||||
'bundleComponents.variant.catalogItem',
|
||||
@@ -435,11 +450,13 @@ class CatalogService
|
||||
|
||||
/**
|
||||
* @param array<int, string> $attributeCodes
|
||||
* @param array<int, string> $multiSelectAttributeCodes
|
||||
* @return array<string, ItemAttribute>
|
||||
*/
|
||||
private function createItemAttributes(
|
||||
CatalogItem $catalogItem,
|
||||
array $attributeCodes,
|
||||
array $multiSelectAttributeCodes = [],
|
||||
): array {
|
||||
$itemAttributes = [];
|
||||
$attributeCodes = array_values(array_unique($attributeCodes));
|
||||
@@ -462,6 +479,7 @@ class CatalogService
|
||||
|
||||
$itemAttribute = $catalogItem->itemAttributes()->create([
|
||||
'attribute_id' => $attribute->id,
|
||||
'allow_multi_select' => in_array($attributeCode, $multiSelectAttributeCodes, true),
|
||||
]);
|
||||
|
||||
$itemAttributes[$attributeCode] = $itemAttribute;
|
||||
@@ -495,28 +513,49 @@ class CatalogService
|
||||
}
|
||||
|
||||
$inventory = $this->createInventory((int) ($data['real_stock'] ?? 0));
|
||||
$eventDateId = $data['event_date_id'] ?? null;
|
||||
|
||||
if (
|
||||
$eventDateId !== null
|
||||
&& (
|
||||
! EventDate::query()
|
||||
->whereKey($eventDateId)
|
||||
->where('tenant_code', $catalogItem->tenant_code)
|
||||
->exists()
|
||||
$eventDateIds = collect($data['event_date_ids'] ?? [])
|
||||
->when(
|
||||
isset($data['event_date_id']),
|
||||
fn ($ids) => $ids->push($data['event_date_id']),
|
||||
)
|
||||
) {
|
||||
->filter(fn ($id): bool => $id !== null)
|
||||
->map(fn ($id): int => (int) $id)
|
||||
->unique()
|
||||
->sort()
|
||||
->values();
|
||||
|
||||
$eventDateItemAttribute = $itemAttributes['event_date'] ?? null;
|
||||
if ($eventDateItemAttribute !== null && (
|
||||
$eventDateIds->isEmpty()
|
||||
|| (! $eventDateItemAttribute->allow_multi_select && $eventDateIds->count() !== 1)
|
||||
)) {
|
||||
throw ValidationException::withMessages([
|
||||
"variants.{$index}.event_date_id" => [
|
||||
'The event date must belong to the catalog item tenant.',
|
||||
"variants.{$index}.event_date_ids" => [
|
||||
$eventDateItemAttribute->allow_multi_select
|
||||
? 'At least one event date must be selected.'
|
||||
: 'Exactly one event date must be selected.',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
$validEventDateCount = EventDate::query()
|
||||
->whereKey($eventDateIds)
|
||||
->where('tenant_code', $catalogItem->tenant_code)
|
||||
->count();
|
||||
|
||||
if ($validEventDateCount !== $eventDateIds->count()) {
|
||||
throw ValidationException::withMessages([
|
||||
"variants.{$index}.event_date_ids" => [
|
||||
'Every event date must belong to the catalog item tenant.',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
$variant = $catalogItem->variants()->create([
|
||||
'inventory_id' => $inventory->id,
|
||||
'event_date_id' => $eventDateId,
|
||||
'event_date_id' => $eventDateIds->count() === 1 ? $eventDateIds->first() : null,
|
||||
]);
|
||||
$variant->eventDates()->sync($eventDateIds->all());
|
||||
$variant->setRelation('catalogItem', $catalogItem);
|
||||
|
||||
foreach ($data['values'] ?? [] as $attributeCode => $value) {
|
||||
@@ -566,7 +605,17 @@ class CatalogService
|
||||
sort($attributeCodes);
|
||||
|
||||
foreach (array_values($variants) as $index => $variant) {
|
||||
$combination = [(string) ($variant['event_date_id'] ?? '')];
|
||||
$eventDateIds = collect($variant['event_date_ids'] ?? [])
|
||||
->when(
|
||||
isset($variant['event_date_id']),
|
||||
fn ($ids) => $ids->push($variant['event_date_id']),
|
||||
)
|
||||
->map(fn ($id): int => (int) $id)
|
||||
->unique()
|
||||
->sort()
|
||||
->values()
|
||||
->implode(',');
|
||||
$combination = [$eventDateIds];
|
||||
|
||||
foreach ($attributeCodes as $attributeCode) {
|
||||
$value = trim((string) ($variant['values'][$attributeCode] ?? ''));
|
||||
|
||||
@@ -44,6 +44,7 @@ class FeaturedGroupService
|
||||
'variants.inventory',
|
||||
'variants.attachments',
|
||||
'variants.eventDate',
|
||||
'variants.eventDates',
|
||||
'variants.definitions.itemAttribute.attribute',
|
||||
'bundleComponents.catalogItem',
|
||||
'bundleComponents.variant.catalogItem',
|
||||
|
||||
@@ -9,6 +9,7 @@ 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\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
@@ -43,6 +44,17 @@ class EventDate extends Model
|
||||
return $this->hasMany(Variant::class);
|
||||
}
|
||||
|
||||
/** @return BelongsToMany<Variant, $this> */
|
||||
public function selectedByVariants(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(
|
||||
Variant::class,
|
||||
'variant_event_dates',
|
||||
'event_date_id',
|
||||
'variant_id',
|
||||
);
|
||||
}
|
||||
|
||||
public function startsAt(): CarbonInterface
|
||||
{
|
||||
return Carbon::parse($this->date->format('Y-m-d').' '.$this->time_start);
|
||||
|
||||
@@ -98,14 +98,28 @@ class PurchaseItemResource extends JsonResource
|
||||
return [];
|
||||
}
|
||||
|
||||
return $variant->definitions
|
||||
$attributes = $variant->definitions
|
||||
->map(fn ($definition): array => [
|
||||
'name' => (string) ($definition->itemAttribute?->attribute?->nombre ?? ''),
|
||||
'value' => $definition->value,
|
||||
])
|
||||
->filter(fn (array $attribute): bool => $attribute['name'] !== '' || $attribute['value'] !== null)
|
||||
->values()
|
||||
->all();
|
||||
->values();
|
||||
|
||||
$eventDates = $variant->relationLoaded('eventDates')
|
||||
? $variant->selectedEventDates()
|
||||
: collect();
|
||||
if ($eventDates->isNotEmpty()) {
|
||||
$attributes->prepend([
|
||||
'name' => 'Fecha',
|
||||
'value' => $eventDates
|
||||
->map(fn ($eventDate): string => $eventDate->date->format('Y-m-d'))
|
||||
->values()
|
||||
->all(),
|
||||
]);
|
||||
}
|
||||
|
||||
return $attributes->all();
|
||||
}
|
||||
|
||||
private function formatMoney(float|int|string|null $amount): string
|
||||
|
||||
@@ -53,13 +53,25 @@ class PurchaseItemSnapshotFactory
|
||||
/** @return array<int, array{name: string, value: mixed}> */
|
||||
private function snapshotAttributes(Variant $variant): array
|
||||
{
|
||||
return $variant->definitions
|
||||
$attributes = $variant->definitions
|
||||
->map(fn ($definition): array => [
|
||||
'name' => (string) ($definition->itemAttribute?->attribute?->nombre ?? ''),
|
||||
'value' => $definition->value,
|
||||
])
|
||||
->filter(fn (array $attribute): bool => $attribute['name'] !== '' || $attribute['value'] !== null)
|
||||
->values()
|
||||
->all();
|
||||
->values();
|
||||
|
||||
$eventDates = $variant->selectedEventDates();
|
||||
if ($eventDates->isNotEmpty()) {
|
||||
$attributes->prepend([
|
||||
'name' => 'Fecha',
|
||||
'value' => $eventDates
|
||||
->map(fn ($eventDate): string => $eventDate->date->format('Y-m-d'))
|
||||
->values()
|
||||
->all(),
|
||||
]);
|
||||
}
|
||||
|
||||
return $attributes->all();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,6 +278,8 @@ class StartCheckoutService
|
||||
'variant.attachments',
|
||||
'variant.catalogItem',
|
||||
'variant.definitions.itemAttribute.attribute',
|
||||
'variant.eventDates',
|
||||
'variant.eventDate',
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Domains\Ticket\Services;
|
||||
use App\Domains\Auth\Models\User;
|
||||
use App\Domains\Catalog\Models\CatalogItem;
|
||||
use App\Domains\Catalog\Models\Variant;
|
||||
use App\Domains\Event\Models\EventDate;
|
||||
use App\Domains\Ticket\Enums\ValidityTimeType;
|
||||
use App\Domains\Ticket\Exceptions\TicketGenerationException;
|
||||
use App\Domains\Ticket\Models\Ticket;
|
||||
@@ -44,9 +45,11 @@ class TicketGeneratorService
|
||||
): Ticket {
|
||||
$item = $target['catalog_item'];
|
||||
$variant = $target['variant'];
|
||||
$eventDate = $target['event_date'];
|
||||
$validityTime = $this->resolveValidityTime($item, $variant);
|
||||
$validityTime = $this->materializeEventDateValidityTime(
|
||||
$variant,
|
||||
$eventDate,
|
||||
$validityTime,
|
||||
$fixedValidityTimes,
|
||||
);
|
||||
@@ -68,7 +71,7 @@ class TicketGeneratorService
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, array{catalog_item: CatalogItem, variant: Variant|null}>
|
||||
* @return Collection<int, array{catalog_item: CatalogItem, variant: Variant|null, event_date: EventDate|null}>
|
||||
*/
|
||||
private function resolveTargets(
|
||||
CatalogItem $catalogItem,
|
||||
@@ -79,10 +82,7 @@ class TicketGeneratorService
|
||||
$variant = $this->resolveVariant($catalogItem, $sourceVariantId);
|
||||
$this->validateTarget($catalogItem, $variant);
|
||||
|
||||
return Collection::times($quantity, fn (): array => [
|
||||
'catalog_item' => $catalogItem,
|
||||
'variant' => $variant,
|
||||
]);
|
||||
return $this->targetsForVariant($catalogItem, $variant, $quantity);
|
||||
}
|
||||
|
||||
$catalogItem->loadMissing([
|
||||
@@ -100,17 +100,46 @@ class TicketGeneratorService
|
||||
$variant = $component->variant;
|
||||
$this->validateTarget($componentItem, $variant);
|
||||
|
||||
return Collection::times(
|
||||
return $this->targetsForVariant(
|
||||
$componentItem,
|
||||
$variant,
|
||||
$quantity * $component->quantity,
|
||||
fn (): array => [
|
||||
'catalog_item' => $componentItem,
|
||||
'variant' => $variant,
|
||||
],
|
||||
);
|
||||
})
|
||||
->values();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, array{catalog_item: CatalogItem, variant: Variant|null, event_date: EventDate|null}>
|
||||
*/
|
||||
private function targetsForVariant(
|
||||
CatalogItem $catalogItem,
|
||||
?Variant $variant,
|
||||
int $quantity,
|
||||
): Collection {
|
||||
if ($variant === null) {
|
||||
return Collection::times($quantity, fn (): array => [
|
||||
'catalog_item' => $catalogItem,
|
||||
'variant' => null,
|
||||
'event_date' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
$variant->loadMissing(['eventDates', 'eventDate']);
|
||||
$selectedEventDates = $variant->selectedEventDates();
|
||||
$eventDates = $selectedEventDates->isEmpty()
|
||||
? collect([null])
|
||||
: $selectedEventDates;
|
||||
|
||||
return Collection::times($quantity)
|
||||
->flatMap(fn () => $eventDates->map(fn (?EventDate $eventDate): array => [
|
||||
'catalog_item' => $catalogItem,
|
||||
'variant' => $variant,
|
||||
'event_date' => $eventDate,
|
||||
]))
|
||||
->values();
|
||||
}
|
||||
|
||||
private function resolveVariant(
|
||||
CatalogItem $catalogItem,
|
||||
?int $sourceVariantId,
|
||||
@@ -185,32 +214,29 @@ class TicketGeneratorService
|
||||
*/
|
||||
private function materializeEventDateValidityTime(
|
||||
?Variant $variant,
|
||||
?EventDate $eventDate,
|
||||
?ValidityTime $validityTime,
|
||||
array &$fixedValidityTimes,
|
||||
): ?ValidityTime {
|
||||
if (
|
||||
$variant === null
|
||||
|| $validityTime === null
|
||||
|| $validityTime->type !== ValidityTimeType::TimeWindow
|
||||
|| $eventDate === null
|
||||
) {
|
||||
return $validityTime;
|
||||
}
|
||||
|
||||
$variant->loadMissing('eventDate');
|
||||
$eventDate = $variant->eventDate;
|
||||
|
||||
if ($eventDate === null) {
|
||||
if ($validityTime !== null && $validityTime->type !== ValidityTimeType::TimeWindow) {
|
||||
return $validityTime;
|
||||
}
|
||||
|
||||
$cacheKey = $eventDate->getKey().':'.$validityTime->getKey();
|
||||
$cacheKey = $eventDate->getKey().':'.($validityTime?->getKey() ?? 'event');
|
||||
|
||||
if (isset($fixedValidityTimes[$cacheKey])) {
|
||||
return $fixedValidityTimes[$cacheKey];
|
||||
}
|
||||
|
||||
$startsAt = $validityTime->startsAt($eventDate->date);
|
||||
$expiresAt = $validityTime->expiresAt($eventDate->date);
|
||||
$startsAt = $validityTime?->startsAt($eventDate->date) ?? $eventDate->startsAt();
|
||||
$expiresAt = $validityTime?->expiresAt($eventDate->date) ?? $eventDate->endsAt();
|
||||
|
||||
if (
|
||||
$startsAt !== null
|
||||
|
||||
Reference in New Issue
Block a user