refactor(stock): make expired reservations terminal
This commit is contained in:
parent
8545fbc645
commit
896fb81bcf
|
|
@ -86,4 +86,14 @@ class CartController extends Controller
|
||||||
'message' => __('api.cart.item_removed'),
|
'message' => __('api.cart.item_removed'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function restart(Request $request, Tenant $tenant): CartResource
|
||||||
|
{
|
||||||
|
return CartResource::make(
|
||||||
|
$this->cartService->restartExpired($tenant, $request),
|
||||||
|
)->additional([
|
||||||
|
'code' => 'cart.restarted',
|
||||||
|
'message' => __('api.cart.restarted'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,7 @@ class Cart extends Model
|
||||||
|
|
||||||
return DB::transaction(function () use ($catalogItemId, $variantId, $quantity): CartItem {
|
return DB::transaction(function () use ($catalogItemId, $variantId, $quantity): CartItem {
|
||||||
$this->invalidateCurrentCheckout();
|
$this->invalidateCurrentCheckout();
|
||||||
|
app(StockReservationService::class)->assertCartReservationUsable($this);
|
||||||
$selectedItem = $this->resolveScopedItem($catalogItemId, $variantId, true);
|
$selectedItem = $this->resolveScopedItem($catalogItemId, $variantId, true);
|
||||||
$cartQuantity = (int) $this->items()
|
$cartQuantity = (int) $this->items()
|
||||||
->where('catalog_item_id', $catalogItemId)
|
->where('catalog_item_id', $catalogItemId)
|
||||||
|
|
@ -183,6 +184,7 @@ class Cart extends Model
|
||||||
$excludedPurchaseId,
|
$excludedPurchaseId,
|
||||||
): CartItem {
|
): CartItem {
|
||||||
$this->invalidateCurrentCheckout();
|
$this->invalidateCurrentCheckout();
|
||||||
|
app(StockReservationService::class)->assertCartReservationUsable($this);
|
||||||
|
|
||||||
/** @var CartItem $item */
|
/** @var CartItem $item */
|
||||||
$item = $this->items()
|
$item = $this->items()
|
||||||
|
|
@ -284,6 +286,7 @@ class Cart extends Model
|
||||||
{
|
{
|
||||||
DB::transaction(function () use ($cartItemId): void {
|
DB::transaction(function () use ($cartItemId): void {
|
||||||
$this->invalidateCurrentCheckout();
|
$this->invalidateCurrentCheckout();
|
||||||
|
app(StockReservationService::class)->assertCartReservationUsable($this);
|
||||||
|
|
||||||
/** @var CartItem $item */
|
/** @var CartItem $item */
|
||||||
$item = $this->items()
|
$item = $this->items()
|
||||||
|
|
@ -334,9 +337,14 @@ class Cart extends Model
|
||||||
Purchase::STATUS_CREATED,
|
Purchase::STATUS_CREATED,
|
||||||
Purchase::STATUS_PENDING_PAYMENT,
|
Purchase::STATUS_PENDING_PAYMENT,
|
||||||
], true)) {
|
], true)) {
|
||||||
|
app(StockReservationService::class)->returnToCart($currentPurchase, $cart);
|
||||||
$currentPurchase->update([
|
$currentPurchase->update([
|
||||||
'status' => Purchase::STATUS_SUPERSEDED,
|
'status' => Purchase::STATUS_SUPERSEDED,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$this->current_purchase_id = null;
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
app(StockReservationService::class)->releaseForPurchase(
|
app(StockReservationService::class)->releaseForPurchase(
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,12 @@ namespace App\Domains\Cart\Services;
|
||||||
|
|
||||||
use App\Domains\Auth\Models\User;
|
use App\Domains\Auth\Models\User;
|
||||||
use App\Domains\Cart\Models\Cart;
|
use App\Domains\Cart\Models\Cart;
|
||||||
|
use App\Domains\Catalog\Models\StockReservation;
|
||||||
|
use App\Domains\Catalog\Services\ExpireStockReservationsService;
|
||||||
use App\Domains\Tenant\Models\Tenant;
|
use App\Domains\Tenant\Models\Tenant;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Illuminate\Validation\ValidationException;
|
use Illuminate\Validation\ValidationException;
|
||||||
use Symfony\Component\HttpFoundation\Cookie;
|
use Symfony\Component\HttpFoundation\Cookie;
|
||||||
|
|
@ -94,6 +97,45 @@ class CartService
|
||||||
return $this->loadCart($cart, $tenant);
|
return $this->loadCart($cart, $tenant);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function restartExpired(Tenant $tenant, Request $request): Cart
|
||||||
|
{
|
||||||
|
$identity = $this->requireIdentity($request);
|
||||||
|
$cart = $this->findCartOrFail($tenant, $identity);
|
||||||
|
if ($cart->current_stock_reservation_id === null
|
||||||
|
|| ! app(ExpireStockReservationsService::class)
|
||||||
|
->expireIfOverdue($cart->current_stock_reservation_id)) {
|
||||||
|
throw ValidationException::withMessages([
|
||||||
|
'cart' => __('api.cart.reservation_not_expired'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$newCart = DB::transaction(function () use ($cart, $identity, $tenant): Cart {
|
||||||
|
/** @var Cart $cart */
|
||||||
|
$cart = Cart::query()->lockForUpdate()->findOrFail($cart->getKey());
|
||||||
|
/** @var StockReservation|null $reservation */
|
||||||
|
$reservation = $cart->current_stock_reservation_id === null
|
||||||
|
? null
|
||||||
|
: StockReservation::query()
|
||||||
|
->lockForUpdate()
|
||||||
|
->find($cart->current_stock_reservation_id);
|
||||||
|
|
||||||
|
if ($reservation?->status !== StockReservation::STATUS_EXPIRED) {
|
||||||
|
throw ValidationException::withMessages([
|
||||||
|
'cart' => __('api.cart.reservation_not_expired'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$cart->update([
|
||||||
|
'status' => 'abandoned',
|
||||||
|
'current_purchase_id' => null,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $this->findOrCreateCart($tenant, $identity);
|
||||||
|
});
|
||||||
|
|
||||||
|
return $this->loadCart($newCart, $tenant);
|
||||||
|
}
|
||||||
|
|
||||||
public function makeGuestTokenCookie(string $guestToken): Cookie
|
public function makeGuestTokenCookie(string $guestToken): Cookie
|
||||||
{
|
{
|
||||||
$secure = (bool) config('session.secure');
|
$secure = (bool) config('session.secure');
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use Illuminate\Support\Facades\Route;
|
||||||
Route::prefix('tenants/{tenant:codigo}')
|
Route::prefix('tenants/{tenant:codigo}')
|
||||||
->group(function (): void {
|
->group(function (): void {
|
||||||
Route::get('cart', [CartController::class, 'show']);
|
Route::get('cart', [CartController::class, 'show']);
|
||||||
|
Route::post('cart/restart', [CartController::class, 'restart']);
|
||||||
Route::post('cart/items', [CartController::class, 'addItem']);
|
Route::post('cart/items', [CartController::class, 'addItem']);
|
||||||
Route::patch('cart/items/{cartItem}', [CartController::class, 'updateItemQuantity']);
|
Route::patch('cart/items/{cartItem}', [CartController::class, 'updateItemQuantity']);
|
||||||
Route::delete('cart/items/{cartItem}', [CartController::class, 'removeItem']);
|
Route::delete('cart/items/{cartItem}', [CartController::class, 'removeItem']);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Catalog\Exceptions;
|
||||||
|
|
||||||
|
use RuntimeException;
|
||||||
|
|
||||||
|
class StockReservationExpiredException extends RuntimeException
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct(__('api.cart.reservation_expired'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -76,6 +76,20 @@ class ExpireStockReservationsService
|
||||||
return $summary;
|
return $summary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function expireIfOverdue(int $reservationId): bool
|
||||||
|
{
|
||||||
|
/** @var StockReservation|null $reservation */
|
||||||
|
$reservation = StockReservation::query()->find($reservationId);
|
||||||
|
if ($reservation?->status === StockReservation::STATUS_EXPIRED) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (! $this->isOverdue($reservation)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->expireReservation($reservationId) !== null;
|
||||||
|
}
|
||||||
|
|
||||||
/** @return 'purchases'|'cart_reservations'|'orphan_reservations'|null */
|
/** @return 'purchases'|'cart_reservations'|'orphan_reservations'|null */
|
||||||
private function expireReservation(int $reservationId): ?string
|
private function expireReservation(int $reservationId): ?string
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ namespace App\Domains\Catalog\Services;
|
||||||
|
|
||||||
use App\Domains\Cart\Models\Cart;
|
use App\Domains\Cart\Models\Cart;
|
||||||
use App\Domains\Cart\Models\CartItem;
|
use App\Domains\Cart\Models\CartItem;
|
||||||
|
use App\Domains\Catalog\Exceptions\StockReservationExpiredException;
|
||||||
use App\Domains\Catalog\Models\Inventory;
|
use App\Domains\Catalog\Models\Inventory;
|
||||||
use App\Domains\Catalog\Models\StockReservation;
|
use App\Domains\Catalog\Models\StockReservation;
|
||||||
use App\Domains\Catalog\Models\StockReservationLine;
|
use App\Domains\Catalog\Models\StockReservationLine;
|
||||||
|
|
@ -43,13 +44,8 @@ class StockReservationService
|
||||||
? null
|
? null
|
||||||
: StockReservation::query()->lockForUpdate()->find($lockedCart->current_stock_reservation_id);
|
: StockReservation::query()->lockForUpdate()->find($lockedCart->current_stock_reservation_id);
|
||||||
|
|
||||||
if ($reservation !== null
|
if ($reservation !== null) {
|
||||||
&& $reservation->status === StockReservation::STATUS_ACTIVE
|
$this->assertUsableCartReservation($reservation);
|
||||||
&& $reservation->expires_at !== null
|
|
||||||
&& $reservation->expires_at->isPast()) {
|
|
||||||
$this->finalizeLocked($reservation, StockReservation::STATUS_EXPIRED, null);
|
|
||||||
$lockedCart->update(['current_stock_reservation_id' => null]);
|
|
||||||
$reservation = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($requirements === []) {
|
if ($requirements === []) {
|
||||||
|
|
@ -67,7 +63,7 @@ class StockReservationService
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($reservation === null || $reservation->status !== StockReservation::STATUS_ACTIVE) {
|
if ($reservation === null) {
|
||||||
$reservation = StockReservation::query()->create([
|
$reservation = StockReservation::query()->create([
|
||||||
'status' => StockReservation::STATUS_ACTIVE,
|
'status' => StockReservation::STATUS_ACTIVE,
|
||||||
'expires_at' => $this->expiration(),
|
'expires_at' => $this->expiration(),
|
||||||
|
|
@ -175,9 +171,7 @@ class StockReservationService
|
||||||
|
|
||||||
/** @var StockReservation $reservation */
|
/** @var StockReservation $reservation */
|
||||||
$reservation = StockReservation::query()->lockForUpdate()->findOrFail($lockedCart->current_stock_reservation_id);
|
$reservation = StockReservation::query()->lockForUpdate()->findOrFail($lockedCart->current_stock_reservation_id);
|
||||||
if ($reservation->status !== StockReservation::STATUS_ACTIVE) {
|
$this->assertUsableCartReservation($reservation);
|
||||||
throw new \InvalidArgumentException('La reserva de stock no está activa.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$linkedPurchase = Purchase::query()
|
$linkedPurchase = Purchase::query()
|
||||||
->where('stock_reservation_id', $reservation->getKey())
|
->where('stock_reservation_id', $reservation->getKey())
|
||||||
|
|
@ -213,6 +207,9 @@ class StockReservationService
|
||||||
if ($reservation->status !== StockReservation::STATUS_ACTIVE) {
|
if ($reservation->status !== StockReservation::STATUS_ACTIVE) {
|
||||||
throw new \InvalidArgumentException('La reserva de stock no está activa.');
|
throw new \InvalidArgumentException('La reserva de stock no está activa.');
|
||||||
}
|
}
|
||||||
|
if ($reservation->expires_at !== null && ! $reservation->expires_at->isFuture()) {
|
||||||
|
throw new StockReservationExpiredException;
|
||||||
|
}
|
||||||
|
|
||||||
$lines = $this->lockLines($reservation);
|
$lines = $this->lockLines($reservation);
|
||||||
if ($lines->isEmpty()) {
|
if ($lines->isEmpty()) {
|
||||||
|
|
@ -267,6 +264,50 @@ class StockReservationService
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function returnToCart(Purchase $purchase, Cart $cart): StockReservation
|
||||||
|
{
|
||||||
|
return DB::transaction(function () use ($purchase, $cart): StockReservation {
|
||||||
|
/** @var Purchase $purchase */
|
||||||
|
$purchase = Purchase::query()->lockForUpdate()->findOrFail($purchase->getKey());
|
||||||
|
/** @var Cart $cart */
|
||||||
|
$cart = Cart::query()->lockForUpdate()->findOrFail($cart->getKey());
|
||||||
|
|
||||||
|
if ($purchase->stock_reservation_id === null
|
||||||
|
|| $cart->current_stock_reservation_id !== $purchase->stock_reservation_id) {
|
||||||
|
throw new \InvalidArgumentException('La compra y el carrito no comparten la reserva activa.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var StockReservation $reservation */
|
||||||
|
$reservation = StockReservation::query()
|
||||||
|
->lockForUpdate()
|
||||||
|
->findOrFail($purchase->stock_reservation_id);
|
||||||
|
$this->assertUsableCartReservation($reservation);
|
||||||
|
|
||||||
|
$purchase->update(['stock_reservation_id' => null]);
|
||||||
|
$cart->update(['current_purchase_id' => null]);
|
||||||
|
$reservation->update(['expires_at' => $this->expiration()]);
|
||||||
|
|
||||||
|
return $reservation->fresh('lines');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function assertCartReservationUsable(Cart $cart): void
|
||||||
|
{
|
||||||
|
DB::transaction(function () use ($cart): void {
|
||||||
|
/** @var Cart $cart */
|
||||||
|
$cart = Cart::query()->lockForUpdate()->findOrFail($cart->getKey());
|
||||||
|
if ($cart->current_stock_reservation_id === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var StockReservation $reservation */
|
||||||
|
$reservation = StockReservation::query()
|
||||||
|
->lockForUpdate()
|
||||||
|
->findOrFail($cart->current_stock_reservation_id);
|
||||||
|
$this->assertUsableCartReservation($reservation);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public function releaseCurrentCartReservation(
|
public function releaseCurrentCartReservation(
|
||||||
Cart $cart,
|
Cart $cart,
|
||||||
string $reason = self::REASON_CART_CHANGED,
|
string $reason = self::REASON_CART_CHANGED,
|
||||||
|
|
@ -318,6 +359,9 @@ class StockReservationService
|
||||||
if ($reservation->status !== StockReservation::STATUS_ACTIVE) {
|
if ($reservation->status !== StockReservation::STATUS_ACTIVE) {
|
||||||
throw new \InvalidArgumentException('La reserva de stock no está activa.');
|
throw new \InvalidArgumentException('La reserva de stock no está activa.');
|
||||||
}
|
}
|
||||||
|
if ($reservation->expires_at !== null && ! $reservation->expires_at->isFuture()) {
|
||||||
|
throw new StockReservationExpiredException;
|
||||||
|
}
|
||||||
|
|
||||||
$reservation->update(['expires_at' => $expiresAt]);
|
$reservation->update(['expires_at' => $expiresAt]);
|
||||||
});
|
});
|
||||||
|
|
@ -420,9 +464,24 @@ class StockReservationService
|
||||||
'release_reason' => $status === StockReservation::STATUS_RELEASED ? $reason : null,
|
'release_reason' => $status === StockReservation::STATUS_RELEASED ? $reason : null,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Cart::query()
|
if ($status === StockReservation::STATUS_RELEASED) {
|
||||||
->where('current_stock_reservation_id', $reservation->getKey())
|
Cart::query()
|
||||||
->update(['current_stock_reservation_id' => null]);
|
->where('current_stock_reservation_id', $reservation->getKey())
|
||||||
|
->update(['current_stock_reservation_id' => null]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function assertUsableCartReservation(StockReservation $reservation): void
|
||||||
|
{
|
||||||
|
if ($reservation->status === StockReservation::STATUS_EXPIRED
|
||||||
|
|| ($reservation->expires_at !== null && ! $reservation->expires_at->isFuture())) {
|
||||||
|
throw new StockReservationExpiredException;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($reservation->status !== StockReservation::STATUS_ACTIVE
|
||||||
|
|| $reservation->expires_at === null) {
|
||||||
|
throw new \InvalidArgumentException('La reserva de stock no está disponible para operar el carrito.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function expiration(): Carbon
|
private function expiration(): Carbon
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ namespace App\Domains\Purchase\Services\Checkout;
|
||||||
use App\Domains\Cart\Models\Cart;
|
use App\Domains\Cart\Models\Cart;
|
||||||
use App\Domains\Catalog\Models\StockReservation;
|
use App\Domains\Catalog\Models\StockReservation;
|
||||||
use App\Domains\Catalog\Services\StockReservationService;
|
use App\Domains\Catalog\Services\StockReservationService;
|
||||||
|
use App\Domains\Purchase\Exceptions\PurchaseExpiredException;
|
||||||
use App\Domains\Purchase\Models\Purchase;
|
use App\Domains\Purchase\Models\Purchase;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Validation\ValidationException;
|
use Illuminate\Validation\ValidationException;
|
||||||
|
|
@ -42,6 +43,11 @@ class ReleaseCheckoutService
|
||||||
): Purchase {
|
): Purchase {
|
||||||
$purchase = $this->lockPurchase($purchase);
|
$purchase = $this->lockPurchase($purchase);
|
||||||
|
|
||||||
|
if ($purchase->status === Purchase::STATUS_EXPIRED
|
||||||
|
&& $targetStatus !== Purchase::STATUS_EXPIRED) {
|
||||||
|
throw new PurchaseExpiredException;
|
||||||
|
}
|
||||||
|
|
||||||
if ($purchase->status === Purchase::STATUS_PAID) {
|
if ($purchase->status === Purchase::STATUS_PAID) {
|
||||||
if ($targetStatus === Purchase::STATUS_EXPIRED) {
|
if ($targetStatus === Purchase::STATUS_EXPIRED) {
|
||||||
return $this->loadPurchase($purchase);
|
return $this->loadPurchase($purchase);
|
||||||
|
|
@ -66,6 +72,18 @@ class ReleaseCheckoutService
|
||||||
|
|
||||||
$cart = $purchase->cart()->withTrashed()->lockForUpdate()->first();
|
$cart = $purchase->cart()->withTrashed()->lockForUpdate()->first();
|
||||||
|
|
||||||
|
if ($targetStatus === Purchase::STATUS_CANCELLED
|
||||||
|
&& $cart?->status === 'active'
|
||||||
|
&& in_array($purchase->status, [
|
||||||
|
Purchase::STATUS_CREATED,
|
||||||
|
Purchase::STATUS_PENDING_PAYMENT,
|
||||||
|
], true)) {
|
||||||
|
$this->reservations->returnToCart($purchase, $cart);
|
||||||
|
$purchase->update(['status' => Purchase::STATUS_CANCELLED]);
|
||||||
|
|
||||||
|
return $this->loadPurchase($purchase);
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
$targetStatus === Purchase::STATUS_EXPIRED
|
$targetStatus === Purchase::STATUS_EXPIRED
|
||||||
&& (! in_array($purchase->status, [
|
&& (! in_array($purchase->status, [
|
||||||
|
|
@ -112,17 +130,17 @@ class ReleaseCheckoutService
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($cart->status === 'active') {
|
if ($cart->status === 'active') {
|
||||||
|
$cartUpdate = [
|
||||||
|
'current_purchase_id' => null,
|
||||||
|
];
|
||||||
|
if ($targetStatus !== Purchase::STATUS_EXPIRED) {
|
||||||
|
$cartUpdate['current_stock_reservation_id'] = null;
|
||||||
|
}
|
||||||
|
|
||||||
Cart::query()
|
Cart::query()
|
||||||
->whereKey($cart->getKey())
|
->whereKey($cart->getKey())
|
||||||
->where('current_purchase_id', $purchase->getKey())
|
->where('current_purchase_id', $purchase->getKey())
|
||||||
->update([
|
->update($cartUpdate);
|
||||||
'current_purchase_id' => null,
|
|
||||||
'current_stock_reservation_id' => null,
|
|
||||||
]);
|
|
||||||
|
|
||||||
if ($targetStatus === Purchase::STATUS_CANCELLED) {
|
|
||||||
$this->reservations->syncCart($cart);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -314,17 +314,10 @@ class StartCheckoutService
|
||||||
Purchase::STATUS_CREATED,
|
Purchase::STATUS_CREATED,
|
||||||
Purchase::STATUS_PENDING_PAYMENT,
|
Purchase::STATUS_PENDING_PAYMENT,
|
||||||
], true)) {
|
], true)) {
|
||||||
|
$this->reservations->returnToCart($currentPurchase, $cart);
|
||||||
$currentPurchase->update([
|
$currentPurchase->update([
|
||||||
'status' => Purchase::STATUS_SUPERSEDED,
|
'status' => Purchase::STATUS_SUPERSEDED,
|
||||||
]);
|
]);
|
||||||
$this->reservations->releaseForPurchase(
|
|
||||||
$currentPurchase,
|
|
||||||
reason: StockReservationService::REASON_PURCHASE_SUPERSEDED,
|
|
||||||
);
|
|
||||||
$cart->update([
|
|
||||||
'current_purchase_id' => null,
|
|
||||||
'current_stock_reservation_id' => null,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $cart;
|
return $cart;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Domains\Auth\Exceptions\AccountLockedException;
|
use App\Domains\Auth\Exceptions\AccountLockedException;
|
||||||
|
use App\Domains\Catalog\Exceptions\StockReservationExpiredException;
|
||||||
use App\Domains\Purchase\Exceptions\InsufficientStockException;
|
use App\Domains\Purchase\Exceptions\InsufficientStockException;
|
||||||
use App\Domains\Purchase\Exceptions\PurchaseExpiredException;
|
use App\Domains\Purchase\Exceptions\PurchaseExpiredException;
|
||||||
use App\Domains\Purchase\Exceptions\PurchaseLimitExceededException;
|
use App\Domains\Purchase\Exceptions\PurchaseLimitExceededException;
|
||||||
|
|
@ -113,6 +114,16 @@ return Application::configure(basePath: dirname(__DIR__))
|
||||||
'message' => $exception->getMessage(),
|
'message' => $exception->getMessage(),
|
||||||
], 422);
|
], 422);
|
||||||
});
|
});
|
||||||
|
$exceptions->render(function (StockReservationExpiredException $exception, Request $request) {
|
||||||
|
if (! $request->is('api/*')) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'code' => 'stock_reservation.expired',
|
||||||
|
'message' => $exception->getMessage(),
|
||||||
|
], 422);
|
||||||
|
});
|
||||||
$exceptions->render(function (ModelNotFoundException $exception, Request $request) {
|
$exceptions->render(function (ModelNotFoundException $exception, Request $request) {
|
||||||
if (! $request->is('api/*')) {
|
if (! $request->is('api/*')) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,9 @@ return [
|
||||||
'bundle_variant_forbidden' => 'A bundle cannot have a variant.',
|
'bundle_variant_forbidden' => 'A bundle cannot have a variant.',
|
||||||
'empty_bundle' => 'The bundle has no components.',
|
'empty_bundle' => 'The bundle has no components.',
|
||||||
'variant_required' => 'You must select a variant for this item.',
|
'variant_required' => 'You must select a variant for this item.',
|
||||||
|
'reservation_expired' => 'The stock reservation has expired. Abandon this cart to start a new one.',
|
||||||
|
'reservation_not_expired' => 'The cart can only be restarted after its stock reservation expires.',
|
||||||
|
'restarted' => 'The expired cart was abandoned. You can start a new one.',
|
||||||
],
|
],
|
||||||
'purchase' => [
|
'purchase' => [
|
||||||
'expired' => 'The purchase has expired. Please start a new purchase.',
|
'expired' => 'The purchase has expired. Please start a new purchase.',
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,9 @@ return [
|
||||||
'bundle_variant_forbidden' => 'Un bundle no admite una variante.',
|
'bundle_variant_forbidden' => 'Un bundle no admite una variante.',
|
||||||
'empty_bundle' => 'El bundle no tiene componentes.',
|
'empty_bundle' => 'El bundle no tiene componentes.',
|
||||||
'variant_required' => 'Debe seleccionar una variante para este ítem.',
|
'variant_required' => 'Debe seleccionar una variante para este ítem.',
|
||||||
|
'reservation_expired' => 'La reserva de stock venció. Abandoná este carrito para comenzar uno nuevo.',
|
||||||
|
'reservation_not_expired' => 'El carrito sólo puede reiniciarse cuando su reserva de stock está vencida.',
|
||||||
|
'restarted' => 'Carrito vencido abandonado. Podés comenzar uno nuevo.',
|
||||||
],
|
],
|
||||||
'purchase' => [
|
'purchase' => [
|
||||||
'expired' => "La compra venci\u{00F3}. Inici\u{00E1} una nueva compra.",
|
'expired' => "La compra venci\u{00F3}. Inici\u{00E1} una nueva compra.",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue