瀏覽代碼

修复购物车合并

chengwl 11 小時之前
父節點
當前提交
d4c6a0fe5c

+ 43 - 45
packages/Webkul/BagistoApi/src/State/CartTokenProcessor.php

@@ -1027,64 +1027,62 @@ class CartTokenProcessor implements ProcessorInterface
 
         $guestCart->load('items.child');
 
-        foreach ($guestCart->items as $item) {
-            try {
-                $cartItem = $customerCart->items()
-                    ->where('product_id', $item->product_id)
-                    ->where('type', $item->type)
-                    ->first();
-
-                if ($cartItem) {
-                    $cartItem->update([
-                        'quantity' => $cartItem->quantity + $item->quantity,
+        $guard = auth()->guard();
+        $previousCustomer = $guard->user();
+
+        $guard->setUser($customer);
+        CartFacade::setCart($customerCart);
+
+        try {
+            foreach ($guestCart->items as $item) {
+                if (! $item->product) {
+                    continue;
+                }
+
+                try {
+                    CartFacade::addProduct($item->product, [
+                        ...(is_array($item->additional) ? $item->additional : []),
+                        'quantity' => $item->quantity,
                     ]);
-                } else {
-                    $newItem = $item->replicate()
-                        ->fill(['cart_id' => $customerCart->id]);
-                    $newItem->save();
-
-                    // Replicate child item for configurable products
-                    if ($item->type === 'configurable' && $item->child) {
-                        $item->child->replicate()
-                            ->fill([
-                                'cart_id'   => $customerCart->id,
-                                'parent_id' => $newItem->id,
-                            ])
-                            ->save();
-                    }
+                } catch (\Exception $e) {
+                    continue;
                 }
-            } catch (\Exception $e) {
-                continue;
             }
-        }
 
-        $guestCart->update(['is_active' => 0]);
+            $guestCart->update(['is_active' => 0]);
 
-        // Reload cart with relationships and remove invalid items
-        // (e.g., configurable items without child entries or deleted products)
-        $customerCart = CartModel::with('items.product', 'items.child.product')->find($customerCart->id);
+            // Reload cart with relationships and remove invalid items
+            // (e.g., configurable items without child entries or deleted products)
+            $customerCart = CartModel::with('items.product', 'items.child.product')->find($customerCart->id);
 
-        foreach ($customerCart->items as $item) {
-            if (! $item->product
-                || ($item->type === 'configurable' && ! $item->child)
-            ) {
-                $item->delete();
+            foreach ($customerCart->items as $item) {
+                if (! $item->product
+                    || ($item->type === 'configurable' && ! $item->child)
+                ) {
+                    $item->delete();
+                }
             }
-        }
 
-        $customerCart = CartModel::with('items.product')->find($customerCart->id);
+            $customerCart = CartModel::with('items.product')->find($customerCart->id);
 
-        CartFacade::setCart($customerCart);
+            CartFacade::setCart($customerCart);
 
-        CartFacade::collectTotals();
+            CartFacade::collectTotals();
 
-        $customerCart = CartModel::find($customerCart->id);
+            $customerCart = CartModel::find($customerCart->id);
 
-        $cartData = CartData::fromModel($customerCart);
-        $cartData->success = true;
-        $cartData->message = __('bagistoapi::app.graphql.cart.guest-cart-merged');
+            $cartData = CartData::fromModel($customerCart);
+            $cartData->success = true;
+            $cartData->message = __('bagistoapi::app.graphql.cart.guest-cart-merged');
 
-        return (array) $cartData;
+            return (array) $cartData;
+        } finally {
+            if ($previousCustomer) {
+                $guard->setUser($previousCustomer);
+            } else {
+                app('auth')->forgetGuards();
+            }
+        }
     }
 
     /**

+ 141 - 0
packages/Webkul/BagistoApi/tests/Feature/GraphQL/MergeCartTest.php

@@ -0,0 +1,141 @@
+<?php
+
+namespace Webkul\BagistoApi\Tests\Feature\GraphQL;
+
+use Webkul\BagistoApi\Models\StorefrontKey;
+use Webkul\BagistoApi\Tests\GraphQLTestCase;
+use Webkul\Product\Models\Product;
+
+class MergeCartTest extends GraphQLTestCase
+{
+    public function setUp(): void
+    {
+        parent::setUp();
+
+        $this->storefrontKey = StorefrontKey::generateKey();
+
+        StorefrontKey::query()->create([
+            'name'       => 'Merge Cart Test',
+            'key'        => $this->storefrontKey,
+            'is_active'  => true,
+            'rate_limit' => 1000,
+        ]);
+    }
+
+    public function test_merge_recalculates_totals_when_same_product_exists_in_both_carts(): void
+    {
+        $product = Product::query()
+            ->where('type', 'simple')
+            ->whereHas('inventories', fn ($query) => $query->where('qty', '>=', 3))
+            ->get()
+            ->first(fn (Product $product) => (bool) $product->status);
+
+        if (! $product) {
+            $this->markTestSkipped('A saleable simple product is required for this test.');
+        }
+
+        $guestCart = $this->createCart();
+        $this->addProduct($guestCart['token'], $product->id, 2);
+
+        $customerData = $this->createTestCustomer();
+        $this->addProduct($customerData['token'], $product->id, 1);
+
+        $mutation = <<<'GQL'
+            mutation mergeCart($cartId: Int!) {
+              createMergeCart(input: {cartId: $cartId}) {
+                mergeCart {
+                  success
+                  baseSubtotal
+                  items {
+                    edges {
+                      node {
+                        quantity
+                        basePrice
+                        baseTotal
+                      }
+                    }
+                  }
+                }
+              }
+            }
+        GQL;
+
+        $response = $this->graphQL($mutation, [
+            'cartId' => $guestCart['id'],
+        ], $this->customerHeaders($customerData['token']));
+
+        $response->assertSuccessful();
+
+        $cart = $response->json('data.createMergeCart.mergeCart');
+        $item = $cart['items']['edges'][0]['node'];
+
+        $this->assertTrue($cart['success']);
+        $this->assertSame(3, $item['quantity']);
+        $this->assertEqualsWithDelta(
+            (float) $item['basePrice'] * 3,
+            (float) $item['baseTotal'],
+            0.0001
+        );
+        $this->assertEqualsWithDelta(
+            (float) $item['baseTotal'],
+            (float) $cart['baseSubtotal'],
+            0.0001
+        );
+    }
+
+    private function createCart(): array
+    {
+        $mutation = <<<'GQL'
+            mutation createCart {
+              createCartToken(input: {}) {
+                cartToken {
+                  _id
+                  cartToken
+                }
+              }
+            }
+        GQL;
+
+        $response = $this->graphQL($mutation);
+        $response->assertSuccessful();
+
+        return [
+            'id'    => (int) $response->json('data.createCartToken.cartToken._id'),
+            'token' => (string) $response->json('data.createCartToken.cartToken.cartToken'),
+        ];
+    }
+
+    private function addProduct(string $token, int $productId, int $quantity): void
+    {
+        $mutation = <<<'GQL'
+            mutation addProduct($productId: Int!, $quantity: Int!) {
+              createAddProductInCart(input: {
+                productId: $productId
+                quantity: $quantity
+              }) {
+                addProductInCart {
+                  success
+                }
+              }
+            }
+        GQL;
+
+        $response = $this->graphQL($mutation, [
+            'productId' => $productId,
+            'quantity'  => $quantity,
+        ], $this->customerHeaders($token));
+
+        $response->assertSuccessful();
+        $this->assertTrue(
+            (bool) $response->json('data.createAddProductInCart.addProductInCart.success'),
+            $response->getContent()
+        );
+    }
+
+    private function customerHeaders(string $token): array
+    {
+        return [
+            'Authorization' => 'Bearer '.$token,
+        ];
+    }
+}

+ 4 - 0
packages/Webkul/Checkout/src/Cart.php

@@ -230,6 +230,10 @@ class Cart
                 'customer_email'      => $customer->email,
             ], $guestCart->id);
 
+            $this->setCart($this->cartRepository->find($guestCart->id));
+
+            $this->collectTotals();
+
             session()->forget('cart');
 
             return;