*/ public function all(Tenant $tenant): Collection { return CatalogItem::query() ->where('tenant_code', $tenant->codigo) ->where('event_product_type', EventProductType::Entry->value) ->with([ 'variants.inventory', 'variants.eventDate', 'variants.eventDates', ]) ->orderBy('id') ->get(); } /** * @param array> $entries * @return Collection */ public function upsertMany(Tenant $tenant, array $entries): Collection { return DB::transaction(function () use ($tenant, $entries): Collection { $reservedSlugs = []; $category = Category::query()->firstOrCreate([ 'tenant_code' => $tenant->codigo, 'nombre' => 'Entradas', ]); return collect($entries)->map(function (array $entry, int $index) use ($tenant, $category, &$reservedSlugs): CatalogItem { if (isset($entry['id'])) { return $this->update($tenant, $category, $entry, $index); } $slug = $this->uniqueSlug($tenant, $entry['title'], $reservedSlugs); $reservedSlugs[] = $slug; return $this->catalogService->create([ 'tenant_code' => $tenant->codigo, 'slug' => $slug, 'nombre' => $entry['title'], 'descripcion' => $entry['description'] ?? null, 'category_id' => $category->id, 'precio' => $entry['price'], 'event_product_type' => EventProductType::Entry->value, 'has_tickets' => true, 'inventory_policy' => InventoryPolicy::Tracked->value, 'attribute_codes' => ['event_date'], 'multi_select_attribute_codes' => ['event_date'], 'variants' => [[ 'real_stock' => $entry['stock'], 'event_date_ids' => array_values($entry['event_date_ids']), ]], ]); })->values(); }); } /** @param array $entry */ private function update(Tenant $tenant, Category $category, array $entry, int $index): CatalogItem { $catalogItem = CatalogItem::query() ->whereKey($entry['id']) ->where('tenant_code', $tenant->codigo) ->where('event_product_type', EventProductType::Entry->value) ->lockForUpdate() ->firstOrFail(); $variants = Variant::query() ->where('catalog_item_id', $catalogItem->id) ->lockForUpdate() ->get(); if ($variants->count() !== 1) { throw ValidationException::withMessages([ "entries.{$index}.id" => [ 'La entrada no posee una Ășnica variante editable.', ], ]); } $variant = $variants->first(); $inventory = Inventory::query() ->whereKey($variant->inventory_id) ->lockForUpdate() ->firstOrFail(); if ((int) $entry['stock'] < $inventory->reserved_stock) { throw ValidationException::withMessages([ "entries.{$index}.stock" => [ 'El stock no puede ser menor que la cantidad actualmente reservada.', ], ]); } $eventDateIds = collect($entry['event_date_ids']) ->map(fn ($id): int => (int) $id) ->unique() ->values(); $catalogItem->update([ 'nombre' => $entry['title'], 'descripcion' => $entry['description'] ?? null, 'category_id' => $category->id, 'precio' => $entry['price'], 'has_tickets' => true, 'inventory_policy' => InventoryPolicy::Tracked->value, ]); $variant->update([ 'event_date_id' => $eventDateIds->count() === 1 ? $eventDateIds->first() : null, ]); $variant->eventDates()->sync($eventDateIds->all()); $catalogItem->itemAttributes() ->whereHas('attribute', fn ($query) => $query->where('codigo', 'event_date')) ->update(['allow_multi_select' => true]); $inventory->update(['real_stock' => $entry['stock']]); return $catalogItem->load([ 'variants.inventory', 'variants.eventDate', 'variants.eventDates', ]); } /** @param array $reservedSlugs */ private function uniqueSlug(Tenant $tenant, string $title, array $reservedSlugs): string { $baseSlug = Str::slug($title) ?: 'entrada'; $slug = $baseSlug; $suffix = 2; while ( in_array($slug, $reservedSlugs, true) || CatalogItem::query() ->where('tenant_code', $tenant->codigo) ->where('slug', $slug) ->exists() ) { $slug = "{$baseSlug}-{$suffix}"; $suffix++; } return $slug; } }