'integer', ]; } /** * @return BelongsTo */ public function user(): BelongsTo { return $this->belongsTo(User::class); } /** * @return HasMany */ public function items(): HasMany { return $this->hasMany(CartItem::class, 'cart_id'); } public function addItem(int $productVariantId, int $quantity): CartItem { if ($quantity <= 0) { throw ValidationException::withMessages([ 'cantidad' => 'La cantidad debe ser mayor a cero.', ]); } return DB::transaction(function () use ($productVariantId, $quantity) { /** @var ProductVariant $variant */ $variant = ProductVariant::query() ->lockForUpdate() ->findOrFail($productVariantId); if ($variant->stock < $quantity) { throw ValidationException::withMessages([ 'cantidad' => 'Stock insuficiente para la variante solicitada.', ]); } /** @var CartItem|null $item */ $item = $this->items() ->where('producto_variante_id', $productVariantId) ->lockForUpdate() ->first(); if ($item) { $item->cantidad += $quantity; $item->save(); } else { $item = $this->items()->create([ 'producto_variante_id' => $productVariantId, 'cantidad' => $quantity, ]); } $variant->decrement('stock', $quantity); return $item->fresh(); }); } public function updateItem(int $productVariantId, int $quantity): CartItem { if ($quantity <= 0) { throw ValidationException::withMessages([ 'cantidad' => 'La cantidad debe ser mayor a cero.', ]); } return DB::transaction(function () use ($productVariantId, $quantity) { /** @var CartItem $item */ $item = $this->items() ->where('producto_variante_id', $productVariantId) ->lockForUpdate() ->firstOrFail(); /** @var ProductVariant $variant */ $variant = ProductVariant::query() ->lockForUpdate() ->findOrFail($productVariantId); $delta = $quantity - $item->cantidad; if ($delta > 0 && $variant->stock < $delta) { throw ValidationException::withMessages([ 'cantidad' => 'Stock insuficiente para actualizar la cantidad solicitada.', ]); } $item->cantidad = $quantity; $item->save(); if ($delta > 0) { $variant->decrement('stock', $delta); } if ($delta < 0) { $variant->increment('stock', abs($delta)); } return $item->fresh(); }); } public function removeItem(int $productVariantId): void { DB::transaction(function () use ($productVariantId) { /** @var CartItem $item */ $item = $this->items() ->where('producto_variante_id', $productVariantId) ->lockForUpdate() ->firstOrFail(); /** @var ProductVariant $variant */ $variant = ProductVariant::query() ->lockForUpdate() ->findOrFail($productVariantId); $variant->increment('stock', $item->cantidad); $item->delete(); }); } }