diff --git a/app/code/Magento/Catalog/Plugin/Api/ProductLinkRepositoryInterface/ReindexAfterDeleteByIdProductLinksPlugin.php b/app/code/Magento/Catalog/Plugin/Api/ProductLinkRepositoryInterface/ReindexAfterDeleteByIdProductLinksPlugin.php
new file mode 100644
index 0000000000000..91e8629ec212a
--- /dev/null
+++ b/app/code/Magento/Catalog/Plugin/Api/ProductLinkRepositoryInterface/ReindexAfterDeleteByIdProductLinksPlugin.php
@@ -0,0 +1,57 @@
+fullProductIndexer = $fullProductIndexer;
+ $this->productRepository = $productRepository;
+ }
+
+ /**
+ * Complex reindex after product links has been deleted.
+ *
+ * @param ProductLinkRepositoryInterface $subject
+ * @param bool $result
+ * @param string $sku
+ * @param string $type
+ * @param string $linkedProductSku
+ * @return bool
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function afterDeleteById(ProductLinkRepositoryInterface $subject, bool $result, $sku): bool
+ {
+ $product = $this->productRepository->get($sku);
+ $this->fullProductIndexer->executeRow($product->getId());
+
+ return $result;
+ }
+}
diff --git a/app/code/Magento/Catalog/Plugin/Api/ProductLinkRepositoryInterface/ReindexAfterSaveProductLinksPlugin.php b/app/code/Magento/Catalog/Plugin/Api/ProductLinkRepositoryInterface/ReindexAfterSaveProductLinksPlugin.php
new file mode 100644
index 0000000000000..480399035a7a3
--- /dev/null
+++ b/app/code/Magento/Catalog/Plugin/Api/ProductLinkRepositoryInterface/ReindexAfterSaveProductLinksPlugin.php
@@ -0,0 +1,56 @@
+fullProductIndexer = $fullProductIndexer;
+ $this->productRepository = $productRepository;
+ }
+
+ /**
+ * Complex reindex after product links has been saved.
+ *
+ * @param ProductLinkRepositoryInterface $subject
+ * @param bool $result
+ * @param ProductLinkInterface $entity
+ * @return bool
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function afterSave(ProductLinkRepositoryInterface $subject, bool $result, ProductLinkInterface $entity): bool
+ {
+ $product = $this->productRepository->get($entity->getSku());
+ $this->fullProductIndexer->executeRow($product->getId());
+
+ return $result;
+ }
+}
diff --git a/app/code/Magento/Catalog/etc/webapi_rest/di.xml b/app/code/Magento/Catalog/etc/webapi_rest/di.xml
index 1fd47fde304ec..a33c3a19e1e4f 100644
--- a/app/code/Magento/Catalog/etc/webapi_rest/di.xml
+++ b/app/code/Magento/Catalog/etc/webapi_rest/di.xml
@@ -40,4 +40,8 @@
Magento\Catalog\Model\Product\Webapi\Rest\RequestTypeBasedDeserializer
+
+
+
+
diff --git a/app/code/Magento/Catalog/etc/webapi_soap/di.xml b/app/code/Magento/Catalog/etc/webapi_soap/di.xml
index a709f23d8c12b..03671ba0bb2e0 100644
--- a/app/code/Magento/Catalog/etc/webapi_soap/di.xml
+++ b/app/code/Magento/Catalog/etc/webapi_soap/di.xml
@@ -40,4 +40,8 @@
Magento\Framework\Webapi\Rest\Request\Deserializer\Xml
+
+
+
+
diff --git a/app/code/Magento/Catalog/view/adminhtml/web/catalog/product/composite/configure.js b/app/code/Magento/Catalog/view/adminhtml/web/catalog/product/composite/configure.js
index 4040ff9d684f4..d460c93714b3d 100644
--- a/app/code/Magento/Catalog/view/adminhtml/web/catalog/product/composite/configure.js
+++ b/app/code/Magento/Catalog/view/adminhtml/web/catalog/product/composite/configure.js
@@ -47,10 +47,14 @@ define([
* Initialize object
*/
initialize: function () {
- var self = this;
+ var self = this,
+ popupDialog = jQuery('#product_composite_configure');
this._initWindowElements();
jQuery.async('#product_composite_configure', function (el) {
+ if (el !== popupDialog[0]) {
+ el = popupDialog[0];
+ }
self.dialog = jQuery(el).modal({
title: jQuery.mage.__('Configure Product'),
type: 'slide',
diff --git a/app/code/Magento/Checkout/view/frontend/templates/onepage/link.phtml b/app/code/Magento/Checkout/view/frontend/templates/onepage/link.phtml
index b667764ac7bba..c2f99c03c8f4c 100644
--- a/app/code/Magento/Checkout/view/frontend/templates/onepage/link.phtml
+++ b/app/code/Magento/Checkout/view/frontend/templates/onepage/link.phtml
@@ -4,21 +4,27 @@
* See COPYING.txt for license details.
*/
-/** @var $block \Magento\Checkout\Block\Onepage\Link */
+use Magento\Checkout\Block\Onepage\Link;
+use Magento\Framework\Escaper;
+
+/**
+ * @var Link $block
+ * @var Escaper $escaper
+ */
?>
-isPossibleOnepageCheckout()) :?>
+isPossibleOnepageCheckout()): ?>
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/totals.js b/app/code/Magento/Checkout/view/frontend/web/js/model/totals.js
index aba0c31b998d1..fe525bddc02f3 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/model/totals.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/model/totals.js
@@ -22,7 +22,7 @@ define([
quoteItems(newValue.items);
});
- if (quoteSubtotal !== subtotalAmount) {
+ if (!isNaN(subtotalAmount) && quoteSubtotal !== subtotalAmount) {
customerData.reload(['cart'], false);
}
diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerWishlistConfigureItemActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerWishlistConfigureItemActionGroup.xml
new file mode 100644
index 0000000000000..de126e6cc6585
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerWishlistConfigureItemActionGroup.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerWishlistSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerWishlistSection.xml
index 39a67968c66e4..0a44149cb067b 100644
--- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerWishlistSection.xml
+++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerWishlistSection.xml
@@ -14,5 +14,8 @@
+
+
+
diff --git a/app/code/Magento/Downloadable/Model/Link/DeleteHandler.php b/app/code/Magento/Downloadable/Model/Link/DeleteHandler.php
index 399550e5f33c0..c2a3b20ff9e36 100644
--- a/app/code/Magento/Downloadable/Model/Link/DeleteHandler.php
+++ b/app/code/Magento/Downloadable/Model/Link/DeleteHandler.php
@@ -9,7 +9,7 @@
use Magento\Framework\EntityManager\Operation\ExtensionInterface;
/**
- * Class DeleteHandler
+ * Delete Handler for Downloadable Product Links.
*/
class DeleteHandler implements ExtensionInterface
{
@@ -27,6 +27,8 @@ public function __construct(LinkRepository $linkRepository)
}
/**
+ * Delete Downloadable Links for the provided Product.
+ *
* @param object $entity
* @param array $arguments
* @return \Magento\Catalog\Api\Data\ProductInterface|object
@@ -41,6 +43,8 @@ public function execute($entity, $arguments = [])
foreach ($this->linkRepository->getList($entity->getSku()) as $link) {
$this->linkRepository->delete($link->getId());
}
+ $entity->setDownloadableLinks(null);
+
return $entity;
}
}
diff --git a/app/code/Magento/Downloadable/Model/Link/ReadHandler.php b/app/code/Magento/Downloadable/Model/Link/ReadHandler.php
index a11b38ee3afd8..d3a2349739c26 100644
--- a/app/code/Magento/Downloadable/Model/Link/ReadHandler.php
+++ b/app/code/Magento/Downloadable/Model/Link/ReadHandler.php
@@ -9,7 +9,7 @@
use Magento\Framework\EntityManager\Operation\ExtensionInterface;
/**
- * Class ReadHandler
+ * Read Handler for Downloadable Product Links.
*/
class ReadHandler implements ExtensionInterface
{
@@ -27,6 +27,8 @@ public function __construct(LinkRepository $linkRepository)
}
/**
+ * Read Downloadable Links for the provided Product.
+ *
* @param object $entity
* @param array $arguments
* @return \Magento\Catalog\Api\Data\ProductInterface|object
@@ -40,10 +42,9 @@ public function execute($entity, $arguments = [])
}
$entityExtension = $entity->getExtensionAttributes();
$links = $this->linkRepository->getLinksByProduct($entity);
- if ($links) {
- $entityExtension->setDownloadableProductLinks($links);
- }
+ $entityExtension->setDownloadableProductLinks($links);
$entity->setExtensionAttributes($entityExtension);
+
return $entity;
}
}
diff --git a/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AssertStorefrontLinkOnDownloadableProductPageActionGroup.xml b/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AssertStorefrontLinkOnDownloadableProductPageActionGroup.xml
new file mode 100644
index 0000000000000..978fff04938f0
--- /dev/null
+++ b/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AssertStorefrontLinkOnDownloadableProductPageActionGroup.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+ Validates that the provided Link Title is present on Downloadable Product details page.
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AssertStorefrontNoLinkOnDownloadableProductPageActionGroup.xml b/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AssertStorefrontNoLinkOnDownloadableProductPageActionGroup.xml
new file mode 100644
index 0000000000000..8b71839669876
--- /dev/null
+++ b/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AssertStorefrontNoLinkOnDownloadableProductPageActionGroup.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+ Validates that the provided Link Title is NOT present on Downloadable Product details page.
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Downloadable/Test/Mftf/Section/StorefrontDownloadableProductSection.xml b/app/code/Magento/Downloadable/Test/Mftf/Section/StorefrontDownloadableProductSection.xml
index dc2a58be138e7..e3302c7337e09 100644
--- a/app/code/Magento/Downloadable/Test/Mftf/Section/StorefrontDownloadableProductSection.xml
+++ b/app/code/Magento/Downloadable/Test/Mftf/Section/StorefrontDownloadableProductSection.xml
@@ -16,5 +16,6 @@
+
diff --git a/app/code/Magento/Paypal/view/frontend/templates/express/review.phtml b/app/code/Magento/Paypal/view/frontend/templates/express/review.phtml
index 69c7c8179850a..afd1e91c6e588 100644
--- a/app/code/Magento/Paypal/view/frontend/templates/express/review.phtml
+++ b/app/code/Magento/Paypal/view/frontend/templates/express/review.phtml
@@ -4,9 +4,14 @@
* See COPYING.txt for license details.
*/
+use Magento\Framework\Escaper;
+use Magento\Framework\View\Helper\SecureHtmlRenderer;
+use Magento\Paypal\Block\Express\Review;
+
/**
- * @var \Magento\Paypal\Block\Express\Review $block
- * @var \Magento\Framework\View\Helper\SecureHtmlRenderer $secureRenderer
+ * @var Review $block
+ * @var Escaper $escaper
+ * @var SecureHtmlRenderer $secureRenderer
*/
?>
@@ -15,11 +20,11 @@
getShippingAddress()): ?>
- = $block->escapeHtml(__('Shipping Method')) ?>
+ = $escaper->escapeHtml(__('Shipping Method')) ?>
+
- = $block->escapeHtml(__(
+ = $escaper->escapeHtml(__(
'Sorry, no quotes are available for this order right now.'
)); ?>
@@ -80,40 +79,40 @@
- = $block->escapeHtml(__('Shipping Address')) ?>
+ = $escaper->escapeHtml(__('Shipping Address')) ?>
- = $block->escapeHtml(
+ = $escaper->escapeHtml(
$block->renderAddress($block->getShippingAddress()),
['br']
- );?>
+ ); ?>
getCanEditShippingAddress()): ?>
-
= $block->escapeHtml(__('Payment Method')) ?>
+
= $escaper->escapeHtml(__('Payment Method')) ?>
- = $block->escapeHtml($block->getPaymentMethodTitle()) ?>
- = $block->escapeHtml($block->getEmail()) ?>
+ = $escaper->escapeHtml($block->getPaymentMethodTitle()) ?>
+ = $escaper->escapeHtml($block->getEmail()) ?>
- getEditUrl()): ?>
-
-
+ getEditUrl()): ?>
+
+
@@ -124,29 +123,29 @@
= $block->getChildHtml('details') ?>
-
@@ -158,7 +157,7 @@
"orderReview": {
"shippingSubmitFormSelector": "#shipping-method-form",
"shippingSelector": "#shipping-method",
- "shippingMethodUpdateUrl": "= $block->escapeUrl($block->getUpdateShippingMethodsUrl()) ?>",
+ "shippingMethodUpdateUrl": "= $escaper->escapeJs($block->getUpdateShippingMethodsUrl()) ?>",
"isAjax": = /* @noEscape */ $block->getUseAjax() ? 'true' : 'false' ?>,
"canEditShippingMethod": = /* @noEscape */ $block->canEditShippingMethod() ? 'true' : 'false' ?>
}
diff --git a/app/code/Magento/Paypal/view/frontend/web/js/order-review.js b/app/code/Magento/Paypal/view/frontend/web/js/order-review.js
index e3db1010693ee..6a3914ed19d59 100644
--- a/app/code/Magento/Paypal/view/frontend/web/js/order-review.js
+++ b/app/code/Magento/Paypal/view/frontend/web/js/order-review.js
@@ -25,7 +25,6 @@ define([
shippingMethodContainer: '#shipping-method-container',
agreementSelector: 'div.checkout-agreements input',
isAjax: false,
- updateShippingMethodSubmitSelector: '#update-shipping-method-submit',
shippingMethodUpdateUrl: null,
updateOrderSubmitUrl: null,
canEditShippingMethod: false
@@ -55,14 +54,12 @@ define([
this.options.updateOrderSubmitUrl,
this.options.updateContainerSelector
)
- ).find(this.options.updateOrderSelector).on('click', $.proxy(this._updateOrderHandler, this)).end()
- .find(this.options.updateShippingMethodSubmitSelector).hide().end();
+ ).find(this.options.updateOrderSelector).on('click', $.proxy(this._updateOrderHandler, this)).end();
this._shippingTobilling();
if ($(this.options.shippingSubmitFormSelector).length && this.options.canEditShippingMethod) {
this.isShippingSubmitForm = true;
$(this.options.shippingSubmitFormSelector)
- .find(this.options.updateShippingMethodSubmitSelector).hide().end()
.on('change',
this.options.shippingSelector,
$.proxy(
diff --git a/app/code/Magento/Shipping/Block/Adminhtml/Order/Packaging.php b/app/code/Magento/Shipping/Block/Adminhtml/Order/Packaging.php
index ce4521c9baa51..f66b37a9cd34f 100644
--- a/app/code/Magento/Shipping/Block/Adminhtml/Order/Packaging.php
+++ b/app/code/Magento/Shipping/Block/Adminhtml/Order/Packaging.php
@@ -380,7 +380,7 @@ public function getContentTypes()
public function getCustomValueCurrencyCode()
{
$orderInfo = $this->getShipment()->getOrder();
- return $orderInfo->getBaseCurrency()->getCurrencyCode();
+ return $orderInfo->getOrderCurrency()->getCurrencyCode();
}
/**
diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/AdminConfigureCustomerWishListItemTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/AdminConfigureCustomerWishListItemTest.xml
new file mode 100644
index 0000000000000..6c4ddf2bfd006
--- /dev/null
+++ b/app/code/Magento/Wishlist/Test/Mftf/Test/AdminConfigureCustomerWishListItemTest.xml
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/api-functional/testsuite/Magento/GroupedProduct/Api/ProductLinkRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/GroupedProduct/Api/ProductLinkRepositoryTest.php
index efa7341c36a40..c6394ad9561f7 100644
--- a/dev/tests/api-functional/testsuite/Magento/GroupedProduct/Api/ProductLinkRepositoryTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GroupedProduct/Api/ProductLinkRepositoryTest.php
@@ -4,14 +4,18 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\GroupedProduct\Api;
-use Magento\TestFramework\Helper\Bootstrap;
-use Magento\Indexer\Model\Config;
+use Magento\Catalog\Api\ProductLinkManagementInterface;
use Magento\Framework\Indexer\IndexerRegistry;
use Magento\Framework\Webapi\Rest\Request;
+use Magento\Indexer\Model\Config;
+use Magento\TestFramework\Helper\Bootstrap;
+use Magento\TestFramework\TestCase\WebapiAbstract;
-class ProductLinkRepositoryTest extends \Magento\TestFramework\TestCase\WebapiAbstract
+class ProductLinkRepositoryTest extends WebapiAbstract
{
const SERVICE_NAME = 'catalogProductLinkRepositoryV1';
const SERVICE_VERSION = 'V1';
@@ -55,7 +59,7 @@ public function testSave(): void
'linked_product_sku' => 'simple-1',
'position' => 3,
'extension_attributes' => [
- 'qty' => (float) 300.0000,
+ 'qty' => (float)300.0000,
],
];
@@ -72,12 +76,15 @@ public function testSave(): void
];
$this->_webApiCall($serviceInfo, ['entity' => $productData]);
- /** @var \Magento\Catalog\Api\ProductLinkManagementInterface $linkManagement */
- $linkManagement = $this->objectManager->get(\Magento\Catalog\Api\ProductLinkManagementInterface::class);
+ /** @var ProductLinkManagementInterface $linkManagement */
+ $linkManagement = $this->objectManager->get(ProductLinkManagementInterface::class);
$actual = $linkManagement->getLinkedItemsByType($productSku, $linkType);
- array_walk($actual, function (&$item) {
- $item = $item->__toArray();
- });
+ array_walk(
+ $actual,
+ function (&$item) {
+ $item = $item->__toArray();
+ }
+ );
$this->assertEquals($productData, $actual[2]);
}
@@ -98,7 +105,7 @@ public function testLinkWithScheduledIndex(): void
'linked_product_sku' => $productSimple,
'position' => 3,
'extension_attributes' => [
- 'qty' => (float) 300.0000,
+ 'qty' => (float)300.0000,
],
];
$serviceInfo = [
@@ -124,6 +131,101 @@ public function testLinkWithScheduledIndex(): void
$this->restoreIndexMode();
}
+ /**
+ * Verify empty out of stock grouped product is in stock after child has been added.
+ *
+ * @return void
+ * @magentoApiDataFixture Magento/GroupedProduct/_files/empty_grouped_product.php
+ * @magentoApiDataFixture Magento/Catalog/_files/product_virtual.php
+ */
+ public function testGroupedProductIsInStockAfterAddChild(): void
+ {
+ $productSku = 'grouped-product';
+ self::assertFalse($this->isProductInStock($productSku));
+ $items = [
+ 'sku' => $productSku,
+ 'link_type' => 'associated',
+ 'linked_product_type' => 'virtual',
+ 'linked_product_sku' => 'virtual-product',
+ 'position' => 3,
+ 'extension_attributes' => [
+ 'qty' => 1,
+ ],
+ ];
+ $serviceInfo = [
+ 'rest' => [
+ 'resourcePath' => self::RESOURCE_PATH . $productSku . '/links',
+ 'httpMethod' => Request::HTTP_METHOD_PUT,
+ ],
+ 'soap' => [
+ 'service' => self::SERVICE_NAME,
+ 'serviceVersion' => self::SERVICE_VERSION,
+ 'operation' => self::SERVICE_NAME . 'Save',
+ ],
+ ];
+ $this->_webApiCall($serviceInfo, ['entity' => $items]);
+ self::assertTrue($this->isProductInStock($productSku));
+ }
+
+ /**
+ * Verify in stock grouped product is out stock after children have been removed.
+ *
+ * @return void
+ * @magentoApiDataFixture Magento/GroupedProduct/_files/product_grouped_with_simple.php
+ */
+ public function testGroupedProductIsOutOfStockAfterRemoveChild(): void
+ {
+ $productSku = 'grouped';
+ $childrenSkus = [
+ 'simple_11',
+ 'simple_22',
+ ];
+ self::assertTrue($this->isProductInStock($productSku));
+
+ foreach ($childrenSkus as $childSku) {
+ $serviceInfo = [
+ 'rest' => [
+ 'resourcePath' => self::RESOURCE_PATH . $productSku . '/links/associated/' . $childSku,
+ 'httpMethod' => Request::HTTP_METHOD_DELETE,
+ ],
+ 'soap' => [
+ 'service' => self::SERVICE_NAME,
+ 'serviceVersion' => self::SERVICE_VERSION,
+ 'operation' => self::SERVICE_NAME . 'DeleteById',
+ ],
+ ];
+ $requestData = ['sku' => $productSku, 'type' => 'associated', 'linkedProductSku' => $childSku];
+ $this->_webApiCall($serviceInfo, $requestData);
+ }
+
+ self::assertFalse($this->isProductInStock($productSku));
+ }
+
+
+ /**
+ * Check product stock status.
+ *
+ * @param string $productSku
+ * @return bool
+ */
+ private function isProductInStock(string $productSku): bool
+ {
+ $serviceInfo = [
+ 'rest' => [
+ 'resourcePath' => '/V1/stockStatuses/' . $productSku,
+ 'httpMethod' => Request::HTTP_METHOD_GET,
+ ],
+ 'soap' => [
+ 'service' => 'catalogInventoryStockRegistryV1',
+ 'serviceVersion' => self::SERVICE_VERSION,
+ 'operation' => 'catalogInventoryStockRegistryV1getStockStatusBySku',
+ ],
+ ];
+ $result = $this->_webApiCall($serviceInfo, ['productSku' => $productSku]);
+
+ return (bool)$result['stock_status'];
+ }
+
/**
* @param string $productSku
* @return array
@@ -139,11 +241,11 @@ private function buildSearchCriteria(string $productSku): array
[
'field' => 'search_term',
'value' => $productSku,
- ]
- ]
- ]
- ]
- ]
+ ],
+ ],
+ ],
+ ],
+ ],
];
}
@@ -156,13 +258,13 @@ private function buildSearchServiceInfo(array $searchCriteria): array
return [
'rest' => [
'resourcePath' => self::RESOURCE_PATH_SEARCH . '?' . http_build_query($searchCriteria),
- 'httpMethod' => Request::HTTP_METHOD_GET
+ 'httpMethod' => Request::HTTP_METHOD_GET,
],
'soap' => [
'service' => self::SERVICE_NAME_SEARCH,
'serviceVersion' => self::SERVICE_VERSION,
- 'operation' => self::SERVICE_NAME_SEARCH . 'Search'
- ]
+ 'operation' => self::SERVICE_NAME_SEARCH . 'Search',
+ ],
];
}
diff --git a/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/empty_grouped_product.php b/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/empty_grouped_product.php
new file mode 100644
index 0000000000000..d02894866fb33
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/empty_grouped_product.php
@@ -0,0 +1,28 @@
+get(ProductRepositoryInterface::class);
+$product = Bootstrap::getObjectManager()->create(Product::class);
+$product->isObjectNew(true);
+$product->setTypeId(Grouped::TYPE_CODE)
+ ->setAttributeSetId(4)
+ ->setWebsiteIds([1])
+ ->setName('Grouped Product')
+ ->setSku('grouped-product')
+ ->setPrice(100)
+ ->setTaxClassId(0)
+ ->setVisibility(Visibility::VISIBILITY_BOTH)
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setStockData(['use_config_manage_stock' => 1, 'qty' => 0, 'is_in_stock' => 1])
+ ->save();
diff --git a/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/empty_grouped_product_rollback.php b/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/empty_grouped_product_rollback.php
new file mode 100644
index 0000000000000..522579ae5469c
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/empty_grouped_product_rollback.php
@@ -0,0 +1,23 @@
+get(Registry::class);
+$productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class);
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+try {
+ $groupedProduct = $productRepository->get('grouped-product', false, null, true);
+ $groupedProduct->delete();
+} catch (NoSuchEntityException $e) {
+ //already deleted
+}
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Shipping/Block/Adminhtml/Order/AddToPackageTest.php b/dev/tests/integration/testsuite/Magento/Shipping/Block/Adminhtml/Order/AddToPackageTest.php
index fbbc6ef25cc09..ebda955569e52 100644
--- a/dev/tests/integration/testsuite/Magento/Shipping/Block/Adminhtml/Order/AddToPackageTest.php
+++ b/dev/tests/integration/testsuite/Magento/Shipping/Block/Adminhtml/Order/AddToPackageTest.php
@@ -3,6 +3,8 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Shipping\Block\Adminhtml\Order;
use Magento\Backend\Block\Template;
@@ -28,12 +30,19 @@ class AddToPackageTest extends TestCase
*/
private $orderRepository;
- /** @var ObjectManagerInterface */
+ /**
+ * @var ObjectManagerInterface
+ */
private $objectManager;
- /** @var Registry */
+ /**
+ * @var Registry
+ */
private $registry;
+ /**
+ * @inheritDoc
+ */
protected function setUp(): void
{
$this->objectManager = Bootstrap::getObjectManager();
@@ -42,30 +51,45 @@ protected function setUp(): void
}
/**
- * Loads order entity by provided order increment ID.
+ * Test that Packaging popup renders
*
- * @param string $incrementId
- * @return OrderInterface
+ * @magentoDataFixture Magento/Shipping/_files/shipping_with_carrier_data.php
*/
- private function getOrderByIncrementId(string $incrementId) : OrderInterface
+ public function testGetCommentsHtml(): void
{
- /** @var SearchCriteria $searchCriteria */
- $searchCriteria = $this->objectManager->get(SearchCriteriaBuilder::class)
- ->addFilter('increment_id', $incrementId)
- ->create();
-
- $items = $this->orderRepository->getList($searchCriteria)
- ->getItems();
+ $expectedNeedle = "packaging.setItemQtyCallback(function(itemId){
+ var item = $$('[name=\"shipment[items]['+itemId+']\"]')[0],
+ itemTitle = $('order_item_' + itemId + '_title');
+ if (!itemTitle && !item) {
+ return 0;
+ }
+ if (item && !isNaN(item.value)) {
+ return item.value;
+ }
+ });";
+ $this->assertStringContainsString($expectedNeedle, $this->getHtml());
+ }
- return array_pop($items);
+ /**
+ * Verify currency code on custom value field
+ *
+ * @magentoDataFixture Magento/Shipping/_files/shipping_with_carrier_data_different_currency_code.php
+ */
+ public function testGetCurrencyCodeCustomValue ()
+ {
+ $template = '/\s*?(?[A-Za-z]+)\s*?<\/span>/';
+ $matches = [];
+ preg_match($template, $this->getHtml(), $matches);
+ $currency = $matches['currency'] ?? null;
+ $this->assertEquals('FR',$currency );
}
/**
- * Test that Packaging popup renders
+ * Get html for packaging popup
*
- * @magentoDataFixture Magento/Shipping/_files/shipping_with_carrier_data.php
+ * @return string
*/
- public function testGetCommentsHtml()
+ private function getHtml()
{
/** @var Template $block */
$block = $this->objectManager->get(Packaging::class);
@@ -78,17 +102,26 @@ public function testGetCommentsHtml()
$this->registry->register('current_shipment', $shipment);
$block->setTemplate('Magento_Shipping::order/packaging/popup.phtml');
- $html = $block->toHtml();
- $expectedNeedle = "packaging.setItemQtyCallback(function(itemId){
- var item = $$('[name=\"shipment[items]['+itemId+']\"]')[0],
- itemTitle = $('order_item_' + itemId + '_title');
- if (!itemTitle && !item) {
- return 0;
- }
- if (item && !isNaN(item.value)) {
- return item.value;
- }
- });";
- $this->assertStringContainsString($expectedNeedle, $html);
+
+ return $block->toHtml();
+ }
+
+ /**
+ * Loads order entity by provided order increment ID.
+ *
+ * @param string $incrementId
+ * @return OrderInterface
+ */
+ private function getOrderByIncrementId(string $incrementId) : OrderInterface
+ {
+ /** @var SearchCriteria $searchCriteria */
+ $searchCriteria = $this->objectManager->get(SearchCriteriaBuilder::class)
+ ->addFilter('increment_id', $incrementId)
+ ->create();
+
+ $items = $this->orderRepository->getList($searchCriteria)
+ ->getItems();
+
+ return array_pop($items);
}
}
diff --git a/dev/tests/integration/testsuite/Magento/Shipping/_files/shipping_with_carrier_data_different_currency_code.php b/dev/tests/integration/testsuite/Magento/Shipping/_files/shipping_with_carrier_data_different_currency_code.php
new file mode 100644
index 0000000000000..ac54274ba0262
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Shipping/_files/shipping_with_carrier_data_different_currency_code.php
@@ -0,0 +1,50 @@
+requireDataFixture('Magento/Sales/_files/order_with_customer.php');
+
+$objectManager = Bootstrap::getObjectManager();
+/** @var Transaction $transaction */
+$transaction = $objectManager->get(Transaction::class);
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+$product = $productRepository->get('simple');
+/** @var Order $order */
+$order = $objectManager->get(OrderInterfaceFactory::class)->create()->loadByIncrementId('100000001');
+$order->setShippingDescription('UPS Next Day Air')
+ ->setShippingMethod('ups_11')
+ ->setOrderCurrencyCode('FR')
+ ->setShippingAmount(0)
+ ->setCouponCode('1234567890')
+ ->setDiscountDescription('1234567890');
+
+/** @var OrderRepositoryInterface $orderRepository */
+$orderRepository = $objectManager->create(OrderRepositoryInterface::class);
+$orderRepository->save($order);
+
+$shipmentItems = [];
+foreach ($order->getItems() as $orderItem) {
+ $shipmentItems[$orderItem->getId()] = $orderItem->getQtyOrdered();
+}
+$tracking = [
+ 'carrier_code' => 'ups',
+ 'title' => 'United Parcel Service',
+ 'number' => '987654321',
+];
+
+$shipment = $objectManager->get(ShipmentFactory::class)->create($order, $shipmentItems, [$tracking]);
+$shipment->register();
+$transaction->addObject($shipment)->addObject($order)->save();
diff --git a/dev/tests/integration/testsuite/Magento/Shipping/_files/shipping_with_carrier_data_different_currency_code_rollback.php b/dev/tests/integration/testsuite/Magento/Shipping/_files/shipping_with_carrier_data_different_currency_code_rollback.php
new file mode 100644
index 0000000000000..bbb90e0326aec
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Shipping/_files/shipping_with_carrier_data_different_currency_code_rollback.php
@@ -0,0 +1,9 @@
+requireDataFixture('Magento/Sales/_files/order_with_customer_rollback.php');
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/totals.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/totals.test.js
new file mode 100644
index 0000000000000..b411bd457b548
--- /dev/null
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/totals.test.js
@@ -0,0 +1,86 @@
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+/* eslint-disable max-nested-callbacks */
+/*jscs:disable jsDoc*/
+define([
+ 'squire', 'jquery', 'ko'
+], function (Squire, $, ko) {
+ 'use strict';
+
+ var injector = new Squire(),
+ cartData = {
+ 'subtotalAmount': 10
+ },
+ cart = ko.observable(cartData),
+ cartDataTwo = {
+ 'subtotalAmount': NaN
+ },
+ cartTwo = ko.observable(cartDataTwo),
+ mocks = {
+ 'Magento_Checkout/js/model/quote': {
+ totals: ko.observable({
+ 'subtotal': 4
+ })
+ },
+ 'Magento_Customer/js/customer-data': {
+ get: function () {
+ return cart;
+ },
+ reload: jasmine.createSpy(),
+ getInitCustomerData: function () {}
+ }
+ },
+ mocksTwo = {
+ 'Magento_Checkout/js/model/quote': {
+ totals: ko.observable({
+ 'subtotal': 10
+ })
+ },
+ 'Magento_Customer/js/customer-data': {
+ get: function () {
+ return cartTwo;
+ },
+ reload: jasmine.createSpy(),
+ getInitCustomerData: function () {}
+ }
+ };
+
+ afterEach(function () {
+ try {
+ injector.clean();
+ injector.remove();
+ } catch (e) {}
+ });
+
+ describe('Test that customer data is reloaded when quote subtotal and cart subtotal are different', function () {
+ beforeEach(function (done) {
+ injector.mock(mocks);
+ injector.require(['Magento_Checkout/js/model/totals'], function () {
+ done();
+ });
+ });
+ it('Test that customer data is reloaded when quote subtotal and cart subtotal are different', function () {
+ expect(mocks['Magento_Checkout/js/model/quote'].totals().subtotal).toBe(4);
+ expect(cart().subtotalAmount).toBe(10);
+ expect(mocks['Magento_Customer/js/customer-data'].reload).toHaveBeenCalled();
+ });
+ });
+
+ describe('Test that customer data is not reloaded when cart subtotal is NaN', function () {
+ beforeEach(function (done) {
+ injector.mock(mocksTwo);
+ injector.require(['Magento_Checkout/js/model/totals'], function () {
+ done();
+ });
+ });
+ it('Test that customer data is not reloaded when cart subtotal is NaN', function () {
+ expect(mocksTwo['Magento_Checkout/js/model/quote'].totals().subtotal).toBe(10);
+ expect(cartTwo().subtotalAmount).toBeNaN();
+ expect(mocksTwo['Magento_Customer/js/customer-data'].reload).not.toHaveBeenCalled();
+ });
+ });
+});
+