1
1
<?php
2
2
/**
3
- * Copyright © Magento, Inc. All rights reserved.
4
- * See COPYING.txt for license details .
3
+ * Copyright 2019 Adobe
4
+ * All Rights Reserved .
5
5
*/
6
6
declare (strict_types=1 );
7
7
8
8
namespace Magento \QuoteGraphQl \Model \Cart \MergeCarts ;
9
9
10
-
10
+ use Magento \Catalog \Api \Data \ProductInterface ;
11
+ use Magento \Catalog \Api \ProductRepositoryInterface ;
11
12
use Magento \CatalogInventory \Api \StockRegistryInterface ;
13
+ use Magento \Checkout \Model \Config ;
12
14
use Magento \Framework \Exception \CouldNotSaveException ;
13
15
use Magento \Framework \Exception \NoSuchEntityException ;
14
16
use Magento \Quote \Api \CartItemRepositoryInterface ;
15
17
use Magento \Quote \Api \Data \CartInterface ;
16
18
use Magento \Quote \Api \Data \CartItemInterface ;
19
+ use Magento \Quote \Model \Quote \Item ;
17
20
18
21
class CartQuantityValidator implements CartQuantityValidatorInterface
19
22
{
20
23
/**
21
- * @var CartItemRepositoryInterface
22
- */
23
- private $ cartItemRepository ;
24
-
25
- /**
26
- * @var StockRegistryInterface
24
+ * @var array
27
25
*/
28
- private $ stockRegistry ;
26
+ private array $ cumulativeQty = [] ;
29
27
30
28
/**
29
+ * CartQuantityValidator Constructor
30
+ *
31
31
* @param CartItemRepositoryInterface $cartItemRepository
32
32
* @param StockRegistryInterface $stockRegistry
33
+ * @param Config $config
34
+ * @param ProductRepositoryInterface $productRepository
33
35
*/
34
36
public function __construct (
35
- CartItemRepositoryInterface $ cartItemRepository ,
36
- StockRegistryInterface $ stockRegistry
37
+ private readonly CartItemRepositoryInterface $ cartItemRepository ,
38
+ private readonly StockRegistryInterface $ stockRegistry ,
39
+ private readonly Config $ config ,
40
+ private readonly ProductRepositoryInterface $ productRepository
37
41
) {
38
- $ this ->cartItemRepository = $ cartItemRepository ;
39
- $ this ->stockRegistry = $ stockRegistry ;
40
42
}
41
43
42
44
/**
@@ -49,28 +51,166 @@ public function __construct(
49
51
public function validateFinalCartQuantities (CartInterface $ customerCart , CartInterface $ guestCart ): bool
50
52
{
51
53
$ modified = false ;
52
- /** @var CartItemInterface $guestCartItem */
54
+ $ this ->cumulativeQty = [];
55
+
53
56
foreach ($ guestCart ->getAllVisibleItems () as $ guestCartItem ) {
54
- foreach ($ customerCart ->getAllItems () as $ customerCartItem ) {
55
- if ($ customerCartItem ->compare ($ guestCartItem )) {
56
- $ product = $ customerCartItem ->getProduct ();
57
- $ stockCurrentQty = $ this ->stockRegistry ->getStockStatus (
58
- $ product ->getId (),
59
- $ product ->getStore ()->getWebsiteId ()
60
- )->getQty ();
61
- if ($ stockCurrentQty < $ guestCartItem ->getQty () + $ customerCartItem ->getQty ()) {
62
- try {
63
- $ this ->cartItemRepository ->deleteById ($ guestCart ->getId (), $ guestCartItem ->getItemId ());
64
- $ modified = true ;
65
- } catch (NoSuchEntityException $ e ) {
66
- continue ;
67
- } catch (CouldNotSaveException $ e ) {
68
- continue ;
69
- }
70
- }
57
+ foreach ($ customerCart ->getAllVisibleItems () as $ customerCartItem ) {
58
+ if (!$ customerCartItem ->compare ($ guestCartItem )) {
59
+ continue ;
60
+ }
61
+
62
+ if ($ this ->config ->getCartMergePreference () === Config::CART_PREFERENCE_CUSTOMER ) {
63
+ $ this ->safeDeleteCartItem ((int ) $ guestCart ->getId (), (int ) $ guestCartItem ->getItemId ());
64
+ $ modified = true ;
65
+ continue ;
66
+ }
67
+
68
+ $ sku = $ this ->getSkuFromItem ($ customerCartItem );
69
+ $ product = $ this ->getProduct ((int ) $ customerCartItem ->getProduct ()->getId ());
70
+ $ isAvailable = $ customerCartItem ->getChildren ()
71
+ ? $ this ->validateCompositeProductQty ($ product , $ guestCartItem , $ customerCartItem )
72
+ : $ this ->validateProductQty ($ product , $ sku , $ guestCartItem ->getQty (), $ customerCartItem ->getQty ());
73
+
74
+ if ($ this ->config ->getCartMergePreference () === Config::CART_PREFERENCE_GUEST ) {
75
+ $ this ->safeDeleteCartItem ((int ) $ customerCart ->getId (), (int ) $ customerCartItem ->getItemId ());
76
+ $ modified = true ;
77
+ }
78
+
79
+ if (!$ isAvailable ) {
80
+ $ this ->safeDeleteCartItem ((int ) $ guestCart ->getId (), (int ) $ guestCartItem ->getItemId ());
81
+ $ modified = true ;
71
82
}
72
83
}
73
84
}
85
+
74
86
return $ modified ;
75
87
}
88
+
89
+ /**
90
+ * Get SKU from Cart Item
91
+ *
92
+ * @param CartItemInterface $item
93
+ * @return string
94
+ */
95
+ private function getSkuFromItem (CartItemInterface $ item ): string
96
+ {
97
+ return $ item ->getProduct ()->getOptions ()
98
+ ? $ this ->getProduct ((int ) $ item ->getProduct ()->getId ())->getSku ()
99
+ : $ item ->getProduct ()->getSku ();
100
+ }
101
+
102
+ /**
103
+ * Get current cart item quantity based on merge preference
104
+ *
105
+ * @param float $guestQty
106
+ * @param float $customerQty
107
+ * @return float
108
+ */
109
+ private function getCurrentCartItemQty (float $ guestQty , float $ customerQty ): float
110
+ {
111
+ return match ($ this ->config ->getCartMergePreference ()) {
112
+ Config::CART_PREFERENCE_CUSTOMER => $ customerQty ,
113
+ Config::CART_PREFERENCE_GUEST => $ guestQty ,
114
+ default => $ guestQty + $ customerQty
115
+ };
116
+ }
117
+
118
+ /**
119
+ * Validate product stock availability
120
+ *
121
+ * @param ProductInterface $product
122
+ * @param string $sku
123
+ * @param float $guestQty
124
+ * @param float $customerQty
125
+ * @return bool
126
+ */
127
+ private function validateProductQty (
128
+ ProductInterface $ product ,
129
+ string $ sku ,
130
+ float $ guestQty ,
131
+ float $ customerQty
132
+ ): bool {
133
+ $ salableQty = $ this ->stockRegistry ->getStockStatus (
134
+ $ product ->getId (),
135
+ $ product ->getStore ()->getWebsiteId ()
136
+ )->getQty ();
137
+
138
+ $ this ->cumulativeQty [$ sku ] ??= 0 ;
139
+ $ this ->cumulativeQty [$ sku ] += $ this ->getCurrentCartItemQty ($ guestQty , $ customerQty );
140
+
141
+ return $ salableQty >= $ this ->cumulativeQty [$ sku ];
142
+ }
143
+
144
+ /**
145
+ * Validate composite product quantities
146
+ *
147
+ * @param ProductInterface $productInterface
148
+ * @param Item $guestCartItem
149
+ * @param Item $customerCartItem
150
+ * @return bool
151
+ */
152
+ private function validateCompositeProductQty (
153
+ ProductInterface $ productInterface ,
154
+ Item $ guestCartItem ,
155
+ Item $ customerCartItem
156
+ ): bool {
157
+ $ guestChildItems = $ this ->retrieveChildItems ($ guestCartItem );
158
+ foreach ($ customerCartItem ->getChildren () as $ customerChildItem ) {
159
+ $ childProduct = $ customerChildItem ->getProduct ()->getOptions ()
160
+ ? $ this ->getProduct ((int ) $ customerChildItem ->getProduct ()->getId ())
161
+ : $ customerChildItem ->getProduct ();
162
+ $ sku = $ childProduct ->getSku ();
163
+ $ customerItemQty = $ customerCartItem ->getQty () * $ customerChildItem ->getQty ();
164
+ $ guestItemQty = $ guestCartItem ->getQty () * $ guestChildItems [$ sku ]->getQty ();
165
+
166
+ if (!$ this ->validateProductQty ($ childProduct , $ sku , $ guestItemQty , $ customerItemQty )) {
167
+ return false ;
168
+ }
169
+ }
170
+
171
+ return true ;
172
+ }
173
+
174
+ /**
175
+ * Get product by ID
176
+ *
177
+ * @param int $productId
178
+ * @return ProductInterface
179
+ * @throws NoSuchEntityException
180
+ */
181
+ private function getProduct (int $ productId ): ProductInterface
182
+ {
183
+ return $ this ->productRepository ->getById ($ productId );
184
+ }
185
+
186
+ /**
187
+ * Retrieve child items from a quote item
188
+ *
189
+ * @param Item $quoteItem
190
+ * @return Item[]
191
+ */
192
+ private function retrieveChildItems (Item $ quoteItem ): array
193
+ {
194
+ $ childItems = [];
195
+ foreach ($ quoteItem ->getChildren () as $ childItem ) {
196
+ $ childItems [$ childItem ->getProduct ()->getSku ()] = $ childItem ;
197
+ }
198
+ return $ childItems ;
199
+ }
200
+
201
+ /**
202
+ * Safely delete a cart item by ID
203
+ *
204
+ * @param int $cartId
205
+ * @param int $itemId
206
+ * @return void
207
+ */
208
+ private function safeDeleteCartItem (int $ cartId , int $ itemId ): void
209
+ {
210
+ try {
211
+ $ this ->cartItemRepository ->deleteById ($ cartId , $ itemId );
212
+ } catch (NoSuchEntityException |CouldNotSaveException $ e ) { // phpcs:ignore
213
+ // Optionally log the error.
214
+ }
215
+ }
76
216
}
0 commit comments