refactor(cart): own checkout item editing
This commit is contained in:
@@ -572,6 +572,113 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"response": []
|
"response": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Prepare Checkout Cart Editing Cart",
|
||||||
|
"request": {
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Accept",
|
||||||
|
"value": "application/json",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Ruta Laravel: `POST /api/tenants/{tenant:codigo}/checkout-carts/{cart}/edit`\n\nControlador: `App\\Domains\\Cart\\Controllers\\CartController@prepareCheckoutEditing`\n\nRequiere autenticación Sanctum.",
|
||||||
|
"url": {
|
||||||
|
"raw": "{{base_url}}/api/tenants/{{tenant_code}}/checkout-carts/{{cart}}/edit",
|
||||||
|
"host": [
|
||||||
|
"{{base_url}}"
|
||||||
|
],
|
||||||
|
"path": [
|
||||||
|
"api",
|
||||||
|
"tenants",
|
||||||
|
"{{tenant_code}}",
|
||||||
|
"checkout-carts",
|
||||||
|
"{{cart}}",
|
||||||
|
"edit"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "[]",
|
||||||
|
"options": {
|
||||||
|
"raw": {
|
||||||
|
"language": "json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"auth": {
|
||||||
|
"type": "bearer",
|
||||||
|
"bearer": [
|
||||||
|
{
|
||||||
|
"key": "token",
|
||||||
|
"value": "{{token}}",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Update Checkout Cart Item Cart",
|
||||||
|
"request": {
|
||||||
|
"method": "PATCH",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Accept",
|
||||||
|
"value": "application/json",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Ruta Laravel: `PATCH /api/tenants/{tenant:codigo}/checkout-carts/{cart}/items/{cartItem}`\n\nControlador: `App\\Domains\\Cart\\Controllers\\CartController@updateCheckoutItem`\n\nRequiere autenticación Sanctum.",
|
||||||
|
"url": {
|
||||||
|
"raw": "{{base_url}}/api/tenants/{{tenant_code}}/checkout-carts/{{cart}}/items/{{cart_item_id}}",
|
||||||
|
"host": [
|
||||||
|
"{{base_url}}"
|
||||||
|
],
|
||||||
|
"path": [
|
||||||
|
"api",
|
||||||
|
"tenants",
|
||||||
|
"{{tenant_code}}",
|
||||||
|
"checkout-carts",
|
||||||
|
"{{cart}}",
|
||||||
|
"items",
|
||||||
|
"{{cart_item_id}}"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\n \"cantidad\": 2\n}",
|
||||||
|
"options": {
|
||||||
|
"raw": {
|
||||||
|
"language": "json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"auth": {
|
||||||
|
"type": "bearer",
|
||||||
|
"bearer": [
|
||||||
|
{
|
||||||
|
"key": "token",
|
||||||
|
"value": "{{token}}",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -1115,99 +1222,6 @@
|
|||||||
},
|
},
|
||||||
"response": []
|
"response": []
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "Prepare Item Editing Purchase",
|
|
||||||
"request": {
|
|
||||||
"method": "POST",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "Accept",
|
|
||||||
"value": "application/json",
|
|
||||||
"type": "text"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Ruta Laravel: `POST /api/tenants/{tenant:codigo}/compras/{compra}/edit-items`\n\nControlador: `App\\Domains\\Purchase\\Controllers\\PurchaseController@prepareItemEditing`\n\nRequiere autenticación Sanctum.",
|
|
||||||
"url": {
|
|
||||||
"raw": "{{base_url}}/api/tenants/{{tenant_code}}/compras/{{purchase_id}}/edit-items",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"tenants",
|
|
||||||
"{{tenant_code}}",
|
|
||||||
"compras",
|
|
||||||
"{{purchase_id}}",
|
|
||||||
"edit-items"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"auth": {
|
|
||||||
"type": "bearer",
|
|
||||||
"bearer": [
|
|
||||||
{
|
|
||||||
"key": "token",
|
|
||||||
"value": "{{token}}",
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Update Item Quantity Purchase",
|
|
||||||
"request": {
|
|
||||||
"method": "PATCH",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "Accept",
|
|
||||||
"value": "application/json",
|
|
||||||
"type": "text"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Content-Type",
|
|
||||||
"value": "application/json",
|
|
||||||
"type": "text"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Ruta Laravel: `PATCH /api/tenants/{tenant:codigo}/compras/{compra}/items/{item}`\n\nControlador: `App\\Domains\\Purchase\\Controllers\\PurchaseController@updateItemQuantity`\n\nRequiere autenticación Sanctum.",
|
|
||||||
"url": {
|
|
||||||
"raw": "{{base_url}}/api/tenants/{{tenant_code}}/compras/{{purchase_id}}/items/{{purchase_item_id}}",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"tenants",
|
|
||||||
"{{tenant_code}}",
|
|
||||||
"compras",
|
|
||||||
"{{purchase_id}}",
|
|
||||||
"items",
|
|
||||||
"{{purchase_item_id}}"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"body": {
|
|
||||||
"mode": "raw",
|
|
||||||
"raw": "{\n \"quantity\": 2\n}",
|
|
||||||
"options": {
|
|
||||||
"raw": {
|
|
||||||
"language": "json"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"auth": {
|
|
||||||
"type": "bearer",
|
|
||||||
"bearer": [
|
|
||||||
{
|
|
||||||
"key": "token",
|
|
||||||
"value": "{{token}}",
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "Create Payment Intent Purchase",
|
"name": "Create Payment Intent Purchase",
|
||||||
"request": {
|
"request": {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Domains\Cart\Controllers;
|
namespace App\Domains\Cart\Controllers;
|
||||||
|
|
||||||
|
use App\Domains\Cart\Models\Cart;
|
||||||
use App\Domains\Cart\Models\CartItem;
|
use App\Domains\Cart\Models\CartItem;
|
||||||
use App\Domains\Cart\Requests\AddCartItemRequest;
|
use App\Domains\Cart\Requests\AddCartItemRequest;
|
||||||
use App\Domains\Cart\Requests\UpdateCartItemQuantityRequest;
|
use App\Domains\Cart\Requests\UpdateCartItemQuantityRequest;
|
||||||
@@ -77,6 +78,46 @@ class CartController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function updateCheckoutItem(
|
||||||
|
UpdateCartItemQuantityRequest $request,
|
||||||
|
Tenant $tenant,
|
||||||
|
Cart $cart,
|
||||||
|
CartItem $cartItem,
|
||||||
|
): CartResource {
|
||||||
|
$updatesVariant = $request->exists('variant_id');
|
||||||
|
|
||||||
|
return CartResource::make(
|
||||||
|
$this->cartService->updateCheckoutItem(
|
||||||
|
$tenant,
|
||||||
|
$request,
|
||||||
|
$cart,
|
||||||
|
$cartItem->getKey(),
|
||||||
|
(int) $request->validated('cantidad'),
|
||||||
|
$updatesVariant
|
||||||
|
? ($request->validated('variant_id') !== null
|
||||||
|
? (int) $request->validated('variant_id')
|
||||||
|
: null)
|
||||||
|
: $cartItem->variant_id,
|
||||||
|
$updatesVariant,
|
||||||
|
),
|
||||||
|
)->additional([
|
||||||
|
'code' => $updatesVariant ? 'cart.item_updated' : 'cart.quantity_updated',
|
||||||
|
'message' => $updatesVariant
|
||||||
|
? __('api.cart.item_updated')
|
||||||
|
: __('api.cart.quantity_updated'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function prepareCheckoutEditing(
|
||||||
|
Request $request,
|
||||||
|
Tenant $tenant,
|
||||||
|
Cart $cart,
|
||||||
|
): CartResource {
|
||||||
|
return CartResource::make(
|
||||||
|
$this->cartService->prepareCheckoutEditing($tenant, $request, $cart),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public function removeItem(Request $request, Tenant $tenant, CartItem $cartItem): CartResource
|
public function removeItem(Request $request, Tenant $tenant, CartItem $cartItem): CartResource
|
||||||
{
|
{
|
||||||
return CartResource::make(
|
return CartResource::make(
|
||||||
|
|||||||
@@ -143,6 +143,7 @@ class Cart extends Model
|
|||||||
int $quantity,
|
int $quantity,
|
||||||
?int $variantId = null,
|
?int $variantId = null,
|
||||||
bool $updateVariant = false,
|
bool $updateVariant = false,
|
||||||
|
?int $excludedPurchaseId = null,
|
||||||
): CartItem {
|
): CartItem {
|
||||||
if ($quantity <= 0) {
|
if ($quantity <= 0) {
|
||||||
throw ValidationException::withMessages([
|
throw ValidationException::withMessages([
|
||||||
@@ -155,6 +156,7 @@ class Cart extends Model
|
|||||||
$quantity,
|
$quantity,
|
||||||
$variantId,
|
$variantId,
|
||||||
$updateVariant,
|
$updateVariant,
|
||||||
|
$excludedPurchaseId,
|
||||||
): CartItem {
|
): CartItem {
|
||||||
/** @var CartItem $item */
|
/** @var CartItem $item */
|
||||||
$item = $this->items()
|
$item = $this->items()
|
||||||
@@ -182,6 +184,7 @@ class Cart extends Model
|
|||||||
$this->assertUserPurchaseLimit(
|
$this->assertUserPurchaseLimit(
|
||||||
$nextSelection,
|
$nextSelection,
|
||||||
$otherVariantsQuantity + $quantity,
|
$otherVariantsQuantity + $quantity,
|
||||||
|
$excludedPurchaseId,
|
||||||
);
|
);
|
||||||
|
|
||||||
app(StockReservationService::class)->release($item, $currentSelection, $item->cantidad);
|
app(StockReservationService::class)->release($item, $currentSelection, $item->cantidad);
|
||||||
@@ -228,6 +231,7 @@ class Cart extends Model
|
|||||||
$this->assertUserPurchaseLimit(
|
$this->assertUserPurchaseLimit(
|
||||||
$currentSelection,
|
$currentSelection,
|
||||||
$otherVariantsQuantity + $quantity,
|
$otherVariantsQuantity + $quantity,
|
||||||
|
$excludedPurchaseId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,6 +354,7 @@ class Cart extends Model
|
|||||||
private function assertUserPurchaseLimit(
|
private function assertUserPurchaseLimit(
|
||||||
CatalogItem|Variant $selectedItem,
|
CatalogItem|Variant $selectedItem,
|
||||||
int $cartQuantity,
|
int $cartQuantity,
|
||||||
|
?int $excludedPurchaseId = null,
|
||||||
): void {
|
): void {
|
||||||
if ($this->user_id === null) {
|
if ($this->user_id === null) {
|
||||||
return;
|
return;
|
||||||
@@ -363,6 +368,7 @@ class Cart extends Model
|
|||||||
$catalogItem,
|
$catalogItem,
|
||||||
$this->user_id,
|
$this->user_id,
|
||||||
$cartQuantity,
|
$cartQuantity,
|
||||||
|
$excludedPurchaseId,
|
||||||
field: 'cantidad',
|
field: 'cantidad',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,15 +4,23 @@ 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\Services\StockReservationService;
|
||||||
|
use App\Domains\Purchase\Models\Purchase;
|
||||||
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 Symfony\Component\HttpFoundation\Cookie;
|
use Symfony\Component\HttpFoundation\Cookie;
|
||||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||||
|
|
||||||
class CartService
|
class CartService
|
||||||
{
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly StockReservationService $reservations,
|
||||||
|
) {}
|
||||||
|
|
||||||
public function show(Tenant $tenant, Request $request): Cart
|
public function show(Tenant $tenant, Request $request): Cart
|
||||||
{
|
{
|
||||||
$resolvedIdentity = $this->resolveIdentity($request);
|
$resolvedIdentity = $this->resolveIdentity($request);
|
||||||
@@ -66,6 +74,139 @@ class CartService
|
|||||||
return $this->loadCart($cart);
|
return $this->loadCart($cart);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function updateCheckoutItem(
|
||||||
|
Tenant $tenant,
|
||||||
|
Request $request,
|
||||||
|
Cart $cart,
|
||||||
|
int $cartItemId,
|
||||||
|
int $quantity,
|
||||||
|
?int $variantId,
|
||||||
|
bool $updateVariant,
|
||||||
|
): Cart {
|
||||||
|
$user = $request->user() ?? Auth::guard('sanctum')->user();
|
||||||
|
|
||||||
|
if (! $user instanceof User) {
|
||||||
|
throw new NotFoundHttpException('Checkout cart not found.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return DB::transaction(function () use (
|
||||||
|
$tenant,
|
||||||
|
$user,
|
||||||
|
$cart,
|
||||||
|
$cartItemId,
|
||||||
|
$quantity,
|
||||||
|
$variantId,
|
||||||
|
$updateVariant,
|
||||||
|
): Cart {
|
||||||
|
/** @var Purchase|null $purchase */
|
||||||
|
$purchase = Purchase::query()
|
||||||
|
->where('cart_id', $cart->getKey())
|
||||||
|
->where('tenant_codigo', $tenant->codigo)
|
||||||
|
->where('user_id', $user->getKey())
|
||||||
|
->where('status', Purchase::STATUS_CREATED)
|
||||||
|
->whereDoesntHave('items')
|
||||||
|
->lockForUpdate()
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if ($purchase === null) {
|
||||||
|
throw new NotFoundHttpException('Checkout cart not found.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($purchase->expires_at !== null && $purchase->expires_at->isPast()) {
|
||||||
|
throw ValidationException::withMessages([
|
||||||
|
'cart' => __('api.purchase.not_editable'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var Cart|null $checkoutCart */
|
||||||
|
$checkoutCart = Cart::query()
|
||||||
|
->whereKey($cart->getKey())
|
||||||
|
->where('tenant_codigo', $tenant->codigo)
|
||||||
|
->where('user_id', $user->getKey())
|
||||||
|
->where('status', 'checkout')
|
||||||
|
->lockForUpdate()
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if ($checkoutCart === null) {
|
||||||
|
throw new NotFoundHttpException('Checkout cart not found.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$checkoutCart->updateItem(
|
||||||
|
$cartItemId,
|
||||||
|
$quantity,
|
||||||
|
$variantId,
|
||||||
|
$updateVariant,
|
||||||
|
$purchase->getKey(),
|
||||||
|
);
|
||||||
|
$purchase->update(['total' => $checkoutCart->getTotalAmount()]);
|
||||||
|
|
||||||
|
return $this->loadCart($checkoutCart);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function prepareCheckoutEditing(
|
||||||
|
Tenant $tenant,
|
||||||
|
Request $request,
|
||||||
|
Cart $cart,
|
||||||
|
): Cart {
|
||||||
|
$user = $request->user() ?? Auth::guard('sanctum')->user();
|
||||||
|
|
||||||
|
if (! $user instanceof User) {
|
||||||
|
throw new NotFoundHttpException('Checkout cart not found.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return DB::transaction(function () use ($tenant, $user, $cart): Cart {
|
||||||
|
/** @var Purchase|null $purchase */
|
||||||
|
$purchase = Purchase::query()
|
||||||
|
->where('cart_id', $cart->getKey())
|
||||||
|
->where('tenant_codigo', $tenant->codigo)
|
||||||
|
->where('user_id', $user->getKey())
|
||||||
|
->whereIn('status', [
|
||||||
|
Purchase::STATUS_CREATED,
|
||||||
|
Purchase::STATUS_PENDING_PAYMENT,
|
||||||
|
])
|
||||||
|
->whereDoesntHave('items')
|
||||||
|
->lockForUpdate()
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if ($purchase === null) {
|
||||||
|
throw new NotFoundHttpException('Checkout cart not found.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($purchase->expires_at !== null && $purchase->expires_at->isPast()) {
|
||||||
|
throw ValidationException::withMessages([
|
||||||
|
'cart' => __('api.purchase.not_editable'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var Cart|null $checkoutCart */
|
||||||
|
$checkoutCart = Cart::query()
|
||||||
|
->whereKey($cart->getKey())
|
||||||
|
->where('tenant_codigo', $tenant->codigo)
|
||||||
|
->where('user_id', $user->getKey())
|
||||||
|
->where('status', 'checkout')
|
||||||
|
->lockForUpdate()
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if ($checkoutCart === null) {
|
||||||
|
throw new NotFoundHttpException('Checkout cart not found.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$purchase->telepagosQr()->delete();
|
||||||
|
$purchase->update([
|
||||||
|
'status' => Purchase::STATUS_CREATED,
|
||||||
|
'payment_method' => null,
|
||||||
|
'transfer_payer_dni' => null,
|
||||||
|
'expires_at' => now()->addMinutes(
|
||||||
|
max(1, (int) config('purchase.checkout_expiration_minutes', 30)),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
$this->reservations->syncPurchaseExpiration($purchase);
|
||||||
|
|
||||||
|
return $this->loadCart($checkoutCart);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public function removeItem(Tenant $tenant, Request $request, int $cartItemId): Cart
|
public function removeItem(Tenant $tenant, Request $request, int $cartItemId): Cart
|
||||||
{
|
{
|
||||||
$identity = $this->requireIdentity($request);
|
$identity = $this->requireIdentity($request);
|
||||||
|
|||||||
@@ -10,3 +10,10 @@ Route::prefix('tenants/{tenant:codigo}')
|
|||||||
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']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Route::prefix('tenants/{tenant:codigo}')
|
||||||
|
->middleware('auth:sanctum')
|
||||||
|
->group(function (): void {
|
||||||
|
Route::post('checkout-carts/{cart}/edit', [CartController::class, 'prepareCheckoutEditing']);
|
||||||
|
Route::patch('checkout-carts/{cart}/items/{cartItem}', [CartController::class, 'updateCheckoutItem']);
|
||||||
|
});
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ use App\Domains\Purchase\Models\Purchase;
|
|||||||
use App\Domains\Purchase\Requests\PaymentIntentRequest;
|
use App\Domains\Purchase\Requests\PaymentIntentRequest;
|
||||||
use App\Domains\Purchase\Requests\StartCheckoutRequest;
|
use App\Domains\Purchase\Requests\StartCheckoutRequest;
|
||||||
use App\Domains\Purchase\Requests\UpdatePurchaseCustomerRequest;
|
use App\Domains\Purchase\Requests\UpdatePurchaseCustomerRequest;
|
||||||
use App\Domains\Purchase\Requests\UpdatePurchaseItemQuantityRequest;
|
|
||||||
use App\Domains\Purchase\Resources\PurchaseResource;
|
use App\Domains\Purchase\Resources\PurchaseResource;
|
||||||
use App\Domains\Purchase\Services\CheckoutService;
|
use App\Domains\Purchase\Services\CheckoutService;
|
||||||
use App\Domains\Tenant\Models\Tenant;
|
use App\Domains\Tenant\Models\Tenant;
|
||||||
@@ -83,37 +82,6 @@ class PurchaseController extends Controller
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function updateItemQuantity(
|
|
||||||
UpdatePurchaseItemQuantityRequest $request,
|
|
||||||
Tenant $tenant,
|
|
||||||
Purchase $compra,
|
|
||||||
int $item,
|
|
||||||
CheckoutService $checkoutService,
|
|
||||||
): PurchaseResource {
|
|
||||||
$compra = $this->resolveScopedPurchase($tenant, $request->user()->id, $compra);
|
|
||||||
|
|
||||||
return PurchaseResource::make(
|
|
||||||
$checkoutService->updateItemQuantity(
|
|
||||||
$compra,
|
|
||||||
$item,
|
|
||||||
(int) $request->validated('quantity'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function prepareItemEditing(
|
|
||||||
Request $request,
|
|
||||||
Tenant $tenant,
|
|
||||||
Purchase $compra,
|
|
||||||
CheckoutService $checkoutService,
|
|
||||||
): PurchaseResource {
|
|
||||||
$compra = $this->resolveScopedPurchase($tenant, $request->user()->id, $compra);
|
|
||||||
|
|
||||||
return PurchaseResource::make(
|
|
||||||
$checkoutService->prepareItemEditing($compra),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function paymentIntent(
|
public function paymentIntent(
|
||||||
PaymentIntentRequest $request,
|
PaymentIntentRequest $request,
|
||||||
Tenant $tenant,
|
Tenant $tenant,
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Domains\Purchase\Requests;
|
|
||||||
|
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
|
||||||
|
|
||||||
class UpdatePurchaseItemQuantityRequest extends FormRequest
|
|
||||||
{
|
|
||||||
public function authorize(): bool
|
|
||||||
{
|
|
||||||
return $this->user() !== null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function rules(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'quantity' => ['required', 'integer', 'min:1', 'max:100'],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,23 +2,12 @@
|
|||||||
|
|
||||||
namespace App\Domains\Purchase\Services\Checkout;
|
namespace App\Domains\Purchase\Services\Checkout;
|
||||||
|
|
||||||
use App\Domains\Cart\Models\CartItem;
|
|
||||||
use App\Domains\Catalog\Models\Variant;
|
|
||||||
use App\Domains\Catalog\Services\StockReservationService;
|
|
||||||
use App\Domains\Purchase\Models\Purchase;
|
use App\Domains\Purchase\Models\Purchase;
|
||||||
use App\Domains\Purchase\Services\UserPurchaseLimitService;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Validation\ValidationException;
|
use Illuminate\Validation\ValidationException;
|
||||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
|
||||||
|
|
||||||
class EditCheckoutService
|
class EditCheckoutService
|
||||||
{
|
{
|
||||||
public function __construct(
|
|
||||||
private readonly StockReservationService $reservations,
|
|
||||||
private readonly UserPurchaseLimitService $purchaseLimits,
|
|
||||||
private readonly CatalogSelectionResolver $selections,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
/** @param array<string, string> $customerData */
|
/** @param array<string, string> $customerData */
|
||||||
public function updateCustomer(Purchase $purchase, array $customerData): Purchase
|
public function updateCustomer(Purchase $purchase, array $customerData): Purchase
|
||||||
{
|
{
|
||||||
@@ -32,112 +21,6 @@ class EditCheckoutService
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function updateItemQuantity(
|
|
||||||
Purchase $purchase,
|
|
||||||
int $itemId,
|
|
||||||
int $quantity,
|
|
||||||
): Purchase {
|
|
||||||
return DB::transaction(function () use ($purchase, $itemId, $quantity): Purchase {
|
|
||||||
$purchase = $this->lockPurchase($purchase);
|
|
||||||
|
|
||||||
if ($purchase->status !== Purchase::STATUS_CREATED || $this->hasExpired($purchase)) {
|
|
||||||
throw ValidationException::withMessages([
|
|
||||||
'purchase' => __('api.purchase.not_editable'),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->updateCartItemQuantity($purchase, $itemId, $quantity);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public function prepareItemEditing(Purchase $purchase): Purchase
|
|
||||||
{
|
|
||||||
return DB::transaction(function () use ($purchase): Purchase {
|
|
||||||
$purchase = $this->lockPurchase($purchase);
|
|
||||||
$this->assertEditable($purchase);
|
|
||||||
|
|
||||||
$purchase->telepagosQr()->delete();
|
|
||||||
$purchase->update([
|
|
||||||
'status' => Purchase::STATUS_CREATED,
|
|
||||||
'payment_method' => null,
|
|
||||||
'transfer_payer_dni' => null,
|
|
||||||
'expires_at' => now()->addMinutes(
|
|
||||||
max(1, (int) config('purchase.checkout_expiration_minutes', 30)),
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->attachCartReservations($purchase);
|
|
||||||
|
|
||||||
return $this->loadPurchase($purchase);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private function updateCartItemQuantity(Purchase $purchase, int $itemId, int $quantity): Purchase
|
|
||||||
{
|
|
||||||
$cart = $purchase->cart()->lockForUpdate()->first();
|
|
||||||
if ($cart === null || $cart->status !== 'checkout') {
|
|
||||||
throw new NotFoundHttpException('Checkout cart not found.');
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var CartItem|null $cartItem */
|
|
||||||
$cartItem = $cart->items()->whereKey($itemId)->lockForUpdate()->first();
|
|
||||||
if ($cartItem === null) {
|
|
||||||
throw new NotFoundHttpException('Checkout item not found.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$selection = $this->selections->resolve(
|
|
||||||
$purchase->tenant,
|
|
||||||
(int) $cartItem->catalog_item_id,
|
|
||||||
$cartItem->variant_id === null ? null : (int) $cartItem->variant_id,
|
|
||||||
'item',
|
|
||||||
);
|
|
||||||
$difference = $quantity - (int) $cartItem->cantidad;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if ($difference > 0) {
|
|
||||||
$otherItemQuantity = (int) $cart->items()
|
|
||||||
->where('catalog_item_id', $cartItem->catalog_item_id)
|
|
||||||
->whereKeyNot($cartItem->getKey())
|
|
||||||
->sum('cantidad');
|
|
||||||
$catalogItem = $selection instanceof Variant ? $selection->catalogItem : $selection;
|
|
||||||
$this->purchaseLimits->assertCanPurchase(
|
|
||||||
$catalogItem,
|
|
||||||
(int) $purchase->user_id,
|
|
||||||
$otherItemQuantity + $quantity,
|
|
||||||
$purchase->getKey(),
|
|
||||||
);
|
|
||||||
$this->reservations->reserve($cartItem, $selection, $difference);
|
|
||||||
} elseif ($difference < 0) {
|
|
||||||
$this->reservations->release($cartItem, $selection, abs($difference));
|
|
||||||
}
|
|
||||||
} catch (\InvalidArgumentException) {
|
|
||||||
throw ValidationException::withMessages([
|
|
||||||
'quantity' => __('api.purchase.insufficient_stock'),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$cartItem->update(['cantidad' => $quantity]);
|
|
||||||
$cart->unsetRelation('items');
|
|
||||||
$purchase->setRelation('cart', $cart);
|
|
||||||
$purchase->update(['total' => $cart->getTotalAmount()]);
|
|
||||||
|
|
||||||
return $this->loadPurchase($purchase);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function attachCartReservations(Purchase $purchase): void
|
|
||||||
{
|
|
||||||
$cartItems = $purchase->cart?->items()->lockForUpdate()->get() ?? collect();
|
|
||||||
foreach ($cartItems as $cartItem) {
|
|
||||||
$selection = $this->selections->resolve(
|
|
||||||
$purchase->tenant,
|
|
||||||
(int) $cartItem->catalog_item_id,
|
|
||||||
$cartItem->variant_id === null ? null : (int) $cartItem->variant_id,
|
|
||||||
'item',
|
|
||||||
);
|
|
||||||
$this->reservations->attachToPurchase($cartItem, $selection, $purchase);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function assertEditable(Purchase $purchase): void
|
private function assertEditable(Purchase $purchase): void
|
||||||
{
|
{
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -48,19 +48,6 @@ class CheckoutService
|
|||||||
return $this->editor->updateCustomer($purchase, $customerData);
|
return $this->editor->updateCustomer($purchase, $customerData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function updateItemQuantity(
|
|
||||||
Purchase $purchase,
|
|
||||||
int $itemId,
|
|
||||||
int $quantity,
|
|
||||||
): Purchase {
|
|
||||||
return $this->editor->updateItemQuantity($purchase, $itemId, $quantity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function prepareItemEditing(Purchase $purchase): Purchase
|
|
||||||
{
|
|
||||||
return $this->editor->prepareItemEditing($purchase);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function confirmPurchase(Purchase $purchase): void
|
public function confirmPurchase(Purchase $purchase): void
|
||||||
{
|
{
|
||||||
$this->completer->confirm($purchase);
|
$this->completer->confirm($purchase);
|
||||||
|
|||||||
@@ -16,14 +16,16 @@ Implementa el ciclo de compra y checkout: crea la cabecera de compra desde un ca
|
|||||||
`CheckoutService` es la fachada estable. Delega en:
|
`CheckoutService` es la fachada estable. Delega en:
|
||||||
|
|
||||||
- `StartCheckoutService`: inicia la compra desde el carrito o crea un carrito técnico para compra directa, sin crear todavía `PurchaseItem`.
|
- `StartCheckoutService`: inicia la compra desde el carrito o crea un carrito técnico para compra directa, sin crear todavía `PurchaseItem`.
|
||||||
- `EditCheckoutService`: modifica cliente o cantidades del carrito de checkout antes del cierre.
|
- `EditCheckoutService`: modifica los datos del comprador antes del cierre.
|
||||||
- `CompleteCheckoutService`: completa, envía a revisión o materializa los `PurchaseItem` al confirmar el pago.
|
- `CompleteCheckoutService`: completa, envía a revisión o materializa los `PurchaseItem` al confirmar el pago.
|
||||||
- `ReleaseCheckoutService`: cancela, vence y procesa vencimientos pendientes.
|
- `ReleaseCheckoutService`: cancela, vence y procesa vencimientos pendientes.
|
||||||
- `SourceCartService`: sincroniza, restaura o finaliza el carrito fuente.
|
- `SourceCartService`: restaura o finaliza el carrito fuente.
|
||||||
- `CatalogSelectionResolver` y `PurchaseItemSnapshotFactory`: resuelven selecciones y generan snapshots.
|
- `CatalogSelectionResolver` y `PurchaseItemSnapshotFactory`: resuelven selecciones y generan snapshots.
|
||||||
|
|
||||||
Durante `created` y `pending_payment`, `PurchaseResource` publica las líneas del carrito con `items_source=cart`; una compra materializada publica `items_source=purchase`. Los datos descriptivos y económicos del checkout se resuelven siempre desde el catálogo vigente.
|
Durante `created` y `pending_payment`, `PurchaseResource` publica las líneas del carrito con `items_source=cart`; una compra materializada publica `items_source=purchase`. Los datos descriptivos y económicos del checkout se resuelven siempre desde el catálogo vigente.
|
||||||
|
|
||||||
|
Las cantidades y variantes se editan mediante el dominio Cart. Los endpoints autenticados `POST /checkout-carts/{cart}/edit` y `PATCH /checkout-carts/{cart}/items/{cartItem}` validan que el carrito pertenezca al usuario y a una compra editable; Purchase no expone operaciones sobre líneas antes de la confirmación.
|
||||||
|
|
||||||
`UserPurchaseLimitService` controla límites de compra y `CheckoutService` conserva el punto de entrada para controladores e integraciones.
|
`UserPurchaseLimitService` controla límites de compra y `CheckoutService` conserva el punto de entrada para controladores e integraciones.
|
||||||
|
|
||||||
## Endpoints
|
## Endpoints
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ Route::prefix('tenants/{tenant:codigo}')->middleware('auth:sanctum')->group(func
|
|||||||
Route::get('compras', [PurchaseController::class, 'index']);
|
Route::get('compras', [PurchaseController::class, 'index']);
|
||||||
Route::post('compras/start-checkout', [PurchaseController::class, 'startCheckout']);
|
Route::post('compras/start-checkout', [PurchaseController::class, 'startCheckout']);
|
||||||
Route::get('compras/{compra}', [PurchaseController::class, 'show']);
|
Route::get('compras/{compra}', [PurchaseController::class, 'show']);
|
||||||
Route::post('compras/{compra}/edit-items', [PurchaseController::class, 'prepareItemEditing']);
|
|
||||||
Route::patch('compras/{compra}/items/{item}', [PurchaseController::class, 'updateItemQuantity']);
|
|
||||||
Route::patch('compras/{compra}/customer-data', [PurchaseController::class, 'updateCustomerData']);
|
Route::patch('compras/{compra}/customer-data', [PurchaseController::class, 'updateCustomerData']);
|
||||||
Route::post('compras/{compra}/payment-intent', [PurchaseController::class, 'paymentIntent']);
|
Route::post('compras/{compra}/payment-intent', [PurchaseController::class, 'paymentIntent']);
|
||||||
Route::post('compras/{compra}/complete', [PurchaseController::class, 'complete']);
|
Route::post('compras/{compra}/complete', [PurchaseController::class, 'complete']);
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ declare(strict_types=1);
|
|||||||
* Run from the backend root with:
|
* Run from the backend root with:
|
||||||
* php postman/generate-shopit-collection.php
|
* php postman/generate-shopit-collection.php
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$root = dirname(__DIR__);
|
$root = dirname(__DIR__);
|
||||||
chdir($root);
|
chdir($root);
|
||||||
|
|
||||||
@@ -38,7 +37,7 @@ function jsonBody(array $payload): array
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @param array<string, string> $fields
|
/** @param array<string, string> $fields
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
function formDataBody(array $fields, array $fileFields = []): array
|
function formDataBody(array $fields, array $fileFields = []): array
|
||||||
{
|
{
|
||||||
@@ -85,7 +84,8 @@ function bodyFor(string $method, string $uri): ?array
|
|||||||
'POST api/tenants/{tenant:codigo}/catalog-items/{catalogItem}/variant-options' => ['selected_values' => ['color' => 'azul'], 'cart_item_id' => '{{cart_item_id}}'],
|
'POST api/tenants/{tenant:codigo}/catalog-items/{catalogItem}/variant-options' => ['selected_values' => ['color' => 'azul'], 'cart_item_id' => '{{cart_item_id}}'],
|
||||||
'POST api/tenants/{tenant:codigo}/compras/start-checkout' => ['cart_id' => '{{cart_id}}'],
|
'POST api/tenants/{tenant:codigo}/compras/start-checkout' => ['cart_id' => '{{cart_id}}'],
|
||||||
'PATCH api/tenants/{tenant:codigo}/compras/{compra}/customer-data' => ['dni' => '30123456', 'telefono' => '+5491112345678', 'nombre_apellido' => 'Usuario Demo', 'email' => '{{user_email}}'],
|
'PATCH api/tenants/{tenant:codigo}/compras/{compra}/customer-data' => ['dni' => '30123456', 'telefono' => '+5491112345678', 'nombre_apellido' => 'Usuario Demo', 'email' => '{{user_email}}'],
|
||||||
'PATCH api/tenants/{tenant:codigo}/compras/{compra}/items/{item}' => ['quantity' => 2],
|
'PATCH api/tenants/{tenant:codigo}/checkout-carts/{cart}/items/{cartItem}' => ['cantidad' => 2],
|
||||||
|
'POST api/tenants/{tenant:codigo}/checkout-carts/{cart}/edit' => [],
|
||||||
'POST api/tenants/{tenant:codigo}/compras/{compra}/payment-intent' => ['method' => 'transfer', 'transfer_payer_dni' => '30123456'],
|
'POST api/tenants/{tenant:codigo}/compras/{compra}/payment-intent' => ['method' => 'transfer', 'transfer_payer_dni' => '30123456'],
|
||||||
'POST api/tenants/{tenant:codigo}/tickets/pdf' => ['ticket_ids' => [1]],
|
'POST api/tenants/{tenant:codigo}/tickets/pdf' => ['ticket_ids' => [1]],
|
||||||
'POST api/v1/adminapp/login' => ['email' => '{{admin_email}}', 'password' => '{{admin_password}}'],
|
'POST api/v1/adminapp/login' => ['email' => '{{admin_email}}', 'password' => '{{admin_password}}'],
|
||||||
@@ -245,10 +245,12 @@ function requestName(string $method, string $action, bool $multiMethod): string
|
|||||||
$verbs = [
|
$verbs = [
|
||||||
'index' => 'List', 'store' => 'Create', 'show' => 'Get', 'update' => 'Update',
|
'index' => 'List', 'store' => 'Create', 'show' => 'Get', 'update' => 'Update',
|
||||||
'destroy' => 'Delete', 'addItem' => 'Add Item', 'updateItemQuantity' => 'Update Item Quantity',
|
'destroy' => 'Delete', 'addItem' => 'Add Item', 'updateItemQuantity' => 'Update Item Quantity',
|
||||||
|
'updateCheckoutItem' => 'Update Checkout Cart Item',
|
||||||
|
'prepareCheckoutEditing' => 'Prepare Checkout Cart Editing',
|
||||||
'removeItem' => 'Remove Item', 'search' => 'Search', 'category' => 'Get Category',
|
'removeItem' => 'Remove Item', 'search' => 'Search', 'category' => 'Get Category',
|
||||||
'featuredGroupItems' => 'List Featured Group Items', 'variantOptions' => 'Get Variant Options',
|
'featuredGroupItems' => 'List Featured Group Items', 'variantOptions' => 'Get Variant Options',
|
||||||
'startCheckout' => 'Start Checkout', 'updateCustomerData' => 'Update Customer Data',
|
'startCheckout' => 'Start Checkout', 'updateCustomerData' => 'Update Customer Data',
|
||||||
'prepareItemEditing' => 'Prepare Item Editing', 'paymentIntent' => 'Create Payment Intent',
|
'paymentIntent' => 'Create Payment Intent',
|
||||||
'submitForReview' => 'Submit for Review', 'cancel' => 'Cancel', 'complete' => 'Complete',
|
'submitForReview' => 'Submit for Review', 'cancel' => 'Cancel', 'complete' => 'Complete',
|
||||||
'downloadPdf' => 'Download PDF', 'downloadModificationsPdf' => 'Download Modifications PDF',
|
'downloadPdf' => 'Download PDF', 'downloadModificationsPdf' => 'Download Modifications PDF',
|
||||||
'modifications' => 'List Modifications', 'tickets' => 'List Tickets', 'confirm' => 'Confirm',
|
'modifications' => 'List Modifications', 'tickets' => 'List Tickets', 'confirm' => 'Confirm',
|
||||||
|
|||||||
@@ -568,14 +568,12 @@ class StorePurchaseTest extends TestCase
|
|||||||
$itemId = $purchase->cart->items->firstOrFail()->id;
|
$itemId = $purchase->cart->items->firstOrFail()->id;
|
||||||
|
|
||||||
$this->actingAs($user, 'sanctum')
|
$this->actingAs($user, 'sanctum')
|
||||||
->patchJson("/api/tenants/sonder/compras/{$purchase->id}/items/{$itemId}", [
|
->patchJson("/api/tenants/sonder/checkout-carts/{$purchase->cart_id}/items/{$itemId}", [
|
||||||
'quantity' => 4,
|
'cantidad' => 4,
|
||||||
])
|
])
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertJsonPath('data.items.0.quantity', 4)
|
->assertJsonPath('data.items.0.cantidad', 4)
|
||||||
->assertJsonPath('data.items.0.line_total', '200.00')
|
->assertJsonPath('data.subtotal', '200.00');
|
||||||
->assertJsonPath('data.subtotal', '200.00')
|
|
||||||
->assertJsonPath('data.total', '200.00');
|
|
||||||
|
|
||||||
$this->assertDatabaseHas('inventories', [
|
$this->assertDatabaseHas('inventories', [
|
||||||
'id' => $variant->inventory_id,
|
'id' => $variant->inventory_id,
|
||||||
@@ -589,12 +587,17 @@ class StorePurchaseTest extends TestCase
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$this->actingAs($user, 'sanctum')
|
$this->actingAs($user, 'sanctum')
|
||||||
->patchJson("/api/tenants/sonder/compras/{$purchase->id}/items/{$itemId}", [
|
->patchJson("/api/tenants/sonder/checkout-carts/{$purchase->cart_id}/items/{$itemId}", [
|
||||||
'quantity' => 1,
|
'cantidad' => 1,
|
||||||
])
|
])
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertJsonPath('data.items.0.quantity', 1)
|
->assertJsonPath('data.items.0.cantidad', 1)
|
||||||
->assertJsonPath('data.total', '50.00');
|
->assertJsonPath('data.subtotal', '50.00');
|
||||||
|
|
||||||
|
$this->assertDatabaseHas('compras', [
|
||||||
|
'id' => $purchase->id,
|
||||||
|
'total' => '50.00',
|
||||||
|
]);
|
||||||
|
|
||||||
$this->assertDatabaseHas('inventories', [
|
$this->assertDatabaseHas('inventories', [
|
||||||
'id' => $variant->inventory_id,
|
'id' => $variant->inventory_id,
|
||||||
@@ -612,11 +615,11 @@ class StorePurchaseTest extends TestCase
|
|||||||
$itemId = $purchase->cart->items->firstOrFail()->id;
|
$itemId = $purchase->cart->items->firstOrFail()->id;
|
||||||
|
|
||||||
$this->actingAs($user, 'sanctum')
|
$this->actingAs($user, 'sanctum')
|
||||||
->patchJson("/api/tenants/sonder/compras/{$purchase->id}/items/{$itemId}", [
|
->patchJson("/api/tenants/sonder/checkout-carts/{$purchase->cart_id}/items/{$itemId}", [
|
||||||
'quantity' => 4,
|
'cantidad' => 4,
|
||||||
])
|
])
|
||||||
->assertUnprocessable()
|
->assertUnprocessable()
|
||||||
->assertJsonValidationErrors('quantity');
|
->assertJsonValidationErrors('cantidad');
|
||||||
|
|
||||||
$this->assertDatabaseHas('carrito_items', [
|
$this->assertDatabaseHas('carrito_items', [
|
||||||
'id' => $itemId,
|
'id' => $itemId,
|
||||||
@@ -644,10 +647,9 @@ class StorePurchaseTest extends TestCase
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$this->actingAs($user, 'sanctum')
|
$this->actingAs($user, 'sanctum')
|
||||||
->postJson("/api/tenants/sonder/compras/{$purchase->id}/edit-items")
|
->postJson("/api/tenants/sonder/checkout-carts/{$purchase->cart_id}/edit")
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertJsonPath('data.status', Purchase::STATUS_CREATED)
|
->assertJsonPath('data.status', 'checkout');
|
||||||
->assertJsonPath('data.payment_method', null);
|
|
||||||
|
|
||||||
$this->assertDatabaseHas('compras', [
|
$this->assertDatabaseHas('compras', [
|
||||||
'id' => $purchase->id,
|
'id' => $purchase->id,
|
||||||
|
|||||||
Reference in New Issue
Block a user