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