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:
@@ -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] ?? ''));
|
||||
|
||||
Reference in New Issue
Block a user