Skip to content

Commit 8285894

Browse files
author
Viktor Kopin
committed
MC-30348: Rest salesShipOrderV1 API w/ Bundles error: "You can't create a shipment without products"
1 parent 252dd90 commit 8285894

File tree

12 files changed

+666
-72
lines changed

12 files changed

+666
-72
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Bundle\Model\Sales\Order\Shipment;
9+
10+
use Magento\Catalog\Model\Product\Type;
11+
use Magento\Sales\Model\ValidatorInterface;
12+
13+
/**
14+
* Validate if requested order items can be shipped according to bundle product shipment type
15+
*/
16+
class BundleShipmentTypeValidator implements ValidatorInterface
17+
{
18+
/**
19+
* @inheritdoc
20+
*/
21+
public function validate($item)
22+
{
23+
$result = [];
24+
if (!$item->isDummy(true)) {
25+
return $result;
26+
}
27+
28+
$message = 'Cannot create shipment as bundle product "%1" has shipment type "%2". ' .
29+
'%3 should be shipped instead.';
30+
31+
if ($item->getHasChildren() && $item->getProductType() === Type::TYPE_BUNDLE) {
32+
$result[] = __(
33+
$message,
34+
$item->getSku(),
35+
__('Separately'),
36+
__('Bundle product options'),
37+
);
38+
}
39+
40+
if ($item->getParentItem() && $item->getParentItem()->getProductType() === Type::TYPE_BUNDLE) {
41+
$result[] = __(
42+
$message,
43+
$item->getParentItem()->getSku(),
44+
__('Together'),
45+
__('Bundle product itself'),
46+
);
47+
}
48+
49+
return $result;
50+
}
51+
}

app/code/Magento/Bundle/etc/di.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,4 +234,11 @@
234234
</argument>
235235
</arguments>
236236
</type>
237+
<type name="Magento\Sales\Model\Order\Shipment\ShipmentItemsValidator">
238+
<arguments>
239+
<argument name="validators" xsi:type="array">
240+
<item name="shipment_type" xsi:type="object">Magento\Bundle\Model\Sales\Order\Shipment\BundleShipmentTypeValidator</item>
241+
</argument>
242+
</arguments>
243+
</type>
237244
</config>

app/code/Magento/Bundle/i18n/en_US.csv

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,6 @@ Select...,Select...
105105
Status,Status
106106
Thumbnail,Thumbnail
107107
Type,Type
108+
"Cannot create shipment as bundle product ""%1"" has shipment type ""%2"". %3 should be shipped instead.","Cannot create shipment as bundle product ""%1"" has shipment type ""%2"". %3 should be shipped instead."
109+
"Bundle product itself","Bundle product itself"
110+
"Bundle product options","Bundle product options"
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Sales\Model\Order\Shipment;
9+
10+
use Magento\Framework\Exception\ConfigurationMismatchException;
11+
use Magento\Sales\Model\ValidatorInterface;
12+
use Magento\Sales\Model\ValidatorResultInterface;
13+
use Magento\Sales\Model\ValidatorResultInterfaceFactory;
14+
15+
/**
16+
* Requested shipment items validation interface
17+
*/
18+
class ShipmentItemsValidator implements ShipmentItemsValidatorInterface
19+
{
20+
/**
21+
* @var ValidatorInterface[]
22+
*/
23+
private $validators;
24+
25+
/**
26+
* @var ValidatorResultInterfaceFactory
27+
*/
28+
private $validatorResultFactory;
29+
30+
/**
31+
* @param ValidatorResultInterfaceFactory $validatorResult
32+
* @param ValidatorInterface[] $validators
33+
*/
34+
public function __construct(ValidatorResultInterfaceFactory $validatorResult, array $validators = [])
35+
{
36+
$this->validatorResultFactory = $validatorResult;
37+
$this->validators = $validators;
38+
}
39+
40+
/**
41+
* @inheritdoc
42+
*/
43+
public function validate(array $items): ValidatorResultInterface
44+
{
45+
$messages = [];
46+
foreach ($this->validators as $validator) {
47+
if (!$validator instanceof ValidatorInterface) {
48+
throw new ConfigurationMismatchException(
49+
__(
50+
'The "%1" validator is not an instance of the general validator interface.',
51+
get_class($validator)
52+
)
53+
);
54+
}
55+
foreach ($items as $item) {
56+
$messages[] = $validator->validate($item);
57+
}
58+
}
59+
60+
return $this->validatorResultFactory->create(['messages' => array_merge([], ...$messages)]);
61+
}
62+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Sales\Model\Order\Shipment;
9+
10+
use Magento\Framework\Exception\ConfigurationMismatchException;
11+
use Magento\Sales\Api\Data\OrderItemInterface;
12+
use Magento\Sales\Model\ValidatorResultInterface;
13+
14+
/**
15+
* Represents shipment creation items validator interface
16+
*/
17+
interface ShipmentItemsValidatorInterface
18+
{
19+
/**
20+
* Validates shipment items creations
21+
*
22+
* @param OrderItemInterface[] $items
23+
* @return ValidatorResultInterface
24+
* @throws ConfigurationMismatchException
25+
*/
26+
public function validate(array $items): ValidatorResultInterface;
27+
}

app/code/Magento/Sales/Model/Order/Validation/ShipOrder.php

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,23 @@
55
*/
66
namespace Magento\Sales\Model\Order\Validation;
77

8+
use Magento\Framework\App\ObjectManager;
89
use Magento\Sales\Api\Data\OrderInterface;
10+
use Magento\Sales\Api\Data\OrderItemInterface;
11+
use Magento\Sales\Api\Data\ShipmentCommentCreationInterface;
12+
use Magento\Sales\Api\Data\ShipmentCreationArgumentsInterface;
913
use Magento\Sales\Api\Data\ShipmentInterface;
14+
use Magento\Sales\Api\Data\ShipmentItemCreationInterface;
15+
use Magento\Sales\Model\Order\Shipment\ShipmentItemsValidatorInterface;
1016
use Magento\Sales\Model\Order\Shipment\Validation\QuantityValidator;
1117
use Magento\Sales\Model\Order\OrderValidatorInterface;
1218
use Magento\Sales\Model\Order\Shipment\ShipmentValidatorInterface;
1319
use Magento\Sales\Model\Order\Shipment\Validation\TrackValidator;
20+
use Magento\Sales\Model\ValidatorResultInterface;
1421
use Magento\Sales\Model\ValidatorResultMerger;
1522

1623
/**
17-
* Class ShipOrder
24+
* Ship order validation class
1825
*/
1926
class ShipOrder implements ShipOrderInterface
2027
{
@@ -34,44 +41,53 @@ class ShipOrder implements ShipOrderInterface
3441
private $validatorResultMerger;
3542

3643
/**
37-
* ShipOrder constructor.
38-
*
44+
* @var ShipmentItemsValidatorInterface
45+
*/
46+
private $itemsValidator;
47+
48+
/**
3949
* @param OrderValidatorInterface $orderValidator
4050
* @param ShipmentValidatorInterface $shipmentValidator
4151
* @param ValidatorResultMerger $validatorResultMerger
52+
* @param ShipmentItemsValidatorInterface|null $itemsValidator
4253
*/
4354
public function __construct(
4455
OrderValidatorInterface $orderValidator,
4556
ShipmentValidatorInterface $shipmentValidator,
46-
ValidatorResultMerger $validatorResultMerger
57+
ValidatorResultMerger $validatorResultMerger,
58+
ShipmentItemsValidatorInterface $itemsValidator = null
4759
) {
4860
$this->orderValidator = $orderValidator;
4961
$this->shipmentValidator = $shipmentValidator;
5062
$this->validatorResultMerger = $validatorResultMerger;
63+
$this->itemsValidator = $itemsValidator
64+
?? ObjectManager::getInstance()->get(ShipmentItemsValidatorInterface::class);
5165
}
5266

5367
/**
68+
* Order shipment validate
69+
*
5470
* @param OrderInterface $order
5571
* @param ShipmentInterface $shipment
5672
* @param array $items
5773
* @param bool $notify
5874
* @param bool $appendComment
59-
* @param \Magento\Sales\Api\Data\ShipmentCommentCreationInterface|null $comment
75+
* @param ShipmentCommentCreationInterface|null $comment
6076
* @param array $tracks
6177
* @param array $packages
62-
* @param \Magento\Sales\Api\Data\ShipmentCreationArgumentsInterface|null $arguments
63-
* @return \Magento\Sales\Model\ValidatorResultInterface
78+
* @param ShipmentCreationArgumentsInterface|null $arguments
79+
* @return ValidatorResultInterface
6480
*/
6581
public function validate(
6682
$order,
6783
$shipment,
6884
array $items = [],
6985
$notify = false,
7086
$appendComment = false,
71-
\Magento\Sales\Api\Data\ShipmentCommentCreationInterface $comment = null,
87+
ShipmentCommentCreationInterface $comment = null,
7288
array $tracks = [],
7389
array $packages = [],
74-
\Magento\Sales\Api\Data\ShipmentCreationArgumentsInterface $arguments = null
90+
ShipmentCreationArgumentsInterface $arguments = null
7591
) {
7692
$orderValidationResult = $this->orderValidator->validate(
7793
$order,
@@ -87,6 +103,39 @@ public function validate(
87103
]
88104
);
89105

90-
return $this->validatorResultMerger->merge($orderValidationResult, $shipmentValidationResult);
106+
$orderItems = $this->getRequestedOrderItems($items, $order);
107+
$itemsValidationResult = $this->itemsValidator->validate($orderItems);
108+
109+
return $this->validatorResultMerger->merge(
110+
$orderValidationResult,
111+
$shipmentValidationResult,
112+
$itemsValidationResult->getMessages()
113+
);
114+
}
115+
116+
/**
117+
* Return requested order items
118+
*
119+
* @param OrderItemInterface[] $items
120+
* @param OrderInterface $order
121+
* @return OrderItemInterface[]
122+
*/
123+
private function getRequestedOrderItems(array $items, OrderInterface $order): array
124+
{
125+
$requestedItemIds = array_reduce(
126+
$items,
127+
function (array $result, ShipmentItemCreationInterface $item): array {
128+
$result[] = $item->getOrderItemId();
129+
return $result;
130+
},
131+
[]
132+
);
133+
134+
return array_filter(
135+
$order->getAllItems(),
136+
function (OrderItemInterface $orderItem) use ($requestedItemIds): bool {
137+
return in_array($orderItem->getId(), $requestedItemIds);
138+
}
139+
);
91140
}
92141
}

app/code/Magento/Sales/Model/ValidatorResult.php

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,22 @@
66
namespace Magento\Sales\Model;
77

88
/**
9-
* Class ValidatorResult
9+
* Validation result messages class
1010
*/
1111
class ValidatorResult implements ValidatorResultInterface
1212
{
1313
/**
1414
* @var \string[]
1515
*/
16-
private $messages = [];
16+
private $messages;
17+
18+
/**
19+
* @param array $messages
20+
*/
21+
public function __construct(array $messages = [])
22+
{
23+
$this->messages = $messages;
24+
}
1725

1826
/**
1927
* @inheritdoc
@@ -24,15 +32,15 @@ public function addMessage($message)
2432
}
2533

2634
/**
27-
* @return bool
35+
* @inheritdoc
2836
*/
2937
public function hasMessages()
3038
{
3139
return count($this->messages) > 0;
3240
}
3341

3442
/**
35-
* @return \string[]
43+
* @inheritdoc
3644
*/
3745
public function getMessages()
3846
{

app/code/Magento/Sales/etc/di.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@
117117
<preference for="Magento\Sales\Api\RefundInvoiceInterface" type="Magento\Sales\Model\RefundInvoice"/>
118118
<preference for="Magento\Sales\Model\ResourceModel\Provider\NotSyncedDataProviderInterface" type="Magento\Sales\Model\ResourceModel\Provider\NotSyncedDataProvider" />
119119
<preference for="Magento\Sales\Model\ConfigInterface" type="Magento\Sales\Model\Config" />
120+
<preference for="Magento\Sales\Model\Order\Shipment\ShipmentItemsValidatorInterface" type="Magento\Sales\Model\Order\Shipment\ShipmentItemsValidator" />
120121
<type name="Magento\Sales\Model\ResourceModel\Provider\NotSyncedDataProvider">
121122
<arguments>
122123
<argument name="providers" xsi:type="array">

0 commit comments

Comments
 (0)