'decimal:2', ]; } /** * @return BelongsTo */ public function tenant(): BelongsTo { return $this->belongsTo(Tenant::class, 'tenant_codigo', 'codigo'); } /** * @return HasMany */ public function items(): HasMany { return $this->hasMany(BundleItem::class, 'bundle_id'); } /** * @return MorphMany */ public function groupItems(): MorphMany { return $this->morphMany(GroupItem::class, 'groupable'); } public function getPrice(): float { return (float) $this->precio; } public function getName(): string { return $this->nombre ?? 'Bundle'; } public function availableQuantity(): ?int { if ($this->items->isEmpty()) { return 0; } $availableQuantities = []; foreach ($this->items as $item) { $variantQuantity = $item->variant?->availableQuantity(); if ($variantQuantity !== null) { $availableQuantities[] = intdiv($variantQuantity, $item->cantidad); } } return $availableQuantities === [] ? null : min($availableQuantities); } public function reserveStock(int $amount): void { if ($amount < 0) { throw new \InvalidArgumentException('El monto a reservar debe ser positivo.'); } foreach ($this->items as $item) { $item->variant->reserveStock($amount * $item->cantidad); } } public function decrementReservedStock(int $amount): void { if ($amount < 0) { throw new \InvalidArgumentException('El monto a decrementar debe ser positivo.'); } foreach ($this->items as $item) { $item->variant->decrementReservedStock($amount * $item->cantidad); } } public function buy(int $amount): void { if ($amount < 0) { throw new \InvalidArgumentException('El monto a comprar debe ser positivo.'); } foreach ($this->items as $item) { $item->variant->buy($amount * $item->cantidad); } } public function validateStock(): void { $availableQuantity = $this->availableQuantity(); if ($availableQuantity !== null && $availableQuantity <= 0) { throw new \InvalidArgumentException('El bundle no tiene stock tecnico disponible.'); } } protected function stockTecnico(): Attribute { return Attribute::get(fn (): ?int => $this->availableQuantity()); } }