*/ public function generate( CatalogItem $catalogItem, User $user, int $quantity = 1, ?int $sourceVariantId = null, ): Collection { if ($quantity < 1) { throw TicketGenerationException::invalidQuantity(); } return DB::transaction(function () use ($catalogItem, $user, $quantity, $sourceVariantId): Collection { $targets = $this->resolveTargets( $catalogItem, $quantity, $sourceVariantId, ); return $targets->map(function (array $target) use ( $catalogItem, $sourceVariantId, $user, ): Ticket { $item = $target['catalog_item']; $selectedItem = $target['variant'] ?? $item; return Ticket::query()->create([ 'tenant_code' => $item->tenant_code, 'ticket' => (string) Str::uuid(), 'name' => $item->nombre, 'description' => (string) ($item->descripcion ?? ''), 'source_catalog_item_id' => $catalogItem->getKey(), 'source_variant_id' => $sourceVariantId, 'starts_at' => $selectedItem->getMinimumUseDate(), 'expires_at' => $selectedItem->getMaximumUseDate(), 'used_at' => null, 'user_id' => $user->getKey(), ]); }); }); } /** * @return Collection */ private function resolveTargets( CatalogItem $catalogItem, int $quantity, ?int $sourceVariantId, ): Collection { if (! $catalogItem->isBundle()) { $variant = $this->resolveVariant($catalogItem, $sourceVariantId); $this->validateTarget($catalogItem, $variant); return Collection::times($quantity, fn (): array => [ 'catalog_item' => $catalogItem, 'variant' => $variant, ]); } $catalogItem->loadMissing([ 'bundleComponents.catalogItem', 'bundleComponents.variant.catalogItem', ]); if ($catalogItem->bundleComponents->isEmpty()) { throw TicketGenerationException::emptyBundle($catalogItem); } return $catalogItem->bundleComponents ->flatMap(function ($component) use ($quantity): Collection { $componentItem = $component->catalogItem; $variant = $component->variant; $this->validateTarget($componentItem, $variant); return Collection::times( $quantity * $component->quantity, fn (): array => [ 'catalog_item' => $componentItem, 'variant' => $variant, ], ); }) ->values(); } private function resolveVariant( CatalogItem $catalogItem, ?int $sourceVariantId, ): ?Variant { if ($sourceVariantId === null) { return null; } $variant = $catalogItem->variants() ->whereKey($sourceVariantId) ->first(); if ($variant === null) { throw TicketGenerationException::variantNotFound( $catalogItem, $sourceVariantId, ); } $variant->setRelation('catalogItem', $catalogItem); return $variant; } private function validateTarget( CatalogItem $catalogItem, ?Variant $variant, ): void { if (! $catalogItem->has_tickets) { throw TicketGenerationException::ticketsDisabled($catalogItem); } // TODO: Reactivar esta validación cuando los pagos con productos vencidos // deban rechazarse nuevamente. Se deja deshabilitada temporalmente. // $selectedItem = $variant ?? $catalogItem; // // if ($selectedItem->getMaximumUseDate()?->lessThanOrEqualTo(now())) { // throw TicketGenerationException::maximumUseDateReached($catalogItem); // } } }