refactor(catalog): add event date attribute with dynamic options and update related logic

This commit is contained in:
2026-08-07 10:03:31 -03:00
parent 14d53d1e0b
commit 717ee5d194
11 changed files with 224 additions and 86 deletions

View File

@@ -2,6 +2,7 @@
namespace App\Domains\Catalog\Models;
use App\Domains\Event\Models\EventDate;
use App\Domains\Shared\Enums\FieldType;
use App\Domains\Tenant\Models\Tenant;
use Illuminate\Database\Eloquent\Attributes\Fillable;
@@ -51,4 +52,14 @@ class Attribute extends Model
{
return $this->hasMany(AttributeOption::class, 'attribute_id')->orderBy('sort_order');
}
/**
* @return HasMany<EventDate, $this>
*/
public function eventDates(): HasMany
{
return $this->hasMany(EventDate::class, 'tenant_code', 'tenant_codigo')
->orderBy('date')
->orderBy('time_start');
}
}

View File

@@ -6,6 +6,7 @@ use App\Domains\Catalog\Enums\InventoryPolicy;
use App\Domains\Catalog\Models\CatalogItem;
use App\Domains\Catalog\Models\ItemAttribute;
use App\Domains\Catalog\Models\Variant;
use App\Domains\Shared\Enums\FieldType;
use App\Domains\Ticket\Resources\ValidityTimeResource;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
@@ -81,12 +82,6 @@ class CatalogItemDetailResource extends JsonResource
private function attributeData(ItemAttribute $itemAttribute): array
{
$attribute = $itemAttribute->attribute;
$availableValues = $this->variants
->flatMap->definitions
->where('item_attribute_id', $itemAttribute->id)
->pluck('value')
->filter()
->unique();
return [
'id' => $attribute->id,
@@ -95,66 +90,66 @@ class CatalogItemDetailResource extends JsonResource
'is_required' => $attribute->is_required,
'metadata_schema' => $attribute->metadata_schema,
'type' => $attribute->type->value,
'options' => $attribute->options
->whereIn('value', $availableValues)
->map(fn ($option): array => [
'id' => $option->id,
'value' => $option->value,
'label' => $option->label,
'sort_order' => $option->sort_order,
'validity_time_id' => $option->validity_time_id,
'validity_time' => $option->validityTime === null
? null
: ValidityTimeResource::make($option->validityTime),
'metadata' => $option->metadata,
])
->values(),
'options' => $attribute->type === FieldType::EventDate
? $this->eventDateOptions($itemAttribute)
: $this->catalogAttributeOptions($itemAttribute),
];
}
/** @return Collection<int, array<string, mixed>> */
private function eventDateOptions(ItemAttribute $itemAttribute): Collection
{
return $itemAttribute->attribute->eventDates
->map(fn ($eventDate, int $index): array => [
'id' => $eventDate->id,
'value' => (string) $eventDate->id,
'label' => $eventDate->date->format('d/m/Y').' · '
.substr($eventDate->time_start, 0, 5).' a '
.substr($eventDate->time_end, 0, 5),
'sort_order' => $index,
'validity_time_id' => null,
'validity_time' => null,
'metadata' => [
'date' => $eventDate->date->format('Y-m-d'),
'time_start' => $eventDate->time_start,
'time_end' => $eventDate->time_end,
],
]);
}
/** @return Collection<int, array<string, mixed>> */
private function catalogAttributeOptions(ItemAttribute $itemAttribute): Collection
{
$availableValues = $this->variants
->flatMap->definitions
->where('item_attribute_id', $itemAttribute->id)
->pluck('value')
->filter()
->unique();
return $itemAttribute->attribute->options
->whereIn('value', $availableValues)
->map(fn ($option): array => [
'id' => $option->id,
'value' => $option->value,
'label' => $option->label,
'sort_order' => $option->sort_order,
'validity_time_id' => $option->validity_time_id,
'validity_time' => $option->validityTime === null
? null
: ValidityTimeResource::make($option->validityTime),
'metadata' => $option->metadata,
])
->values();
}
/** @return Collection<int, array<string, mixed>> */
private function attributesData(): Collection
{
$attributes = $this->itemAttributes
->map(fn (ItemAttribute $itemAttribute): array => $this->attributeData($itemAttribute));
if ($this->variants->isEmpty() || $this->variants->contains(
fn (Variant $variant): bool => $variant->event_date_id === null
)) {
return $attributes->values();
}
$eventDateAttribute = [
'id' => -1,
'codigo' => 'event_date',
'nombre' => 'Fecha',
'is_required' => true,
'metadata_schema' => null,
'type' => 'event_date',
'options' => $this->variants
->pluck('eventDate')
->filter()
->unique('id')
->sortBy(fn ($eventDate): string => $eventDate->date->format('Y-m-d').' '.$eventDate->time_start)
->values()
->map(fn ($eventDate, int $index): array => [
'id' => $eventDate->id,
'value' => (string) $eventDate->id,
'label' => $eventDate->date->format('d/m/Y').' · '
.substr($eventDate->time_start, 0, 5).' a '
.substr($eventDate->time_end, 0, 5),
'sort_order' => $index,
'validity_time_id' => null,
'validity_time' => null,
'metadata' => [
'date' => $eventDate->date->format('Y-m-d'),
'time_start' => $eventDate->time_start,
'time_end' => $eventDate->time_end,
],
]),
];
return collect([$eventDateAttribute])->concat($attributes)->values();
return $this->itemAttributes
->sortBy(fn (ItemAttribute $itemAttribute): int => $itemAttribute->attribute->type === FieldType::EventDate ? 0 : 1)
->map(fn (ItemAttribute $itemAttribute): array => $this->attributeData($itemAttribute))
->values();
}
/** @return array<string, mixed> */

View File

@@ -44,14 +44,17 @@ class CatalogService
$hasEventDateVariants = $variants !== [] && collect($variants)->every(
fn (array $variant): bool => ! empty($variant['event_date_id'])
);
$hasVariants = $attributeCodes !== [] || $hasEventDateVariants;
if ($hasEventDateVariants && in_array('event_date', $attributeCodes, true)) {
if ($hasEventDateVariants && ! in_array('event_date', $attributeCodes, true)) {
throw ValidationException::withMessages([
'attribute_codes' => ['The event_date code is reserved for event dates.'],
'attribute_codes' => [
'The event_date attribute is required for event date variants.',
],
]);
}
$hasVariants = $attributeCodes !== [] || $hasEventDateVariants;
$this->validateEventProductType($data);
$this->validateUniqueVariantCombinations($variants, $attributeCodes);
@@ -154,6 +157,7 @@ class CatalogService
'brand',
'validityTime',
'itemAttributes.attribute.options.validityTime',
'itemAttributes.attribute.eventDates',
'variants' => fn ($query) => $query->orderBy('id'),
'variants.inventory',
'variants.attachments',

View File

@@ -11,10 +11,16 @@ enum FieldType: string
case Multiselect = 'multiselect';
case Color = 'color';
case Image = 'image';
case EventDate = 'event_date';
public function supportsOptions(): bool
{
return in_array($this, [self::Select, self::Multiselect], true);
return in_array($this, [self::Select, self::Multiselect, self::EventDate], true);
}
public function usesDynamicOptions(): bool
{
return $this === self::EventDate;
}
/**