feat(purchase): implement purchase limit enforcement and custom exception handling
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Purchase\Exceptions;
|
||||
|
||||
use App\Domains\Catalog\Models\CatalogItem;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class PurchaseLimitExceededException extends ValidationException
|
||||
{
|
||||
public readonly int $catalogItemId;
|
||||
|
||||
public readonly string $catalogItemName;
|
||||
|
||||
public readonly int $maximumAddableQuantity;
|
||||
|
||||
public function __construct(CatalogItem $catalogItem, int $maximumAddableQuantity, string $field)
|
||||
{
|
||||
$this->catalogItemId = (int) $catalogItem->getKey();
|
||||
$this->catalogItemName = $catalogItem->nombre;
|
||||
$this->maximumAddableQuantity = $maximumAddableQuantity;
|
||||
|
||||
parent::__construct(validator([], []));
|
||||
|
||||
$message = trans_choice('api.purchase_limit.exceeded', $maximumAddableQuantity, [
|
||||
'max' => $maximumAddableQuantity,
|
||||
'product' => $this->catalogItemName,
|
||||
]);
|
||||
$this->message = $message;
|
||||
$this->validator->errors()->add($field, $message);
|
||||
}
|
||||
}
|
||||
@@ -120,10 +120,16 @@ class StartCheckoutService
|
||||
->each(function (Collection $catalogLines) use ($userId): void {
|
||||
/** @var CatalogItem $catalogItem */
|
||||
$catalogItem = $catalogLines->first()['catalog_item'];
|
||||
$availableQuantities = $catalogLines
|
||||
->map(fn (array $line): ?int => $this->inventory->availableQuantity($line['selection']));
|
||||
$maximumAddableCeiling = $availableQuantities->contains(null)
|
||||
? null
|
||||
: (int) $availableQuantities->sum();
|
||||
$this->purchaseLimits->assertCanPurchase(
|
||||
$catalogItem,
|
||||
$userId,
|
||||
(int) $catalogLines->sum('quantity'),
|
||||
maximumAddableCeiling: $maximumAddableCeiling,
|
||||
field: 'direct_items',
|
||||
);
|
||||
});
|
||||
@@ -335,6 +341,7 @@ class StartCheckoutService
|
||||
$userId,
|
||||
$quantity,
|
||||
excludedCartId: $cartId,
|
||||
heldQuantity: $quantity,
|
||||
field: 'cart_id',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,11 +4,11 @@ namespace App\Domains\Purchase\Services;
|
||||
|
||||
use App\Domains\Cart\Models\CartItem;
|
||||
use App\Domains\Catalog\Models\CatalogItem;
|
||||
use App\Domains\Purchase\Exceptions\PurchaseLimitExceededException;
|
||||
use App\Domains\Purchase\Models\Purchase;
|
||||
use App\Domains\Purchase\Models\PurchaseItem;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class UserPurchaseLimitService
|
||||
{
|
||||
@@ -18,6 +18,8 @@ class UserPurchaseLimitService
|
||||
int $requestedQuantity,
|
||||
?int $excludedPurchaseId = null,
|
||||
?int $excludedCartId = null,
|
||||
int $heldQuantity = 0,
|
||||
?int $maximumAddableCeiling = null,
|
||||
string $field = 'quantity',
|
||||
): void {
|
||||
DB::transaction(function () use (
|
||||
@@ -26,6 +28,8 @@ class UserPurchaseLimitService
|
||||
$requestedQuantity,
|
||||
$excludedPurchaseId,
|
||||
$excludedCartId,
|
||||
$heldQuantity,
|
||||
$maximumAddableCeiling,
|
||||
$field,
|
||||
): void {
|
||||
/** @var CatalogItem $catalogItem */
|
||||
@@ -86,9 +90,20 @@ class UserPurchaseLimitService
|
||||
->sum('cantidad');
|
||||
|
||||
if ($purchasedQuantity + $checkoutQuantity + $reservedCartQuantity + $requestedQuantity > $limit) {
|
||||
throw ValidationException::withMessages([
|
||||
$field => __('api.purchase_limit.exceeded', ['max' => $limit]),
|
||||
]);
|
||||
$remainingQuota = max(
|
||||
0,
|
||||
$limit - $purchasedQuantity - $checkoutQuantity - $reservedCartQuantity,
|
||||
);
|
||||
|
||||
$maximumAddableQuantity = max(0, $remainingQuota - $heldQuantity);
|
||||
|
||||
throw new PurchaseLimitExceededException(
|
||||
$catalogItem,
|
||||
$maximumAddableCeiling === null
|
||||
? $maximumAddableQuantity
|
||||
: min($maximumAddableQuantity, $maximumAddableCeiling),
|
||||
$field,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user