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:
2026-08-07 11:57:38 -03:00
parent 91af233941
commit 21b70777f2
22 changed files with 516 additions and 413 deletions

View File

@@ -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] ?? ''));