100 lines
3.0 KiB
PHP
100 lines
3.0 KiB
PHP
<?php
|
|
|
|
namespace App\Domains\Cart\Services;
|
|
|
|
use App\Domains\Cart\Models\Cart;
|
|
use App\Domains\Cart\Models\CartItem;
|
|
use App\Domains\Catalog\Models\Variant;
|
|
use App\Domains\Catalog\Services\CatalogInventoryService;
|
|
use Illuminate\Validation\ValidationException;
|
|
|
|
class CartVariantReplacementService
|
|
{
|
|
public function __construct(private readonly CatalogInventoryService $inventory) {}
|
|
|
|
public function replaceHistoricalVariants(Cart $cart): void
|
|
{
|
|
$items = $cart->items()
|
|
->whereNotNull('variant_id')
|
|
->orderBy('id')
|
|
->lockForUpdate()
|
|
->get();
|
|
|
|
foreach ($items as $item) {
|
|
$variant = Variant::query()->lockForUpdate()->find($item->variant_id);
|
|
|
|
if ($variant === null) {
|
|
throw $this->unavailableVariant();
|
|
}
|
|
|
|
$replacement = $this->latestReplacement($variant);
|
|
|
|
if ($replacement->is($variant)) {
|
|
if (! $variant->isSellable()) {
|
|
throw $this->unavailableVariant();
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if (! $replacement->isSellable()) {
|
|
throw $this->unavailableVariant();
|
|
}
|
|
|
|
/** @var CartItem|null $targetItem */
|
|
$targetItem = $cart->items()
|
|
->whereKeyNot($item->getKey())
|
|
->where('catalog_item_id', $item->catalog_item_id)
|
|
->where('variant_id', $replacement->getKey())
|
|
->lockForUpdate()
|
|
->first();
|
|
|
|
$replacementQuantity = $item->cantidad + ($targetItem?->cantidad ?? 0);
|
|
if ($replacement->inventory_id !== $variant->inventory_id) {
|
|
$available = $this->inventory->availableQuantity($replacement);
|
|
|
|
if ($available !== null && $available < $replacementQuantity) {
|
|
throw $this->unavailableVariant();
|
|
}
|
|
}
|
|
|
|
if ($targetItem !== null) {
|
|
$targetItem->cantidad += $item->cantidad;
|
|
$targetItem->save();
|
|
$item->delete();
|
|
|
|
continue;
|
|
}
|
|
|
|
$item->update(['variant_id' => $replacement->getKey()]);
|
|
}
|
|
}
|
|
|
|
private function latestReplacement(Variant $variant): Variant
|
|
{
|
|
$current = $variant;
|
|
$visited = [];
|
|
|
|
while ($current->replaced_by_variant_id !== null) {
|
|
if (isset($visited[$current->getKey()])) {
|
|
throw $this->unavailableVariant();
|
|
}
|
|
|
|
$visited[$current->getKey()] = true;
|
|
$current = Variant::query()
|
|
->lockForUpdate()
|
|
->find($current->replaced_by_variant_id)
|
|
?? throw $this->unavailableVariant();
|
|
}
|
|
|
|
return $current;
|
|
}
|
|
|
|
private function unavailableVariant(): ValidationException
|
|
{
|
|
return ValidationException::withMessages([
|
|
'cart_id' => [__('api.cart.cart_variant_unavailable')],
|
|
]);
|
|
}
|
|
}
|