9
9
10
10
use Magento \Bundle \Model \Sales \Order \Shipment \BundleShipmentTypeValidator ;
11
11
use \Laminas \Validator \ValidatorInterface ;
12
+ use Magento \Catalog \Model \Product \Type ;
13
+ use Magento \Framework \Exception \NoSuchEntityException ;
12
14
use Magento \Framework \Phrase ;
13
15
use Magento \Framework \Webapi \Request ;
16
+ use Magento \Sales \Api \Data \ShipmentItemInterface ;
17
+ use Magento \Sales \Model \Order \Item ;
14
18
use Magento \Sales \Model \Order \Shipment ;
15
19
16
20
/**
@@ -20,6 +24,10 @@ class BundleOrderTypeValidator extends BundleShipmentTypeValidator implements Va
20
24
{
21
25
private const SHIPMENT_API_ROUTE = 'v1/shipment ' ;
22
26
27
+ public const SHIPMENT_TYPE_TOGETHER = '0 ' ;
28
+
29
+ public const SHIPMENT_TYPE_SEPARATELY = '1 ' ;
30
+
23
31
/**
24
32
* @var array
25
33
*/
@@ -52,14 +60,23 @@ public function isValid($value): bool
52
60
return true ;
53
61
}
54
62
55
- foreach ($ value ->getOrder ()->getAllItems () as $ orderItem ) {
56
- foreach ($ value ->getItems () as $ shipmentItem ) {
57
- if ($ orderItem ->getItemId () == $ shipmentItem ->getOrderItemId ()) {
58
- if ($ validationMessages = $ this ->validate ($ orderItem )) {
59
- $ this ->renderValidationMessages ($ validationMessages );
60
- }
61
- }
63
+ $ result = $ shippingInfo = [];
64
+ foreach ($ value ->getItems () as $ shipmentItem ) {
65
+ $ shippingInfo [$ shipmentItem ->getOrderItemId ()] = [
66
+ 'shipment_info ' => $ shipmentItem ,
67
+ 'order_info ' => $ value ->getOrder ()->getItemById ($ shipmentItem ->getOrderItemId ())
68
+ ];
69
+ }
70
+
71
+ foreach ($ shippingInfo as $ shippingItemInfo ) {
72
+ if ($ shippingItemInfo ['order_info ' ]->getProductType () === Type::TYPE_BUNDLE ) {
73
+ $ result [] = $ this ->checkBundleItem ($ shippingItemInfo , $ shippingInfo );
74
+ } elseif ($ shippingItemInfo ['order_info ' ]->getParentItem () &&
75
+ $ shippingItemInfo ['order_info ' ]->getParentItem ()->getProductType () === Type::TYPE_BUNDLE
76
+ ) {
77
+ $ result [] = $ this ->checkChildItem ($ shippingItemInfo ['order_info ' ], $ shippingInfo );
62
78
}
79
+ $ this ->renderValidationMessages ($ result );
63
80
}
64
81
65
82
return empty ($ this ->messages );
@@ -75,6 +92,118 @@ public function getMessages(): array
75
92
return $ this ->messages ;
76
93
}
77
94
95
+ /**
96
+ * @param Item $orderItem
97
+ * @param array $shipmentInfo
98
+ * @return Phrase|null
99
+ * @throws NoSuchEntityException
100
+ */
101
+ private function checkChildItem (Item $ orderItem , array $ shipmentInfo ): ?Phrase
102
+ {
103
+ $ result = null ;
104
+ if ($ orderItem ->getParentItem ()->getProductType () === Type::TYPE_BUNDLE &&
105
+ $ orderItem ->getParentItem ()->getProduct ()->getShipmentType () === self ::SHIPMENT_TYPE_TOGETHER ) {
106
+ $ result = __ (
107
+ 'Cannot create shipment as bundle product "%1" has shipment type "%2". ' .
108
+ '%3 should be shipped instead. ' ,
109
+ $ orderItem ->getParentItem ()->getSku (),
110
+ __ ('Together ' ),
111
+ __ ('Bundle product itself ' ),
112
+ );
113
+ }
114
+
115
+ if ($ orderItem ->getParentItem ()->getProductType () === Type::TYPE_BUNDLE &&
116
+ $ orderItem ->getParentItem ()->getProduct ()->getShipmentType () === self ::SHIPMENT_TYPE_SEPARATELY &&
117
+ false === $ this ->hasParentInShipping ($ orderItem , $ shipmentInfo )
118
+ ) {
119
+ $ result = __ (
120
+ 'Cannot create shipment as bundle product %1 should be included as well. ' ,
121
+ $ orderItem ->getParentItem ()->getSku ()
122
+ );
123
+ }
124
+
125
+ return $ result ;
126
+ }
127
+
128
+ /**
129
+ * @param array $shippingItemInfo
130
+ * @param array $shippingInfo
131
+ * @return Phrase|null
132
+ */
133
+ private function checkBundleItem (array $ shippingItemInfo , array $ shippingInfo ): ?Phrase
134
+ {
135
+ $ result = null ;
136
+ /** @var Item $orderItem */
137
+ $ orderItem = $ shippingItemInfo ['order_info ' ];
138
+ /** @var ShipmentItemInterface $shipmentItem */
139
+ $ shipmentItem = $ shippingItemInfo ['shipment_info ' ];
140
+
141
+ if ($ orderItem ->getProduct ()->getShipmentType () === self ::SHIPMENT_TYPE_TOGETHER &&
142
+ $ this ->hasChildrenInShipping ($ shipmentItem , $ shippingInfo )
143
+ ) {
144
+ $ result = __ (
145
+ 'Cannot create shipment as bundle product "%1" has shipment type "%2". ' .
146
+ '%3 should be shipped instead. ' ,
147
+ $ orderItem ->getSku (),
148
+ __ ('Together ' ),
149
+ __ ('Bundle product itself ' ),
150
+ );
151
+ }
152
+ if ($ orderItem ->getProduct ()->getShipmentType () === self ::SHIPMENT_TYPE_SEPARATELY &&
153
+ false === $ this ->hasChildrenInShipping ($ shipmentItem , $ shippingInfo )
154
+ ) {
155
+ $ result = __ (
156
+ 'Cannot create shipment as bundle product "%1" has shipment type "%2". ' .
157
+ '%3. ' ,
158
+ $ orderItem ->getSku (),
159
+ __ ('Separately ' ),
160
+ __ ('Shipment should also incorporate bundle options ' ),
161
+ );
162
+ }
163
+ return $ result ;
164
+ }
165
+
166
+ /**
167
+ * Determines if a child shipment item has its corresponding parent in shipment
168
+ *
169
+ * @param Item $childItem
170
+ * @param array $shipmentInfo
171
+ * @return bool
172
+ */
173
+ private function hasParentInShipping (Item $ childItem , array $ shipmentInfo ): bool
174
+ {
175
+ /** @var Item $orderItem */
176
+ foreach (array_column ($ shipmentInfo , 'order_info ' ) as $ orderItem ) {
177
+ if (!$ orderItem ->getParentItemId () &&
178
+ $ orderItem ->getProductType () === Type::TYPE_BUNDLE &&
179
+ $ childItem ->getParentItemId () == $ orderItem ->getItemId ()
180
+ ) {
181
+ return true ;
182
+ }
183
+ }
184
+ return false ;
185
+ }
186
+
187
+ /**
188
+ * Determines if a bundle shipment item has at least one child in shipment
189
+ *
190
+ * @param ShipmentItemInterface $bundleItem
191
+ * @param array $shippingInfo
192
+ * @return bool
193
+ */
194
+ private function hasChildrenInShipping (ShipmentItemInterface $ bundleItem , array $ shippingInfo ): bool
195
+ {
196
+ /** @var Item $orderItem */
197
+ foreach (array_column ($ shippingInfo , 'order_info ' ) as $ orderItem ) {
198
+ if ($ orderItem ->getParentItemId () &&
199
+ $ orderItem ->getParentItemId () == $ bundleItem ->getOrderItemId ()
200
+ ) {
201
+ return true ;
202
+ }
203
+ }
204
+ return false ;
205
+ }
206
+
78
207
/**
79
208
* Determines if the validation should be triggered or not
80
209
*
@@ -98,5 +227,6 @@ private function renderValidationMessages(array $validationMessages): void
98
227
$ this ->messages [] = $ message ->render ();
99
228
}
100
229
}
230
+ $ this ->messages = array_unique ($ this ->messages );
101
231
}
102
232
}
0 commit comments