Skip to content

Commit 2c90cff

Browse files
authored
Merge pull request #5983 from magento-honey-badgers/241-customer-orders
[honey] MC-20638: MyAccount :: Order Details :: Shipping Details by Order Number
2 parents da50b9b + 35145b1 commit 2c90cff

File tree

127 files changed

+6920
-674
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

127 files changed

+6920
-674
lines changed

Diff for: app/code/Magento/Bundle/Model/Product/Type.php

+76-20
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
namespace Magento\Bundle\Model\Product;
88

9+
use Magento\Bundle\Model\Option;
10+
use Magento\Bundle\Model\ResourceModel\Option\Collection;
911
use Magento\Bundle\Model\ResourceModel\Selection\Collection as Selections;
1012
use Magento\Bundle\Model\ResourceModel\Selection\Collection\FilterApplier as SelectionCollectionFilterApplier;
1113
use Magento\Catalog\Api\ProductRepositoryInterface;
@@ -414,16 +416,13 @@ public function beforeSave($product)
414416
if ($product->getCanSaveBundleSelections()) {
415417
$product->canAffectOptions(true);
416418
$selections = $product->getBundleSelectionsData();
417-
if ($selections && !empty($selections)) {
418-
$options = $product->getBundleOptionsData();
419-
if ($options) {
420-
foreach ($options as $option) {
421-
if (empty($option['delete']) || 1 != (int)$option['delete']) {
422-
$product->setTypeHasOptions(true);
423-
if (1 == (int)$option['required']) {
424-
$product->setTypeHasRequiredOptions(true);
425-
break;
426-
}
419+
if (!empty($selections) && $options = $product->getBundleOptionsData()) {
420+
foreach ($options as $option) {
421+
if (empty($option['delete']) || 1 != (int)$option['delete']) {
422+
$product->setTypeHasOptions(true);
423+
if (1 == (int)$option['required']) {
424+
$product->setTypeHasRequiredOptions(true);
425+
break;
427426
}
428427
}
429428
}
@@ -464,7 +463,7 @@ public function getOptionsIds($product)
464463
public function getOptionsCollection($product)
465464
{
466465
if (!$product->hasData($this->_keyOptionsCollection)) {
467-
/** @var \Magento\Bundle\Model\ResourceModel\Option\Collection $optionsCollection */
466+
/** @var Collection $optionsCollection */
468467
$optionsCollection = $this->_bundleOption->create()
469468
->getResourceCollection();
470469
$optionsCollection->setProductIdFilter($product->getEntityId());
@@ -530,10 +529,10 @@ public function getSelectionsCollection($optionIds, $product)
530529
* Example: the catalog inventory validation of decimal qty can change qty to int,
531530
* so need to change quote item qty option value too.
532531
*
533-
* @param array $options
534-
* @param \Magento\Framework\DataObject $option
535-
* @param mixed $value
536-
* @param \Magento\Catalog\Model\Product $product
532+
* @param array $options
533+
* @param \Magento\Framework\DataObject $option
534+
* @param mixed $value
535+
* @param \Magento\Catalog\Model\Product $product
537536
* @return $this
538537
*/
539538
public function updateQtyOption($options, \Magento\Framework\DataObject $option, $value, $product)
@@ -682,6 +681,11 @@ protected function _prepareProduct(\Magento\Framework\DataObject $buyRequest, $p
682681
$options
683682
);
684683

684+
$this->validateRadioAndSelectOptions(
685+
$optionsCollection,
686+
$options
687+
);
688+
685689
$selectionIds = array_values($this->arrayUtility->flatten($options));
686690
// If product has not been configured yet then $selections array should be empty
687691
if (!empty($selectionIds)) {
@@ -1184,9 +1188,11 @@ public function canConfigure($product)
11841188
* @return void
11851189
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
11861190
*/
1191+
// @codingStandardsIgnoreStart
11871192
public function deleteTypeSpecificData(\Magento\Catalog\Model\Product $product)
11881193
{
11891194
}
1195+
// @codingStandardsIgnoreEnd
11901196

11911197
/**
11921198
* Return array of specific to type product entities
@@ -1196,18 +1202,19 @@ public function deleteTypeSpecificData(\Magento\Catalog\Model\Product $product)
11961202
*/
11971203
public function getIdentities(\Magento\Catalog\Model\Product $product)
11981204
{
1199-
$identities = parent::getIdentities($product);
1205+
$identities = [];
1206+
$identities[] = parent::getIdentities($product);
12001207
/** @var \Magento\Bundle\Model\Option $option */
12011208
foreach ($this->getOptions($product) as $option) {
12021209
if ($option->getSelections()) {
12031210
/** @var \Magento\Catalog\Model\Product $selection */
12041211
foreach ($option->getSelections() as $selection) {
1205-
$identities = array_merge($identities, $selection->getIdentities());
1212+
$identities[] = $selection->getIdentities();
12061213
}
12071214
}
12081215
}
12091216

1210-
return $identities;
1217+
return array_merge([], ...$identities);
12111218
}
12121219

12131220
/**
@@ -1272,6 +1279,53 @@ protected function checkIsAllRequiredOptions($product, $isStrictProcessMode, $op
12721279
}
12731280
}
12741281

1282+
/**
1283+
* Validate Options for Radio and Select input types
1284+
*
1285+
* @param Collection $optionsCollection
1286+
* @param int[] $options
1287+
* @return void
1288+
* @throws \Magento\Framework\Exception\LocalizedException
1289+
*/
1290+
private function validateRadioAndSelectOptions($optionsCollection, $options): void
1291+
{
1292+
$errorTypes = [];
1293+
1294+
if (is_array($optionsCollection->getItems())) {
1295+
foreach ($optionsCollection->getItems() as $option) {
1296+
if ($this->isSelectedOptionValid($option, $options)) {
1297+
$errorTypes[] = $option->getType();
1298+
}
1299+
}
1300+
}
1301+
1302+
if (!empty($errorTypes)) {
1303+
throw new \Magento\Framework\Exception\LocalizedException(
1304+
__(
1305+
'Option type (%types) should have only one element.',
1306+
['types' => implode(", ", $errorTypes)]
1307+
)
1308+
);
1309+
}
1310+
}
1311+
1312+
/**
1313+
* Check if selected option is valid
1314+
*
1315+
* @param Option $option
1316+
* @param array $options
1317+
* @return bool
1318+
*/
1319+
private function isSelectedOptionValid($option, $options): bool
1320+
{
1321+
return (
1322+
($option->getType() == 'radio' || $option->getType() == 'select') &&
1323+
isset($options[$option->getOptionId()]) &&
1324+
is_array($options[$option->getOptionId()]) &&
1325+
count($options[$option->getOptionId()]) > 1
1326+
);
1327+
}
1328+
12751329
/**
12761330
* Check if selection is salable
12771331
*
@@ -1333,16 +1387,18 @@ protected function checkIsResult($_result)
13331387
*/
13341388
protected function mergeSelectionsWithOptions($options, $selections)
13351389
{
1390+
$selections = [];
1391+
13361392
foreach ($options as $option) {
13371393
$optionSelections = $option->getSelections();
13381394
if ($option->getRequired() && is_array($optionSelections) && count($optionSelections) == 1) {
1339-
$selections = array_merge($selections, $optionSelections);
1395+
$selections[] = $optionSelections;
13401396
} else {
13411397
$selections = [];
13421398
break;
13431399
}
13441400
}
13451401

1346-
return $selections;
1402+
return array_merge([], ...$selections);
13471403
}
13481404
}
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\BundleGraphQl\Model\Order\Shipment;
9+
10+
use Magento\Catalog\Model\Product\Type\AbstractType;
11+
use Magento\Sales\Api\Data\ShipmentInterface;
12+
use Magento\Sales\Api\Data\ShipmentItemInterface;
13+
use Magento\SalesGraphQl\Model\Shipment\Item\ShipmentItemFormatter;
14+
use Magento\SalesGraphQl\Model\Shipment\Item\FormatterInterface;
15+
16+
/**
17+
* Format Bundle shipment items for GraphQl output
18+
*/
19+
class BundleShipmentItemFormatter implements FormatterInterface
20+
{
21+
/**
22+
* @var ShipmentItemFormatter
23+
*/
24+
private $itemFormatter;
25+
26+
/**
27+
* @param ShipmentItemFormatter $itemFormatter
28+
*/
29+
public function __construct(ShipmentItemFormatter $itemFormatter)
30+
{
31+
$this->itemFormatter = $itemFormatter;
32+
}
33+
34+
/**
35+
* Format bundle product shipment item
36+
*
37+
* @param ShipmentInterface $shipment
38+
* @param ShipmentItemInterface $item
39+
* @return array|null
40+
*/
41+
public function formatShipmentItem(ShipmentInterface $shipment, ShipmentItemInterface $item): ?array
42+
{
43+
$orderItem = $item->getOrderItem();
44+
$shippingType = $orderItem->getProductOptions()['shipment_type'] ?? null;
45+
if ($shippingType == AbstractType::SHIPMENT_SEPARATELY && !$orderItem->getParentItemId()) {
46+
//When bundle items are shipped separately the children are treated as their own items
47+
return null;
48+
}
49+
return $this->itemFormatter->formatShipmentItem($shipment, $item);
50+
}
51+
}

Diff for: app/code/Magento/SalesGraphQl/Model/Resolver/BundleOptions.php renamed to app/code/Magento/BundleGraphQl/Model/Resolver/Order/Item/BundleOptions.php

+6-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66
declare(strict_types=1);
77

8-
namespace Magento\SalesGraphQl\Model\Resolver;
8+
namespace Magento\BundleGraphQl\Model\Resolver\Order\Item;
99

1010
use Magento\Framework\Exception\LocalizedException;
1111
use Magento\Framework\GraphQl\Config\Element\Field;
@@ -15,6 +15,8 @@
1515
use Magento\Framework\Serialize\Serializer\Json;
1616
use Magento\Sales\Api\Data\InvoiceItemInterface;
1717
use Magento\Sales\Api\Data\OrderItemInterface;
18+
use Magento\Sales\Api\Data\ShipmentItemInterface;
19+
use Magento\Sales\Api\Data\CreditmemoItemInterface;
1820

1921
/**
2022
* Resolve bundle options items for order item
@@ -55,12 +57,12 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
5557
throw new LocalizedException(__('"model" value should be specified'));
5658
}
5759
if ($value['model'] instanceof OrderItemInterface) {
58-
/** @var OrderItemInterface $item */
5960
$item = $value['model'];
6061
return $this->getBundleOptions($item, $value);
6162
}
62-
if ($value['model'] instanceof InvoiceItemInterface) {
63-
/** @var InvoiceItemInterface $item */
63+
if ($value['model'] instanceof InvoiceItemInterface
64+
|| $value['model'] instanceof ShipmentItemInterface
65+
|| $value['model'] instanceof CreditmemoItemInterface) {
6466
$item = $value['model'];
6567
// Have to pass down order and item to map to avoid refetching all data
6668
return $this->getBundleOptions($item->getOrderItem(), $value);

Diff for: app/code/Magento/BundleGraphQl/composer.json

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
"magento/module-quote": "*",
1111
"magento/module-quote-graph-ql": "*",
1212
"magento/module-store": "*",
13+
"magento/module-sales": "*",
14+
"magento/module-sales-graph-ql": "*",
1315
"magento/framework": "*"
1416
},
1517
"license": [

Diff for: app/code/Magento/BundleGraphQl/etc/graphql/di.xml

+35
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,39 @@
6565
</argument>
6666
</arguments>
6767
</type>
68+
<type name="Magento\SalesGraphQl\Model\TypeResolver\OrderItem">
69+
<arguments>
70+
<argument name="productTypeMap" xsi:type="array">
71+
<item name="bundle" xsi:type="string">BundleOrderItem</item>
72+
</argument>
73+
</arguments>
74+
</type>
75+
<type name="Magento\SalesGraphQl\Model\TypeResolver\InvoiceItem">
76+
<arguments>
77+
<argument name="productTypeMap" xsi:type="array">
78+
<item name="bundle" xsi:type="string">BundleInvoiceItem</item>
79+
</argument>
80+
</arguments>
81+
</type>
82+
<type name="Magento\SalesGraphQl\Model\TypeResolver\ShipmentItem">
83+
<arguments>
84+
<argument name="productTypeMap" xsi:type="array">
85+
<item name="bundle" xsi:type="string">BundleShipmentItem</item>
86+
</argument>
87+
</arguments>
88+
</type>
89+
<type name="Magento\SalesGraphQl\Model\TypeResolver\CreditMemoItem">
90+
<arguments>
91+
<argument name="productTypeMap" xsi:type="array">
92+
<item name="bundle" xsi:type="string">BundleCreditMemoItem</item>
93+
</argument>
94+
</arguments>
95+
</type>
96+
<type name="Magento\SalesGraphQl\Model\Shipment\ItemProvider">
97+
<arguments>
98+
<argument name="formatters" xsi:type="array">
99+
<item name="bundle" xsi:type="object">Magento\BundleGraphQl\Model\Order\Shipment\BundleShipmentItemFormatter\Proxy</item>
100+
</argument>
101+
</arguments>
102+
</type>
68103
</config>

Diff for: app/code/Magento/BundleGraphQl/etc/schema.graphqls

+30
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,33 @@ enum ShipBundleItemsEnum @doc(description: "This enumeration defines whether bun
8787
TOGETHER
8888
SEPARATELY
8989
}
90+
91+
type BundleOrderItem implements OrderItemInterface {
92+
bundle_options: [ItemSelectedBundleOption] @doc(description: "A list of bundle options that are assigned to the bundle product") @resolver(class: "Magento\\BundleGraphQl\\Model\\Resolver\\Order\\Item\\BundleOptions")
93+
}
94+
95+
type BundleInvoiceItem implements InvoiceItemInterface{
96+
bundle_options: [ItemSelectedBundleOption] @doc(description: "A list of bundle options that are assigned to the bundle product") @resolver(class: "Magento\\BundleGraphQl\\Model\\Resolver\\Order\\Item\\BundleOptions")
97+
}
98+
99+
type BundleShipmentItem implements ShipmentItemInterface {
100+
bundle_options: [ItemSelectedBundleOption] @doc(description: "A list of bundle options that are assigned to the bundle product") @resolver(class: "Magento\\BundleGraphQl\\Model\\Resolver\\Order\\Item\\BundleOptions")
101+
}
102+
103+
type BundleCreditMemoItem implements CreditMemoItemInterface {
104+
bundle_options: [ItemSelectedBundleOption] @doc(description: "A list of bundle options that are assigned to the bundle product") @resolver(class: "Magento\\BundleGraphQl\\Model\\Resolver\\Order\\Item\\BundleOptions")
105+
}
106+
107+
type ItemSelectedBundleOption @doc(description: "A list of options of the selected bundle product") {
108+
id: ID! @doc(description: "The unique identifier of the option")
109+
label: String! @doc(description: "The label of the option")
110+
values: [ItemSelectedBundleOptionValue] @doc(description: "A list of products that represent the values of the parent option")
111+
}
112+
113+
type ItemSelectedBundleOptionValue @doc(description: "A list of values for the selected bundle product") {
114+
id: ID! @doc(description: "The unique identifier of the value")
115+
product_name: String! @doc(description: "The name of the child bundle product")
116+
product_sku: String! @doc(description: "The SKU of the child bundle product")
117+
quantity: Float! @doc(description: "Indicates how many of this bundle product were ordered")
118+
price: Money! @doc(description: "The price of the child bundle product")
119+
}

Diff for: app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CustomizableEnteredOptionValueUid.php

+3
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ public function resolve(
4545
array $value = null,
4646
array $args = null
4747
) {
48+
if (isset($value['uid'])) {
49+
return $value['uid'];
50+
}
4851
if (!isset($value['option_id']) || empty($value['option_id'])) {
4952
throw new GraphQlInputException(__('"option_id" value should be specified.'));
5053
}

Diff for: app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CustomizableSelectedOptionValueUid.php

+3
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ public function resolve(
4545
array $value = null,
4646
array $args = null
4747
) {
48+
if (isset($value['uid'])) {
49+
return $value['uid'];
50+
}
4851
if (!isset($value['option_id']) || empty($value['option_id'])) {
4952
throw new GraphQlInputException(__('"option_id" value should be specified.'));
5053
}

Diff for: app/code/Magento/GraphQl/etc/schema.graphqls

+5
Original file line numberDiff line numberDiff line change
@@ -277,3 +277,8 @@ enum CurrencyEnum @doc(description: "The list of available currency codes") {
277277
TRL
278278
XPF
279279
}
280+
281+
input EnteredOptionInput @doc(description: "Defines a customer-entered option") {
282+
uid: ID! @doc(description: "An encoded ID")
283+
value: String! @doc(description: "Text the customer entered")
284+
}

0 commit comments

Comments
 (0)