diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Attributes.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Attributes.php
index a08142c10be5e..2df0ff0b6cd7c 100644
--- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Attributes.php
+++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Attributes.php
@@ -64,17 +64,6 @@ public function __construct(
parent::__construct($context, $registry, $formFactory, $data);
}
- /**
- * Construct block
- *
- * @return void
- */
- protected function _construct()
- {
- parent::_construct();
- $this->setShowGlobalIcon(true);
- }
-
/**
* Prepares form
*
diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php
index e1208e25cc7c8..063503682f4db 100644
--- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php
+++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php
@@ -13,10 +13,12 @@
*/
namespace Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Gallery;
+use Magento\Framework\App\ObjectManager;
use Magento\Backend\Block\Media\Uploader;
use Magento\Framework\View\Element\AbstractBlock;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\Exception\FileSystemException;
+use Magento\Backend\Block\DataProviders\ImageUploadConfig as ImageUploadConfigDataProvider;
/**
* Block for gallery content.
@@ -43,21 +45,30 @@ class Content extends \Magento\Backend\Block\Widget
*/
private $imageHelper;
+ /**
+ * @var ImageUploadConfigDataProvider
+ */
+ private $imageUploadConfigDataProvider;
+
/**
* @param \Magento\Backend\Block\Template\Context $context
* @param \Magento\Framework\Json\EncoderInterface $jsonEncoder
* @param \Magento\Catalog\Model\Product\Media\Config $mediaConfig
* @param array $data
+ * @param ImageUploadConfigDataProvider $imageUploadConfigDataProvider
*/
public function __construct(
\Magento\Backend\Block\Template\Context $context,
\Magento\Framework\Json\EncoderInterface $jsonEncoder,
\Magento\Catalog\Model\Product\Media\Config $mediaConfig,
- array $data = []
+ array $data = [],
+ ImageUploadConfigDataProvider $imageUploadConfigDataProvider = null
) {
$this->_jsonEncoder = $jsonEncoder;
$this->_mediaConfig = $mediaConfig;
parent::__construct($context, $data);
+ $this->imageUploadConfigDataProvider = $imageUploadConfigDataProvider
+ ?: ObjectManager::getInstance()->get(ImageUploadConfigDataProvider::class);
}
/**
@@ -67,7 +78,11 @@ public function __construct(
*/
protected function _prepareLayout()
{
- $this->addChild('uploader', \Magento\Backend\Block\Media\Uploader::class);
+ $this->addChild(
+ 'uploader',
+ \Magento\Backend\Block\Media\Uploader::class,
+ ['image_upload_config_data' => $this->imageUploadConfigDataProvider]
+ );
$this->getUploader()->getConfig()->setUrl(
$this->_urlBuilder->addSessionParam()->getUrl('catalog/product_gallery/upload')
diff --git a/app/code/Magento/Catalog/Block/Product/Compare/ListCompare.php b/app/code/Magento/Catalog/Block/Product/Compare/ListCompare.php
index 6c54aa4e171ef..76f5dbd1bea88 100644
--- a/app/code/Magento/Catalog/Block/Product/Compare/ListCompare.php
+++ b/app/code/Magento/Catalog/Block/Product/Compare/ListCompare.php
@@ -122,12 +122,7 @@ public function __construct(
*/
public function getAddToWishlistParams($product)
{
- $continueUrl = $this->urlEncoder->encode($this->getUrl('customer/account'));
- $urlParamName = Action::PARAM_NAME_URL_ENCODED;
-
- $continueUrlParams = [$urlParamName => $continueUrl];
-
- return $this->_wishlistHelper->getAddParams($product, $continueUrlParams);
+ return $this->_wishlistHelper->getAddParams($product);
}
/**
diff --git a/app/code/Magento/Catalog/Block/Product/ProductList/Crosssell.php b/app/code/Magento/Catalog/Block/Product/ProductList/Crosssell.php
index 0c547f81c85d6..596cd7cc5bdce 100644
--- a/app/code/Magento/Catalog/Block/Product/ProductList/Crosssell.php
+++ b/app/code/Magento/Catalog/Block/Product/ProductList/Crosssell.php
@@ -9,6 +9,9 @@
*/
namespace Magento\Catalog\Block\Product\ProductList;
+/**
+ * Crosssell block for product
+ */
class Crosssell extends \Magento\Catalog\Block\Product\AbstractProduct
{
/**
@@ -25,7 +28,7 @@ class Crosssell extends \Magento\Catalog\Block\Product\AbstractProduct
*/
protected function _prepareData()
{
- $product = $this->_coreRegistry->registry('product');
+ $product = $this->getProduct();
/* @var $product \Magento\Catalog\Model\Product */
$this->_itemCollection = $product->getCrossSellProductCollection()->addAttributeToSelect(
@@ -43,6 +46,7 @@ protected function _prepareData()
/**
* Before rendering html process
+ *
* Prepare items collection
*
* @return \Magento\Catalog\Block\Product\ProductList\Crosssell
diff --git a/app/code/Magento/Catalog/Block/Product/ProductList/Related.php b/app/code/Magento/Catalog/Block/Product/ProductList/Related.php
index 219922f9e46d5..6de70bb971367 100644
--- a/app/code/Magento/Catalog/Block/Product/ProductList/Related.php
+++ b/app/code/Magento/Catalog/Block/Product/ProductList/Related.php
@@ -77,11 +77,13 @@ public function __construct(
}
/**
+ * Prepare data
+ *
* @return $this
*/
protected function _prepareData()
{
- $product = $this->_coreRegistry->registry('product');
+ $product = $this->getProduct();
/* @var $product \Magento\Catalog\Model\Product */
$this->_itemCollection = $product->getRelatedProductCollection()->addAttributeToSelect(
@@ -103,6 +105,8 @@ protected function _prepareData()
}
/**
+ * Before to html handler
+ *
* @return $this
*/
protected function _beforeToHtml()
@@ -112,6 +116,8 @@ protected function _beforeToHtml()
}
/**
+ * Get collection items
+ *
* @return Collection
*/
public function getItems()
diff --git a/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php b/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php
index 0b8d2d6c89e72..c530ba4785ad9 100644
--- a/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php
+++ b/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php
@@ -7,6 +7,8 @@
use Magento\Catalog\Helper\Product\ProductList;
use Magento\Catalog\Model\Product\ProductList\Toolbar as ToolbarModel;
+use Magento\Catalog\Model\Product\ProductList\ToolbarMemorizer;
+use Magento\Framework\App\ObjectManager;
/**
* Product list toolbar
@@ -77,6 +79,7 @@ class Toolbar extends \Magento\Framework\View\Element\Template
/**
* @var bool $_paramsMemorizeAllowed
+ * @deprecated
*/
protected $_paramsMemorizeAllowed = true;
@@ -96,6 +99,7 @@ class Toolbar extends \Magento\Framework\View\Element\Template
* Catalog session
*
* @var \Magento\Catalog\Model\Session
+ * @deprecated
*/
protected $_catalogSession;
@@ -104,6 +108,11 @@ class Toolbar extends \Magento\Framework\View\Element\Template
*/
protected $_toolbarModel;
+ /**
+ * @var ToolbarMemorizer
+ */
+ private $toolbarMemorizer;
+
/**
* @var ProductList
*/
@@ -119,6 +128,16 @@ class Toolbar extends \Magento\Framework\View\Element\Template
*/
protected $_postDataHelper;
+ /**
+ * @var \Magento\Framework\App\Http\Context
+ */
+ private $httpContext;
+
+ /**
+ * @var \Magento\Framework\Data\Form\FormKey
+ */
+ private $formKey;
+
/**
* @param \Magento\Framework\View\Element\Template\Context $context
* @param \Magento\Catalog\Model\Session $catalogSession
@@ -128,6 +147,11 @@ class Toolbar extends \Magento\Framework\View\Element\Template
* @param ProductList $productListHelper
* @param \Magento\Framework\Data\Helper\PostHelper $postDataHelper
* @param array $data
+ * @param ToolbarMemorizer|null $toolbarMemorizer
+ * @param \Magento\Framework\App\Http\Context|null $httpContext
+ * @param \Magento\Framework\Data\Form\FormKey|null $formKey
+ *
+ * @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
\Magento\Framework\View\Element\Template\Context $context,
@@ -137,7 +161,10 @@ public function __construct(
\Magento\Framework\Url\EncoderInterface $urlEncoder,
ProductList $productListHelper,
\Magento\Framework\Data\Helper\PostHelper $postDataHelper,
- array $data = []
+ array $data = [],
+ ToolbarMemorizer $toolbarMemorizer = null,
+ \Magento\Framework\App\Http\Context $httpContext = null,
+ \Magento\Framework\Data\Form\FormKey $formKey = null
) {
$this->_catalogSession = $catalogSession;
$this->_catalogConfig = $catalogConfig;
@@ -145,6 +172,15 @@ public function __construct(
$this->urlEncoder = $urlEncoder;
$this->_productListHelper = $productListHelper;
$this->_postDataHelper = $postDataHelper;
+ $this->toolbarMemorizer = $toolbarMemorizer ?: ObjectManager::getInstance()->get(
+ ToolbarMemorizer::class
+ );
+ $this->httpContext = $httpContext ?: ObjectManager::getInstance()->get(
+ \Magento\Framework\App\Http\Context::class
+ );
+ $this->formKey = $formKey ?: ObjectManager::getInstance()->get(
+ \Magento\Framework\Data\Form\FormKey::class
+ );
parent::__construct($context, $data);
}
@@ -152,6 +188,7 @@ public function __construct(
* Disable list state params memorizing
*
* @return $this
+ * @deprecated
*/
public function disableParamsMemorizing()
{
@@ -165,6 +202,7 @@ public function disableParamsMemorizing()
* @param string $param parameter name
* @param mixed $value parameter value
* @return $this
+ * @deprecated
*/
protected function _memorizeParam($param, $value)
{
@@ -244,13 +282,13 @@ public function getCurrentOrder()
$defaultOrder = $keys[0];
}
- $order = $this->_toolbarModel->getOrder();
+ $order = $this->toolbarMemorizer->getOrder();
if (!$order || !isset($orders[$order])) {
$order = $defaultOrder;
}
- if ($order != $defaultOrder) {
- $this->_memorizeParam('sort_order', $order);
+ if ($this->toolbarMemorizer->isMemorizingAllowed()) {
+ $this->httpContext->setValue(ToolbarModel::ORDER_PARAM_NAME, $order, $defaultOrder);
}
$this->setData('_current_grid_order', $order);
@@ -270,13 +308,13 @@ public function getCurrentDirection()
}
$directions = ['asc', 'desc'];
- $dir = strtolower($this->_toolbarModel->getDirection());
+ $dir = strtolower($this->toolbarMemorizer->getDirection());
if (!$dir || !in_array($dir, $directions)) {
$dir = $this->_direction;
}
- if ($dir != $this->_direction) {
- $this->_memorizeParam('sort_direction', $dir);
+ if ($this->toolbarMemorizer->isMemorizingAllowed()) {
+ $this->httpContext->setValue(ToolbarModel::DIRECTION_PARAM_NAME, $dir, $this->_direction);
}
$this->setData('_current_grid_direction', $dir);
@@ -392,6 +430,8 @@ public function getPagerUrl($params = [])
}
/**
+ * Get pager encoded url.
+ *
* @param array $params
* @return string
*/
@@ -412,11 +452,15 @@ public function getCurrentMode()
return $mode;
}
$defaultMode = $this->_productListHelper->getDefaultViewMode($this->getModes());
- $mode = $this->_toolbarModel->getMode();
+ $mode = $this->toolbarMemorizer->getMode();
if (!$mode || !isset($this->_availableMode[$mode])) {
$mode = $defaultMode;
}
+ if ($this->toolbarMemorizer->isMemorizingAllowed()) {
+ $this->httpContext->setValue(ToolbarModel::MODE_PARAM_NAME, $mode, $defaultMode);
+ }
+
$this->setData('_current_grid_mode', $mode);
return $mode;
}
@@ -568,13 +612,13 @@ public function getLimit()
$defaultLimit = $keys[0];
}
- $limit = $this->_toolbarModel->getLimit();
+ $limit = $this->toolbarMemorizer->getLimit();
if (!$limit || !isset($limits[$limit])) {
$limit = $defaultLimit;
}
- if ($limit != $defaultLimit) {
- $this->_memorizeParam('limit_page', $limit);
+ if ($this->toolbarMemorizer->isMemorizingAllowed()) {
+ $this->httpContext->setValue(ToolbarModel::LIMIT_PARAM_NAME, $limit, $defaultLimit);
}
$this->setData('_current_limit', $limit);
@@ -582,6 +626,8 @@ public function getLimit()
}
/**
+ * Check if limit is current used in toolbar.
+ *
* @param int $limit
* @return bool
*/
@@ -591,6 +637,8 @@ public function isLimitCurrent($limit)
}
/**
+ * Pager number of items from which products started on current page.
+ *
* @return int
*/
public function getFirstNum()
@@ -600,6 +648,8 @@ public function getFirstNum()
}
/**
+ * Pager number of items products finished on current page.
+ *
* @return int
*/
public function getLastNum()
@@ -609,6 +659,8 @@ public function getLastNum()
}
/**
+ * Total number of products in current category.
+ *
* @return int
*/
public function getTotalNum()
@@ -617,6 +669,8 @@ public function getTotalNum()
}
/**
+ * Check if current page is the first.
+ *
* @return bool
*/
public function isFirstPage()
@@ -625,6 +679,8 @@ public function isFirstPage()
}
/**
+ * Return last page number.
+ *
* @return int
*/
public function getLastPageNum()
@@ -692,6 +748,8 @@ public function getWidgetOptionsJson(array $customOptions = [])
'orderDefault' => $this->getOrderField(),
'limitDefault' => $this->_productListHelper->getDefaultLimitPerPageValue($defaultMode),
'url' => $this->getPagerUrl(),
+ 'formKey' => $this->formKey->getFormKey(),
+ 'post' => $this->toolbarMemorizer->isMemorizingAllowed() ? true : false
];
$options = array_replace_recursive($options, $customOptions);
return json_encode(['productListToolbarForm' => $options]);
diff --git a/app/code/Magento/Catalog/Block/Product/ProductList/Upsell.php b/app/code/Magento/Catalog/Block/Product/ProductList/Upsell.php
index 0d64ecc9bff90..24822447ae915 100644
--- a/app/code/Magento/Catalog/Block/Product/ProductList/Upsell.php
+++ b/app/code/Magento/Catalog/Block/Product/ProductList/Upsell.php
@@ -91,11 +91,13 @@ public function __construct(
}
/**
+ * Prepare data
+ *
* @return $this
*/
protected function _prepareData()
{
- $product = $this->_coreRegistry->registry('product');
+ $product = $this->getProduct();
/* @var $product \Magento\Catalog\Model\Product */
$this->_itemCollection = $product->getUpSellProductCollection()->setPositionOrder()->addStoreFilter();
if ($this->moduleManager->isEnabled('Magento_Checkout')) {
@@ -121,6 +123,8 @@ protected function _prepareData()
}
/**
+ * Before to html handler
+ *
* @return $this
*/
protected function _beforeToHtml()
@@ -130,6 +134,8 @@ protected function _beforeToHtml()
}
/**
+ * Get items collection
+ *
* @return Collection
*/
public function getItemCollection()
@@ -145,6 +151,8 @@ public function getItemCollection()
}
/**
+ * Get collection items
+ *
* @return \Magento\Framework\DataObject[]
*/
public function getItems()
@@ -156,6 +164,8 @@ public function getItems()
}
/**
+ * Get row count
+ *
* @return float
*/
public function getRowCount()
@@ -164,6 +174,8 @@ public function getRowCount()
}
/**
+ * Set column count
+ *
* @param string $columns
* @return $this
*/
@@ -176,6 +188,8 @@ public function setColumnCount($columns)
}
/**
+ * Get column count
+ *
* @return int
*/
public function getColumnCount()
@@ -184,6 +198,8 @@ public function getColumnCount()
}
/**
+ * Reset items iterator
+ *
* @return void
*/
public function resetItemsIterator()
@@ -193,6 +209,8 @@ public function resetItemsIterator()
}
/**
+ * Get iterable item
+ *
* @return mixed
*/
public function getIterableItem()
@@ -204,6 +222,7 @@ public function getIterableItem()
/**
* Set how many items we need to show in upsell block
+ *
* Notice: this parameter will be also applied
*
* @param string $type
@@ -219,6 +238,8 @@ public function setItemLimit($type, $limit)
}
/**
+ * Get item limit
+ *
* @param string $type
* @return array|int
*/
diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php
index d82f4a04fb252..f11d16755ef0d 100644
--- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php
+++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php
@@ -19,6 +19,8 @@
use Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper\AttributeFilter;
/**
+ * Product helper
+ *
* @api
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
* @since 100.0.2
@@ -365,6 +367,8 @@ private function overwriteValue($optionId, $option, $overwriteOptions)
}
/**
+ * Get link resolver instance
+ *
* @return LinkResolver
* @deprecated 101.0.0
*/
@@ -377,6 +381,8 @@ private function getLinkResolver()
}
/**
+ * Get DateTimeFilter instance
+ *
* @return \Magento\Framework\Stdlib\DateTime\Filter\DateTime
* @deprecated 101.0.0
*/
@@ -391,6 +397,7 @@ private function getDateTimeFilter()
/**
* Remove ids of non selected websites from $websiteIds array and return filtered data
+ *
* $websiteIds parameter expects array with website ids as keys and 1 (selected) or 0 (non selected) as values
* Only one id (default website ID) will be set to $websiteIds array when the single store mode is turned on
*
@@ -463,6 +470,7 @@ private function fillProductOptions(Product $product, array $productOptions)
private function convertSpecialFromDateStringToObject($productData)
{
if (isset($productData['special_from_date']) && $productData['special_from_date'] != '') {
+ $productData['special_from_date'] = $this->getDateTimeFilter()->filter($productData['special_from_date']);
$productData['special_from_date'] = new \DateTime($productData['special_from_date']);
}
diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassStatus.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassStatus.php
index b7655f7ee2862..9d7273fb3f23c 100644
--- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassStatus.php
+++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassStatus.php
@@ -14,6 +14,7 @@
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory;
/**
+ * Updates status for a batch of products.
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class MassStatus extends \Magento\Catalog\Controller\Adminhtml\Product implements HttpPostActionInterface
@@ -87,7 +88,7 @@ public function execute()
$filterRequest = $this->getRequest()->getParam('filters', null);
$status = (int) $this->getRequest()->getParam('status');
- if (null !== $storeId && null !== $filterRequest) {
+ if (null === $storeId && null !== $filterRequest) {
$storeId = (isset($filterRequest['store_id'])) ? (int) $filterRequest['store_id'] : 0;
}
diff --git a/app/code/Magento/Catalog/Controller/Category/View.php b/app/code/Magento/Catalog/Controller/Category/View.php
index 19243aabb1b71..2088bb5ea77cd 100644
--- a/app/code/Magento/Catalog/Controller/Category/View.php
+++ b/app/code/Magento/Catalog/Controller/Category/View.php
@@ -10,6 +10,7 @@
use Magento\Framework\App\Action\HttpGetActionInterface;
use Magento\Catalog\Api\CategoryRepositoryInterface;
use Magento\Catalog\Model\Layer\Resolver;
+use Magento\Catalog\Model\Product\ProductList\ToolbarMemorizer;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\View\Result\PageFactory;
use Magento\Framework\App\Action\Action;
@@ -74,6 +75,11 @@ class View extends Action implements HttpGetActionInterface, HttpPostActionInter
*/
protected $categoryRepository;
+ /**
+ * @var ToolbarMemorizer
+ */
+ private $toolbarMemorizer;
+
/**
* Constructor
*
@@ -87,6 +93,7 @@ class View extends Action implements HttpGetActionInterface, HttpPostActionInter
* @param \Magento\Framework\Controller\Result\ForwardFactory $resultForwardFactory
* @param Resolver $layerResolver
* @param CategoryRepositoryInterface $categoryRepository
+ * @param ToolbarMemorizer|null $toolbarMemorizer
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
@@ -99,7 +106,8 @@ public function __construct(
PageFactory $resultPageFactory,
\Magento\Framework\Controller\Result\ForwardFactory $resultForwardFactory,
Resolver $layerResolver,
- CategoryRepositoryInterface $categoryRepository
+ CategoryRepositoryInterface $categoryRepository,
+ ToolbarMemorizer $toolbarMemorizer = null
) {
parent::__construct($context);
$this->_storeManager = $storeManager;
@@ -111,6 +119,7 @@ public function __construct(
$this->resultForwardFactory = $resultForwardFactory;
$this->layerResolver = $layerResolver;
$this->categoryRepository = $categoryRepository;
+ $this->toolbarMemorizer = $toolbarMemorizer ?: $context->getObjectManager()->get(ToolbarMemorizer::class);
}
/**
@@ -135,6 +144,7 @@ protected function _initCategory()
}
$this->_catalogSession->setLastVisitedCategoryId($category->getId());
$this->_coreRegistry->register('current_category', $category);
+ $this->toolbarMemorizer->memorizeParams();
try {
$this->_eventManager->dispatch(
'catalog_controller_category_init_after',
@@ -198,7 +208,7 @@ public function execute()
if ($layoutUpdates && is_array($layoutUpdates)) {
foreach ($layoutUpdates as $layoutUpdate) {
$page->addUpdate($layoutUpdate);
- $page->addPageLayoutHandles(['layout_update' => md5($layoutUpdate)], null, false);
+ $page->addPageLayoutHandles(['layout_update' => sha1($layoutUpdate)], null, false);
}
}
diff --git a/app/code/Magento/Catalog/Controller/Product/Compare.php b/app/code/Magento/Catalog/Controller/Product/Compare.php
index 1ee146e5aaa70..084a82f87d645 100644
--- a/app/code/Magento/Catalog/Controller/Product/Compare.php
+++ b/app/code/Magento/Catalog/Controller/Product/Compare.php
@@ -6,6 +6,7 @@
namespace Magento\Catalog\Controller\Product;
use Magento\Catalog\Api\ProductRepositoryInterface;
+use Magento\Framework\App\Action\HttpGetActionInterface;
use Magento\Framework\Data\Form\FormKey\Validator;
use Magento\Framework\View\Result\PageFactory;
@@ -15,7 +16,7 @@
* @SuppressWarnings(PHPMD.LongVariable)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
-abstract class Compare extends \Magento\Framework\App\Action\Action
+abstract class Compare extends \Magento\Framework\App\Action\Action implements HttpGetActionInterface
{
/**
* Customer id
@@ -139,4 +140,15 @@ public function setCustomerId($customerId)
$this->_customerId = $customerId;
return $this;
}
+
+ /**
+ * @inheritdoc
+ */
+ public function execute()
+ {
+ $resultRedirect = $this->resultRedirectFactory->create();
+ $resultRedirect->setPath('catalog/product_compare');
+
+ return $resultRedirect;
+ }
}
diff --git a/app/code/Magento/Catalog/Helper/Data.php b/app/code/Magento/Catalog/Helper/Data.php
index ae20cda460796..3e96763632830 100644
--- a/app/code/Magento/Catalog/Helper/Data.php
+++ b/app/code/Magento/Catalog/Helper/Data.php
@@ -7,6 +7,7 @@
use Magento\Catalog\Api\CategoryRepositoryInterface;
use Magento\Catalog\Api\ProductRepositoryInterface;
+use Magento\Store\Model\ScopeInterface;
use Magento\Customer\Model\Session as CustomerSession;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Pricing\PriceCurrencyInterface;
@@ -273,7 +274,8 @@ public function setStoreId($store)
/**
* Return current category path or get it from current category
- * and creating array of categories|product paths for breadcrumbs
+ *
+ * Creating array of categories|product paths for breadcrumbs
*
* @return array
*/
@@ -382,6 +384,7 @@ public function getLastViewedUrl()
/**
* Split SKU of an item by dashes and spaces
+ *
* Words will not be broken, unless this length is greater than $length
*
* @param string $sku
@@ -410,14 +413,15 @@ public function getAttributeHiddenFields()
/**
* Retrieve Catalog Price Scope
*
- * @return int
+ * @return int|null
*/
- public function getPriceScope()
+ public function getPriceScope(): ?int
{
- return $this->scopeConfig->getValue(
+ $priceScope = $this->scopeConfig->getValue(
self::XML_PATH_PRICE_SCOPE,
- \Magento\Store\Model\ScopeInterface::SCOPE_STORE
+ ScopeInterface::SCOPE_STORE
);
+ return isset($priceScope) ? (int)$priceScope : null;
}
/**
@@ -439,7 +443,7 @@ public function isUsingStaticUrlsAllowed()
{
return $this->scopeConfig->isSetFlag(
self::CONFIG_USE_STATIC_URLS,
- \Magento\Store\Model\ScopeInterface::SCOPE_STORE
+ ScopeInterface::SCOPE_STORE
);
}
@@ -454,7 +458,7 @@ public function isUrlDirectivesParsingAllowed()
{
return $this->scopeConfig->isSetFlag(
self::CONFIG_PARSE_URL_DIRECTIVES,
- \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
+ ScopeInterface::SCOPE_STORE,
$this->_storeId
);
}
@@ -472,6 +476,7 @@ public function getPageTemplateProcessor()
/**
* Whether to display items count for each filter option
+ *
* @param int $storeId Store view ID
* @return bool
*/
@@ -479,12 +484,14 @@ public function shouldDisplayProductCountOnLayer($storeId = null)
{
return $this->scopeConfig->isSetFlag(
self::XML_PATH_DISPLAY_PRODUCT_COUNT,
- \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
+ ScopeInterface::SCOPE_STORE,
$storeId
);
}
/**
+ * Convert tax address array to address data object with country id and postcode
+ *
* @param array $taxAddress
* @return \Magento\Customer\Api\Data\AddressInterface|null
*/
diff --git a/app/code/Magento/Catalog/Helper/Image.php b/app/code/Magento/Catalog/Helper/Image.php
index 758e59790d241..170f1209ad9e6 100644
--- a/app/code/Magento/Catalog/Helper/Image.php
+++ b/app/code/Magento/Catalog/Helper/Image.php
@@ -298,6 +298,7 @@ public function resize($width, $height = null)
*
* @param int $quality
* @return $this
+ * @deprecated
*/
public function setQuality($quality)
{
@@ -406,7 +407,8 @@ public function rotate($angle)
/**
* Add watermark to image
- * size param in format 100x200
+ *
+ * Size param in format 100x200
*
* @param string $fileName
* @param string $position
@@ -533,6 +535,8 @@ public function getUrl()
}
/**
+ * Save changes
+ *
* @return $this
*/
public function save()
@@ -553,6 +557,8 @@ public function getResizedImageInfo()
}
/**
+ * Getter for placeholder url
+ *
* @param null|string $placeholder
* @return string
*/
@@ -655,7 +661,8 @@ protected function getWatermarkPosition()
/**
* Set watermark size
- * param size in format 100x200
+ *
+ * Param size in format 100x200
*
* @param string $size
* @return $this
diff --git a/app/code/Magento/Catalog/Model/AbstractModel.php b/app/code/Magento/Catalog/Model/AbstractModel.php
index 007635b124331..78a49cd1e8b14 100644
--- a/app/code/Magento/Catalog/Model/AbstractModel.php
+++ b/app/code/Magento/Catalog/Model/AbstractModel.php
@@ -179,7 +179,7 @@ public function isLockedAttribute($attributeCode)
*
* @param string|array $key
* @param mixed $value
- * @return \Magento\Framework\DataObject
+ * @return $this
*/
public function setData($key, $value = null)
{
@@ -282,9 +282,9 @@ public function getWebsiteStoreIds()
*
* Default value existing is flag for using store value in data
*
- * @param string $attributeCode
- * @param mixed $value
- * @return $this
+ * @param string $attributeCode
+ * @param mixed $value
+ * @return $this
*
* @deprecated 101.0.0
*/
@@ -332,11 +332,10 @@ public function getAttributeDefaultValue($attributeCode)
}
/**
- * Set attribute code flag if attribute has value in current store and does not use
- * value of default store as value
+ * Set attribute code flag if attribute has value in current store and does not use value of default store as value
*
- * @param string $attributeCode
- * @return $this
+ * @param string $attributeCode
+ * @return $this
*
* @deprecated 101.0.0
*/
diff --git a/app/code/Magento/Catalog/Model/Category/Product/PositionResolver.php b/app/code/Magento/Catalog/Model/Category/Product/PositionResolver.php
index 1e07c0cdd924e..44bf153f83697 100644
--- a/app/code/Magento/Catalog/Model/Category/Product/PositionResolver.php
+++ b/app/code/Magento/Catalog/Model/Category/Product/PositionResolver.php
@@ -43,6 +43,8 @@ public function getPositions(int $categoryId): array
$categoryId
)->order(
'ccp.position ' . \Magento\Framework\DB\Select::SQL_ASC
+ )->order(
+ 'ccp.product_id ' . \Magento\Framework\DB\Select::SQL_DESC
);
return array_flip($connection->fetchCol($select));
diff --git a/app/code/Magento/Catalog/Model/Design.php b/app/code/Magento/Catalog/Model/Design.php
index bd7cdabb40856..853bbeac8eb38 100644
--- a/app/code/Magento/Catalog/Model/Design.php
+++ b/app/code/Magento/Catalog/Model/Design.php
@@ -5,6 +5,8 @@
*/
namespace Magento\Catalog\Model;
+use \Magento\Framework\TranslateInterface;
+
/**
* Catalog Custom Category design Model
*
@@ -31,14 +33,20 @@ class Design extends \Magento\Framework\Model\AbstractModel
*/
protected $_localeDate;
+ /**
+ * @var TranslateInterface
+ */
+ private $translator;
+
/**
* @param \Magento\Framework\Model\Context $context
* @param \Magento\Framework\Registry $registry
* @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate
* @param \Magento\Framework\View\DesignInterface $design
- * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource
- * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection
+ * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource
+ * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection
* @param array $data
+ * @param TranslateInterface|null $translator
*/
public function __construct(
\Magento\Framework\Model\Context $context,
@@ -47,10 +55,13 @@ public function __construct(
\Magento\Framework\View\DesignInterface $design,
\Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
\Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
- array $data = []
+ array $data = [],
+ TranslateInterface $translator = null
) {
$this->_localeDate = $localeDate;
$this->_design = $design;
+ $this->translator = $translator ?:
+ \Magento\Framework\App\ObjectManager::getInstance()->get(TranslateInterface::class);
parent::__construct($context, $registry, $resource, $resourceCollection, $data);
}
@@ -63,6 +74,7 @@ public function __construct(
public function applyCustomDesign($design)
{
$this->_design->setDesignTheme($design);
+ $this->translator->loadData(null, true);
return $this;
}
diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php b/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php
index 6a499883ac723..178f4172ce6fa 100644
--- a/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php
+++ b/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php
@@ -123,6 +123,11 @@ abstract class AbstractAction
*/
private $queryGenerator;
+ /**
+ * @var int
+ */
+ private $currentStoreId = 0;
+
/**
* @param ResourceConnection $resource
* @param \Magento\Store\Model\StoreManagerInterface $storeManager
@@ -164,6 +169,7 @@ protected function reindex()
{
foreach ($this->storeManager->getStores() as $store) {
if ($this->getPathFromCategoryId($store->getRootCategoryId())) {
+ $this->currentStoreId = $store->getId();
$this->reindexRootCategory($store);
$this->reindexAnchorCategories($store);
$this->reindexNonAnchorCategories($store);
@@ -586,6 +592,8 @@ protected function createAnchorSelect(Store $store)
}
/**
+ * Get temporary table name
+ *
* Get temporary table name for concurrent indexing in persistent connection
* Temp table name is NOT shared between action instances and each action has it's own temp tree index
*
@@ -597,7 +605,7 @@ protected function getTemporaryTreeIndexTableName()
if (empty($this->tempTreeIndexTableName)) {
$this->tempTreeIndexTableName = $this->connection->getTableName('temp_catalog_category_tree_index')
. '_'
- . substr(md5(time() . random_int(0, 999999999)), 0, 8);
+ . substr(sha1(time() . random_int(0, 999999999)), 0, 8);
}
return $this->tempTreeIndexTableName;
@@ -641,7 +649,6 @@ protected function makeTempCategoryTreeIndex()
['child_id'],
['type' => \Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_INDEX]
);
-
// Drop the temporary table in case it already exists on this (persistent?) connection.
$this->connection->dropTemporaryTable($temporaryName);
$this->connection->createTemporaryTable($temporaryTable);
@@ -659,11 +666,31 @@ protected function makeTempCategoryTreeIndex()
*/
protected function fillTempCategoryTreeIndex($temporaryName)
{
+ $isActiveAttributeId = $this->config->getAttribute(
+ \Magento\Catalog\Model\Category::ENTITY,
+ 'is_active'
+ )->getId();
+ $categoryMetadata = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\CategoryInterface::class);
+ $categoryLinkField = $categoryMetadata->getLinkField();
$selects = $this->prepareSelectsByRange(
$this->connection->select()
->from(
['c' => $this->getTable('catalog_category_entity')],
['entity_id', 'path']
+ )->joinInner(
+ ['ccacd' => $this->getTable('catalog_category_entity_int')],
+ 'ccacd.' . $categoryLinkField . ' = c.' . $categoryLinkField . ' AND ccacd.store_id = 0' .
+ ' AND ccacd.attribute_id = ' . $isActiveAttributeId,
+ []
+ )->joinLeft(
+ ['ccacs' => $this->getTable('catalog_category_entity_int')],
+ 'ccacs.' . $categoryLinkField . ' = c.' . $categoryLinkField
+ . ' AND ccacs.attribute_id = ccacd.attribute_id AND ccacs.store_id = ' .
+ $this->currentStoreId,
+ []
+ )->where(
+ $this->connection->getIfNullSql('ccacs.value', 'ccacd.value') . ' = ?',
+ 1
),
'entity_id'
);
diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php
index b4d6dc2c19e51..aef3e87586015 100644
--- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php
+++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php
@@ -86,10 +86,10 @@ public function execute($entity, $arguments = [])
__('Tier prices data should be array, but actually other type is received')
);
}
- $websiteId = $this->storeManager->getStore($entity->getStoreId())->getWebsiteId();
+ $websiteId = (int)$this->storeManager->getStore($entity->getStoreId())->getWebsiteId();
$isGlobal = $attribute->isScopeGlobal() || $websiteId === 0;
$identifierField = $this->metadataPoll->getMetadata(ProductInterface::class)->getLinkField();
- $productId = (int) $entity->getData($identifierField);
+ $productId = (int)$entity->getData($identifierField);
// prepare original data to compare
$origPrices = [];
@@ -98,7 +98,7 @@ public function execute($entity, $arguments = [])
$origPrices = $entity->getOrigData($attribute->getName());
}
- $old = $this->prepareOriginalDataToCompare($origPrices, $isGlobal);
+ $old = $this->prepareOldTierPriceToCompare($origPrices);
// prepare data for save
$new = $this->prepareNewDataForSave($priceRows, $isGlobal);
@@ -271,21 +271,18 @@ private function isWebsiteGlobal(int $websiteId): bool
}
/**
- * Prepare original data to compare.
+ * Prepare old data to compare.
*
* @param array|null $origPrices
- * @param bool $isGlobal
* @return array
*/
- private function prepareOriginalDataToCompare(?array $origPrices, bool $isGlobal = true): array
+ private function prepareOldTierPriceToCompare(?array $origPrices): array
{
$old = [];
if (is_array($origPrices)) {
foreach ($origPrices as $data) {
- if ($isGlobal === $this->isWebsiteGlobal((int)$data['website_id'])) {
- $key = $this->getPriceKey($data);
- $old[$key] = $data;
- }
+ $key = $this->getPriceKey($data);
+ $old[$key] = $data;
}
}
diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php
index 92b9a2e4239b2..e346c912dccaa 100644
--- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php
+++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php
@@ -13,6 +13,9 @@
use Magento\Catalog\Model\Attribute\ScopeOverriddenValue;
+/**
+ * Backend model for Tierprice attribute
+ */
class Tierprice extends \Magento\Catalog\Model\Product\Attribute\Backend\GroupPrice\AbstractGroupPrice
{
/**
@@ -159,8 +162,22 @@ protected function validatePrice(array $priceRow)
*/
protected function modifyPriceData($object, $data)
{
+ /** @var \Magento\Catalog\Model\Product $object */
$data = parent::modifyPriceData($object, $data);
$price = $object->getPrice();
+
+ $specialPrice = $object->getSpecialPrice();
+ $specialPriceFromDate = $object->getSpecialFromDate();
+ $specialPriceToDate = $object->getSpecialToDate();
+ $today = time();
+
+ if ($specialPrice && ($object->getPrice() > $object->getFinalPrice())) {
+ if ($today >= strtotime($specialPriceFromDate) && $today <= strtotime($specialPriceToDate) ||
+ $today >= strtotime($specialPriceFromDate) && $specialPriceToDate === null) {
+ $price = $specialPrice;
+ }
+ }
+
foreach ($data as $key => $tierPrice) {
$percentageValue = $this->getPercentage($tierPrice);
if ($percentageValue) {
@@ -172,6 +189,10 @@ protected function modifyPriceData($object, $data)
}
/**
+ * Update Price values in DB
+ *
+ * Updates price values in DB from array comparing to old values. Returns bool if updated
+ *
* @param array $valuesToUpdate
* @param array $oldValues
* @return boolean
diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php b/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php
index c6c7fbda7e9ec..0912324745360 100644
--- a/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php
+++ b/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php
@@ -6,9 +6,10 @@
namespace Magento\Catalog\Model\Product\Gallery;
+use Magento\Framework\Api\Data\ImageContentInterface;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\Exception\LocalizedException;
-use Magento\Framework\Filesystem\DriverInterface;
+use Magento\Framework\App\ObjectManager;
/**
* Catalog product Media Gallery attribute processor.
@@ -56,28 +57,39 @@ class Processor
*/
protected $resourceModel;
+ /**
+ * @var \Magento\Framework\File\Mime
+ */
+ private $mime;
+
/**
* @param \Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository
* @param \Magento\MediaStorage\Helper\File\Storage\Database $fileStorageDb
* @param \Magento\Catalog\Model\Product\Media\Config $mediaConfig
* @param \Magento\Framework\Filesystem $filesystem
* @param \Magento\Catalog\Model\ResourceModel\Product\Gallery $resourceModel
+ * @param \Magento\Framework\File\Mime|null $mime
+ * @throws \Magento\Framework\Exception\FileSystemException
*/
public function __construct(
\Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository,
\Magento\MediaStorage\Helper\File\Storage\Database $fileStorageDb,
\Magento\Catalog\Model\Product\Media\Config $mediaConfig,
\Magento\Framework\Filesystem $filesystem,
- \Magento\Catalog\Model\ResourceModel\Product\Gallery $resourceModel
+ \Magento\Catalog\Model\ResourceModel\Product\Gallery $resourceModel,
+ \Magento\Framework\File\Mime $mime = null
) {
$this->attributeRepository = $attributeRepository;
$this->fileStorageDb = $fileStorageDb;
$this->mediaConfig = $mediaConfig;
$this->mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA);
$this->resourceModel = $resourceModel;
+ $this->mime = $mime ?: ObjectManager::getInstance()->get(\Magento\Framework\File\Mime::class);
}
/**
+ * Return media_gallery attribute
+ *
* @return \Magento\Catalog\Api\Data\ProductAttributeInterface
* @since 101.0.0
*/
@@ -183,6 +195,13 @@ public function addImage(
$attrCode = $this->getAttribute()->getAttributeCode();
$mediaGalleryData = $product->getData($attrCode);
$position = 0;
+
+ $absoluteFilePath = $this->mediaDirectory->getAbsolutePath($file);
+ $imageMimeType = $this->mime->getMimeType($absoluteFilePath);
+ $imageContent = $this->mediaDirectory->readFile($absoluteFilePath);
+ $imageBase64 = base64_encode($imageContent);
+ $imageName = $pathinfo['filename'];
+
if (!is_array($mediaGalleryData)) {
$mediaGalleryData = ['images' => []];
}
@@ -197,9 +216,17 @@ public function addImage(
$mediaGalleryData['images'][] = [
'file' => $fileName,
'position' => $position,
- 'media_type' => 'image',
'label' => '',
'disabled' => (int)$exclude,
+ 'media_type' => 'image',
+ 'types' => $mediaAttribute,
+ 'content' => [
+ 'data' => [
+ ImageContentInterface::NAME => $imageName,
+ ImageContentInterface::BASE64_ENCODED_DATA => $imageBase64,
+ ImageContentInterface::TYPE => $imageMimeType,
+ ]
+ ]
];
$product->setData($attrCode, $mediaGalleryData);
@@ -358,7 +385,8 @@ public function setMediaAttribute(\Magento\Catalog\Model\Product $product, $medi
}
/**
- * get media attribute codes
+ * Get media attribute codes
+ *
* @return array
* @since 101.0.0
*/
@@ -368,6 +396,8 @@ public function getMediaAttributeCodes()
}
/**
+ * Trim .tmp ending from filename
+ *
* @param string $file
* @return string
* @since 101.0.0
@@ -489,7 +519,6 @@ public function getAffectedFields($object)
/**
* Attribute value is not to be saved in a conventional way, separate table is used to store the complex value
*
- * {@inheritdoc}
* @since 101.0.0
*/
public function isScalar()
diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/ReadHandler.php b/app/code/Magento/Catalog/Model/Product/Gallery/ReadHandler.php
index c785d08e64b7f..a3726207b3024 100644
--- a/app/code/Magento/Catalog/Model/Product/Gallery/ReadHandler.php
+++ b/app/code/Magento/Catalog/Model/Product/Gallery/ReadHandler.php
@@ -47,6 +47,8 @@ public function __construct(
}
/**
+ * Execute read handler for catalog product gallery
+ *
* @param Product $entity
* @param array $arguments
* @return object
@@ -55,9 +57,6 @@ public function __construct(
*/
public function execute($entity, $arguments = [])
{
- $value = [];
- $value['images'] = [];
-
$mediaEntries = $this->resourceModel->loadProductGalleryByAttributeId(
$entity,
$this->getAttribute()->getAttributeId()
@@ -72,6 +71,8 @@ public function execute($entity, $arguments = [])
}
/**
+ * Add media data to product
+ *
* @param Product $product
* @param array $mediaEntries
* @return void
@@ -79,40 +80,18 @@ public function execute($entity, $arguments = [])
*/
public function addMediaDataToProduct(Product $product, array $mediaEntries)
{
- $attrCode = $this->getAttribute()->getAttributeCode();
- $value = [];
- $value['images'] = [];
- $value['values'] = [];
-
- foreach ($mediaEntries as $mediaEntry) {
- $mediaEntry = $this->substituteNullsWithDefaultValues($mediaEntry);
- $value['images'][$mediaEntry['value_id']] = $mediaEntry;
- }
- $product->setData($attrCode, $value);
- }
-
- /**
- * @param array $rawData
- * @return array
- */
- private function substituteNullsWithDefaultValues(array $rawData)
- {
- $processedData = [];
- foreach ($rawData as $key => $rawValue) {
- if (null !== $rawValue) {
- $processedValue = $rawValue;
- } elseif (isset($rawData[$key . '_default'])) {
- $processedValue = $rawData[$key . '_default'];
- } else {
- $processedValue = null;
- }
- $processedData[$key] = $processedValue;
- }
-
- return $processedData;
+ $product->setData(
+ $this->getAttribute()->getAttributeCode(),
+ [
+ 'images' => array_column($mediaEntries, null, 'value_id'),
+ 'values' => []
+ ]
+ );
}
/**
+ * Get attribute
+ *
* @return \Magento\Catalog\Api\Data\ProductAttributeInterface
* @since 101.0.0
*/
@@ -126,6 +105,8 @@ public function getAttribute()
}
/**
+ * Find default value
+ *
* @param string $key
* @param string[] &$image
* @return string
diff --git a/app/code/Magento/Catalog/Model/Product/Image.php b/app/code/Magento/Catalog/Model/Product/Image.php
index f1ae9ac62dc9b..a0be36c5a327c 100644
--- a/app/code/Magento/Catalog/Model/Product/Image.php
+++ b/app/code/Magento/Catalog/Model/Product/Image.php
@@ -15,6 +15,8 @@
use Magento\Catalog\Model\Product\Image\ParamsBuilder;
/**
+ * Image operations
+ *
* @method string getFile()
* @method string getLabel()
* @method string getPosition()
@@ -24,6 +26,11 @@
*/
class Image extends \Magento\Framework\Model\AbstractModel
{
+ /**
+ * Config path for the jpeg image quality value
+ */
+ const XML_PATH_JPEG_QUALITY = 'system/upload_configuration/jpeg_quality';
+
/**
* @var int
*/
@@ -38,8 +45,9 @@ class Image extends \Magento\Framework\Model\AbstractModel
* Default quality value (for JPEG images only).
*
* @var int
+ * @deprecated use config setting with path self::XML_PATH_JPEG_QUALITY
*/
- protected $_quality = 80;
+ protected $_quality = null;
/**
* @var bool
@@ -203,13 +211,13 @@ class Image extends \Magento\Framework\Model\AbstractModel
* @param \Magento\Framework\Image\Factory $imageFactory
* @param \Magento\Framework\View\Asset\Repository $assetRepo
* @param \Magento\Framework\View\FileSystem $viewFileSystem
+ * @param ImageFactory $viewAssetImageFactory
+ * @param PlaceholderFactory $viewAssetPlaceholderFactory
* @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
* @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource
* @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection
* @param array $data
- * @param ImageFactory|null $viewAssetImageFactory
- * @param PlaceholderFactory|null $viewAssetPlaceholderFactory
- * @param SerializerInterface|null $serializer
+ * @param SerializerInterface $serializer
* @param ParamsBuilder $paramsBuilder
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
* @SuppressWarnings(PHPMD.UnusedLocalVariable)
@@ -249,6 +257,8 @@ public function __construct(
}
/**
+ * Set image width property
+ *
* @param int $width
* @return $this
*/
@@ -259,6 +269,8 @@ public function setWidth($width)
}
/**
+ * Get image width property
+ *
* @return int
*/
public function getWidth()
@@ -267,6 +279,8 @@ public function getWidth()
}
/**
+ * Set image height property
+ *
* @param int $height
* @return $this
*/
@@ -277,6 +291,8 @@ public function setHeight($height)
}
/**
+ * Get image height property
+ *
* @return int
*/
public function getHeight()
@@ -289,6 +305,7 @@ public function getHeight()
*
* @param int $quality
* @return $this
+ * @deprecated use config setting with path self::XML_PATH_JPEG_QUALITY
*/
public function setQuality($quality)
{
@@ -303,10 +320,14 @@ public function setQuality($quality)
*/
public function getQuality()
{
- return $this->_quality;
+ return $this->_quality === null
+ ? $this->_scopeConfig->getValue(self::XML_PATH_JPEG_QUALITY)
+ : $this->_quality;
}
/**
+ * Set _keepAspectRatio property
+ *
* @param bool $keep
* @return $this
*/
@@ -317,6 +338,8 @@ public function setKeepAspectRatio($keep)
}
/**
+ * Set _keepFrame property
+ *
* @param bool $keep
* @return $this
*/
@@ -327,6 +350,8 @@ public function setKeepFrame($keep)
}
/**
+ * Set _keepTransparency
+ *
* @param bool $keep
* @return $this
*/
@@ -337,6 +362,8 @@ public function setKeepTransparency($keep)
}
/**
+ * Set _constrainOnly
+ *
* @param bool $flag
* @return $this
*/
@@ -347,6 +374,8 @@ public function setConstrainOnly($flag)
}
/**
+ * Set background color
+ *
* @param int[] $rgbArray
* @return $this
*/
@@ -357,6 +386,8 @@ public function setBackgroundColor(array $rgbArray)
}
/**
+ * Set size
+ *
* @param string $size
* @return $this
*/
@@ -411,6 +442,8 @@ public function setBaseFile($file)
}
/**
+ * Get base filename
+ *
* @return string
*/
public function getBaseFile()
@@ -419,6 +452,8 @@ public function getBaseFile()
}
/**
+ * Get new file
+ *
* @deprecated 101.1.0
* @return bool|string
*/
@@ -438,6 +473,8 @@ public function isBaseFilePlaceholder()
}
/**
+ * Set image processor
+ *
* @param MagentoImage $processor
* @return $this
*/
@@ -448,6 +485,8 @@ public function setImageProcessor($processor)
}
/**
+ * Get image processor
+ *
* @return MagentoImage
*/
public function getImageProcessor()
@@ -461,11 +500,13 @@ public function getImageProcessor()
$this->_processor->keepTransparency($this->_keepTransparency);
$this->_processor->constrainOnly($this->_constrainOnly);
$this->_processor->backgroundColor($this->_backgroundColor);
- $this->_processor->quality($this->_quality);
+ $this->_processor->quality($this->getQuality());
return $this->_processor;
}
/**
+ * Resize image
+ *
* @see \Magento\Framework\Image\Adapter\AbstractAdapter
* @return $this
*/
@@ -479,6 +520,8 @@ public function resize()
}
/**
+ * Rotate image
+ *
* @param int $angle
* @return $this
*/
@@ -505,7 +548,8 @@ public function setAngle($angle)
/**
* Add watermark to image
- * size param in format 100x200
+ *
+ * Size param in format 100x200
*
* @param string $file
* @param string $position
@@ -564,6 +608,8 @@ public function setWatermark(
}
/**
+ * Save file
+ *
* @return $this
*/
public function saveFile()
@@ -578,6 +624,8 @@ public function saveFile()
}
/**
+ * Get url
+ *
* @return string
*/
public function getUrl()
@@ -586,6 +634,8 @@ public function getUrl()
}
/**
+ * Set destination subdir
+ *
* @param string $dir
* @return $this
*/
@@ -596,6 +646,8 @@ public function setDestinationSubdir($dir)
}
/**
+ * Get destination subdir
+ *
* @return string
*/
public function getDestinationSubdir()
@@ -604,6 +656,8 @@ public function getDestinationSubdir()
}
/**
+ * Check is image cached
+ *
* @return bool
*/
public function isCached()
@@ -636,7 +690,8 @@ public function getWatermarkFile()
/**
* Get relative watermark file path
- * or false if file not found
+ *
+ * Return false if file not found
*
* @return string | bool
*/
@@ -771,7 +826,10 @@ public function getWatermarkHeight()
}
/**
+ * Clear cache
+ *
* @return void
+ * @throws \Magento\Framework\Exception\FileSystemException
*/
public function clearCache()
{
@@ -784,6 +842,7 @@ public function clearCache()
/**
* First check this file on FS
+ *
* If it doesn't exist - try to download it from DB
*
* @param string $filename
@@ -802,6 +861,7 @@ protected function _fileExists($filename)
/**
* Return resized product image information
+ *
* @return array
* @throws NotLoadInfoImageException
*/
@@ -843,7 +903,7 @@ private function getMiscParams()
'transparency' => $this->_keepTransparency,
'background' => $this->_backgroundColor,
'angle' => $this->_angle,
- 'quality' => $this->_quality
+ 'quality' => $this->getQuality()
]
);
}
diff --git a/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php b/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php
index dd8d352fecebc..f6be7f7392b5e 100644
--- a/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php
+++ b/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php
@@ -10,17 +10,13 @@
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\View\ConfigInterface;
use Magento\Store\Model\ScopeInterface;
+use Magento\Catalog\Model\Product\Image;
/**
* Builds parameters array used to build Image Asset
*/
class ParamsBuilder
{
- /**
- * @var int
- */
- private $defaultQuality = 80;
-
/**
* @var array
*/
@@ -69,6 +65,8 @@ public function __construct(
}
/**
+ * Build image params
+ *
* @param array $imageArguments
* @return array
* @SuppressWarnings(PHPMD.NPathComplexity)
@@ -89,6 +87,8 @@ public function build(array $imageArguments): array
}
/**
+ * Overwrite default values
+ *
* @param array $imageArguments
* @return array
*/
@@ -100,11 +100,12 @@ private function overwriteDefaultValues(array $imageArguments): array
$transparency = $imageArguments['transparency'] ?? $this->defaultKeepTransparency;
$background = $imageArguments['background'] ?? $this->defaultBackground;
$angle = $imageArguments['angle'] ?? $this->defaultAngle;
+ $quality = (int) $this->scopeConfig->getValue(Image::XML_PATH_JPEG_QUALITY);
return [
'background' => (array) $background,
'angle' => $angle,
- 'quality' => $this->defaultQuality,
+ 'quality' => $quality,
'keep_aspect_ratio' => (bool) $aspectRatio,
'keep_frame' => (bool) $frame,
'keep_transparency' => (bool) $transparency,
@@ -113,6 +114,8 @@ private function overwriteDefaultValues(array $imageArguments): array
}
/**
+ * Get watermark
+ *
* @param string $type
* @return array
*/
@@ -153,6 +156,7 @@ private function getWatermark(string $type): array
/**
* Get frame from product_image_white_borders
+ *
* @return bool
*/
private function hasDefaultFrame(): bool
diff --git a/app/code/Magento/Catalog/Model/Product/ProductList/ToolbarMemorizer.php b/app/code/Magento/Catalog/Model/Product/ProductList/ToolbarMemorizer.php
new file mode 100644
index 0000000000000..9c1a781d594f7
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/Product/ProductList/ToolbarMemorizer.php
@@ -0,0 +1,179 @@
+toolbarModel = $toolbarModel;
+ $this->catalogSession = $catalogSession;
+ $this->scopeConfig = $scopeConfig;
+ }
+
+ /**
+ * Get sort order
+ *
+ * @return string|bool
+ */
+ public function getOrder()
+ {
+ if ($this->order === null) {
+ $this->order = $this->toolbarModel->getOrder() ??
+ ($this->isMemorizingAllowed() ? $this->catalogSession->getData(Toolbar::ORDER_PARAM_NAME) : null);
+ }
+ return $this->order;
+ }
+
+ /**
+ * Get sort direction
+ *
+ * @return string|bool
+ */
+ public function getDirection()
+ {
+ if ($this->direction === null) {
+ $this->direction = $this->toolbarModel->getDirection() ??
+ ($this->isMemorizingAllowed() ? $this->catalogSession->getData(Toolbar::DIRECTION_PARAM_NAME) : null);
+ }
+ return $this->direction;
+ }
+
+ /**
+ * Get sort mode
+ *
+ * @return string|bool
+ */
+ public function getMode()
+ {
+ if ($this->mode === null) {
+ $this->mode = $this->toolbarModel->getMode() ??
+ ($this->isMemorizingAllowed() ? $this->catalogSession->getData(Toolbar::MODE_PARAM_NAME) : null);
+ }
+ return $this->mode;
+ }
+
+ /**
+ * Get products per page limit
+ *
+ * @return string|bool
+ */
+ public function getLimit()
+ {
+ if ($this->limit === null) {
+ $this->limit = $this->toolbarModel->getLimit() ??
+ ($this->isMemorizingAllowed() ? $this->catalogSession->getData(Toolbar::LIMIT_PARAM_NAME) : null);
+ }
+ return $this->limit;
+ }
+
+ /**
+ * Method to save all catalog parameters in catalog session
+ *
+ * @return void
+ */
+ public function memorizeParams()
+ {
+ if (!$this->catalogSession->getParamsMemorizeDisabled() && $this->isMemorizingAllowed()) {
+ $this->memorizeParam(Toolbar::ORDER_PARAM_NAME, $this->getOrder())
+ ->memorizeParam(Toolbar::DIRECTION_PARAM_NAME, $this->getDirection())
+ ->memorizeParam(Toolbar::MODE_PARAM_NAME, $this->getMode())
+ ->memorizeParam(Toolbar::LIMIT_PARAM_NAME, $this->getLimit());
+ }
+ }
+
+ /**
+ * Check configuration for enabled/disabled toolbar memorizing
+ *
+ * @return bool
+ */
+ public function isMemorizingAllowed()
+ {
+ if ($this->isMemorizingAllowed === null) {
+ $this->isMemorizingAllowed = $this->scopeConfig->isSetFlag(self::XML_PATH_CATALOG_REMEMBER_PAGINATION);
+ }
+ return $this->isMemorizingAllowed;
+ }
+
+ /**
+ * Memorize parameter value for session
+ *
+ * @param string $param parameter name
+ * @param mixed $value parameter value
+ * @return $this
+ */
+ private function memorizeParam($param, $value)
+ {
+ if ($value && $this->catalogSession->getData($param) != $value) {
+ $this->catalogSession->setData($param, $value);
+ }
+ return $this;
+ }
+}
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php
index 46bb74513b59c..4562aa0b0e75f 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php
@@ -322,11 +322,7 @@ public function loadProductCount($items, $countRegular = true, $countAnchor = tr
['e' => $this->getTable('catalog_category_entity')],
'main_table.category_id=e.entity_id',
[]
- )->where(
- 'e.entity_id = :entity_id'
- )->orWhere(
- 'e.path LIKE :c_path'
- );
+ )->where('e.entity_id = :entity_id OR e.path LIKE :c_path');
if ($websiteId) {
$select->join(
['w' => $this->getProductWebsiteTable()],
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
index 5afac59b25db6..14ae38667d873 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
@@ -1534,7 +1534,7 @@ public function addPriceData($customerGroupId = null, $websiteId = null)
/**
* Add attribute to filter
*
- * @param \Magento\Eav\Model\Entity\Attribute\AbstractAttribute|string $attribute
+ * @param \Magento\Eav\Model\Entity\Attribute\AbstractAttribute|string|array $attribute
* @param array $condition
* @param string $joinType
* @return $this
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php
index 2868392f85280..635715a60742f 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php
@@ -49,7 +49,8 @@ public function __construct(
}
/**
- * {@inheritdoc}
+ * @inheritdoc
+ *
* @since 101.0.0
*/
protected function _construct()
@@ -58,7 +59,8 @@ protected function _construct()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
+ *
* @since 101.0.0
*/
public function getConnection()
@@ -67,6 +69,8 @@ public function getConnection()
}
/**
+ * Load data from table by valueId
+ *
* @param string $tableNameAlias
* @param array $ids
* @param int|null $storeId
@@ -111,6 +115,8 @@ public function loadDataFromTableByValueId(
}
/**
+ * Load product gallery by attributeId
+ *
* @param \Magento\Catalog\Model\Product $product
* @param int $attributeId
* @return array
@@ -132,6 +138,8 @@ public function loadProductGalleryByAttributeId($product, $attributeId)
}
/**
+ * Create base load select
+ *
* @param int $entityId
* @param int $storeId
* @param int $attributeId
@@ -151,6 +159,8 @@ protected function createBaseLoadSelect($entityId, $storeId, $attributeId)
}
/**
+ * Create batch base select
+ *
* @param int $storeId
* @param int $attributeId
* @return \Magento\Framework\DB\Select
@@ -190,7 +200,7 @@ public function createBatchBaseSelect($storeId, $attributeId)
'value.' . $linkField . ' = entity.' . $linkField,
]
),
- ['label', 'position', 'disabled']
+ []
)->joinLeft(
['default_value' => $this->getTable(self::GALLERY_VALUE_TABLE)],
implode(
@@ -201,8 +211,15 @@ public function createBatchBaseSelect($storeId, $attributeId)
'default_value.' . $linkField . ' = entity.' . $linkField,
]
),
- ['label_default' => 'label', 'position_default' => 'position', 'disabled_default' => 'disabled']
- )->where(
+ []
+ )->columns([
+ 'label' => $this->getConnection()->getIfNullSql('`value`.`label`', '`default_value`.`label`'),
+ 'position' => $this->getConnection()->getIfNullSql('`value`.`position`', '`default_value`.`position`'),
+ 'disabled' => $this->getConnection()->getIfNullSql('`value`.`disabled`', '`default_value`.`disabled`'),
+ 'label_default' => 'default_value.label',
+ 'position_default' => 'default_value.position',
+ 'disabled_default' => 'default_value.disabled'
+ ])->where(
$mainTableAlias . '.attribute_id = ?',
$attributeId
)->where(
@@ -240,6 +257,8 @@ protected function removeDuplicates(&$result)
}
/**
+ * Get main table alias
+ *
* @return string
* @since 101.0.0
*/
@@ -249,6 +268,8 @@ public function getMainTableAlias()
}
/**
+ * Bind value to entity
+ *
* @param int $valueId
* @param int $entityId
* @return int
@@ -266,6 +287,8 @@ public function bindValueToEntity($valueId, $entityId)
}
/**
+ * Save data row
+ *
* @param string $table
* @param array $data
* @param array $fields
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Image.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Image.php
index 123f358be40c8..77f67480619e0 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Image.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Image.php
@@ -12,6 +12,9 @@
use Magento\Framework\DB\Select;
use Magento\Framework\App\ResourceConnection;
+/**
+ * Class for retrieval of all product images
+ */
class Image
{
/**
@@ -73,15 +76,24 @@ public function getAllProductImages(): \Generator
/**
* Get the number of unique pictures of products
+ *
* @return int
*/
public function getCountAllProductImages(): int
{
- $select = $this->getVisibleImagesSelect()->reset('columns')->columns('count(*)');
+ $select = $this->getVisibleImagesSelect()
+ ->reset('columns')
+ ->reset('distinct')
+ ->columns(
+ new \Zend_Db_Expr('count(distinct value)')
+ );
+
return (int) $this->connection->fetchOne($select);
}
/**
+ * Return Select to fetch all products images
+ *
* @return Select
*/
private function getVisibleImagesSelect(): Select
diff --git a/app/code/Magento/Catalog/Plugin/Framework/App/Action/ContextPlugin.php b/app/code/Magento/Catalog/Plugin/Framework/App/Action/ContextPlugin.php
new file mode 100644
index 0000000000000..6add542b15554
--- /dev/null
+++ b/app/code/Magento/Catalog/Plugin/Framework/App/Action/ContextPlugin.php
@@ -0,0 +1,73 @@
+toolbarMemorizer = $toolbarMemorizer;
+ $this->catalogSession = $catalogSession;
+ $this->httpContext = $httpContext;
+ }
+
+ /**
+ * Update http context with catalog sensitive information.
+ *
+ * @return void
+ */
+ public function beforeDispatch()
+ {
+ if ($this->toolbarMemorizer->isMemorizingAllowed()) {
+ $params = [
+ ToolbarModel::ORDER_PARAM_NAME,
+ ToolbarModel::DIRECTION_PARAM_NAME,
+ ToolbarModel::MODE_PARAM_NAME,
+ ToolbarModel::LIMIT_PARAM_NAME
+ ];
+ foreach ($params as $param) {
+ $paramValue = $this->catalogSession->getData($param);
+ if ($paramValue) {
+ $this->httpContext->setValue($param, $paramValue, false);
+ }
+ }
+ }
+ }
+}
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml
index 76f65381f43fa..57f91b78fcbe9 100644
--- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml
@@ -237,8 +237,30 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml
index 1f6c2ab4bb25f..01b31430793cc 100644
--- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml
@@ -276,4 +276,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml
index 903526a862225..80cadbb6571f2 100644
--- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml
@@ -13,7 +13,6 @@
-
@@ -25,7 +24,6 @@
-
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml
index 1bd9bb4a09c86..fd1b6add8391f 100644
--- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml
@@ -164,6 +164,13 @@
+
+
+
+
+
+
+
@@ -206,4 +213,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml
index 301bd4b7a5745..4c7c011028c92 100644
--- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml
@@ -22,6 +22,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -51,4 +66,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CatalogAttributeSetData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogAttributeSetData.xml
new file mode 100644
index 0000000000000..d78c03a51dd75
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogAttributeSetData.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+ test_set_
+ 7
+ 4
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CatalogStorefrontConfigData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogStorefrontConfigData.xml
new file mode 100644
index 0000000000000..d8ec84013d93b
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogStorefrontConfigData.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+ GridPerPageValues
+ RememberCategoryPagination
+
+
+
+ 9,12,20,24
+
+
+
+ 1
+
+
+
+ DefaultCatalogStorefrontFlagZero
+ DefaultListAllowAll
+ DefaultFlatCatalogProduct
+
+
+
+ 0
+
+
+
+ 0
+
+
+
+ 0
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml
index c575f1a5db82f..5be2a84f54555 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml
@@ -65,4 +65,20 @@
false
0
+
+
+ Green
+ false
+ 3
+ Option7Store0
+ Option8Store1
+
+
+
+ Red
+ false
+ 3
+ Option9Store0
+ Option10Store1
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml
index 6f07c0b9eab30..e5b23fe3a3c34 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml
@@ -302,6 +302,20 @@
EavStockItem
CustomAttributeCategoryIds
+
+ testSku
+ simple
+
+ 4
+ testProductName
+ 123.00
+ testurlkey
+ 1
+ 1
+ 100
+ EavStockItem
+ CustomAttributeCategoryIds
+
magento.jpg
@@ -420,6 +434,36 @@
1
EavStock100
+
+ testSku
+ simple
+ 4
+ 4
+ massUpdateProductName
+ massUpdateProductName
+ 123.00
+ masstesturlkey
+ 1
+ 100
+ 1
+ EavStockItem
+ CustomAttributeCategoryIds
+
+
+ testSku
+ simple
+ 4
+ 4
+ massUpdateProductName
+ massUpdateProductName
+ 123.00
+ masstesturlkey
+ 1
+ 100
+ 1
+ EavStockItem
+ CustomAttributeCategoryIds
+
api-simple-product
simple
diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml
index ce964e2d71503..0e51995ac72e8 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml
@@ -56,4 +56,20 @@
1
option6
+
+ 0
+ Green
+
+
+ 1
+ Green
+
+
+ 0
+ Red
+
+
+ 1
+ Red
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_attribute_set-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_attribute_set-meta.xml
new file mode 100644
index 0000000000000..9ef7b507812a0
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_attribute_set-meta.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+ application/json
+
+ integer
+
+
+ application/json
+
+
+ application/json
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_configuration-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_configuration-meta.xml
new file mode 100644
index 0000000000000..b1f2b43220b36
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_configuration-meta.xml
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ integer
+
+
+
+
+ integer
+
+
+
+
+ integer
+
+
+
+
+ integer
+
+
+
+
+ integer
+
+
+
+
+ integer
+
+
+
+
+ integer
+
+
+
+
+ integer
+
+
+
+
+ integer
+
+
+
+
+ integer
+
+
+
+ integer
+
+
+ integer
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductCreatePage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductCreatePage.xml
index fc776b49ba213..b3ed3f478f810 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductCreatePage.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductCreatePage.xml
@@ -17,5 +17,6 @@
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryContentSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryContentSection.xml
index ed0325394d591..acee714fe3ec7 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryContentSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryContentSection.xml
@@ -15,5 +15,6 @@
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryDisplaySettingsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryDisplaySettingsSection.xml
new file mode 100644
index 0000000000000..daa00eb0a27b7
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryDisplaySettingsSection.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml
index 78d06afa7f003..324f261f3a50a 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml
@@ -75,5 +75,6 @@
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFiltersSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFiltersSection.xml
index 7a9de9670f216..06ff54b2a3997 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFiltersSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFiltersSection.xml
@@ -28,5 +28,7 @@
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormActionSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormActionSection.xml
index afbaba41a9bb7..c7d4cd16d788a 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormActionSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormActionSection.xml
@@ -15,5 +15,6 @@
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedInventorySection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedInventorySection.xml
new file mode 100644
index 0000000000000..0314534dcddfb
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedInventorySection.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml
index 03df9d2a88107..249610568aec7 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml
@@ -28,6 +28,7 @@
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml
index a7e20e22f1ddc..59228d57502f1 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml
@@ -28,6 +28,8 @@
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml
index d484abf2069ff..03566be55ad2f 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml
@@ -9,6 +9,9 @@
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml
index 95d74b9653113..6658ad36d7150 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml
@@ -40,4 +40,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml
index a4bd507d98f55..9e323745835c2 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml
@@ -129,8 +129,8 @@
-
+
@@ -140,6 +140,7 @@
userInput="$$createProduct1.name$$" stepKey="seeProductName4"/>
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml
index 69931395a35d5..1cd0e15780c11 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml
@@ -87,10 +87,9 @@
-
-
+
@@ -115,8 +114,7 @@
-
-
+
@@ -136,9 +134,9 @@
-
-
-
+
+
+
@@ -154,16 +152,16 @@
-
+
-
+
-
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml
index 6b444f1f6663b..c845a27773170 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml
@@ -103,12 +103,15 @@
-
+
+
+
-
+
+
+
-
-
+
@@ -119,6 +122,7 @@
+
@@ -141,6 +145,7 @@
+
@@ -179,4 +184,4 @@
-
\ No newline at end of file
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml
index a03636e52ee97..fcdb92d71f1f8 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml
@@ -71,11 +71,17 @@
+
+
+
+
+
-
+
+
@@ -86,6 +92,7 @@
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontRememberCategoryPaginationTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontRememberCategoryPaginationTest.xml
new file mode 100644
index 0000000000000..0ed61b8636c4f
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontRememberCategoryPaginationTest.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml
new file mode 100644
index 0000000000000..f283a040ced41
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Product/ProductList/ToolbarTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Product/ProductList/ToolbarTest.php
index ac963326dbfa1..884f4c543c8b8 100644
--- a/app/code/Magento/Catalog/Test/Unit/Block/Product/ProductList/ToolbarTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Block/Product/ProductList/ToolbarTest.php
@@ -18,6 +18,11 @@ class ToolbarTest extends \PHPUnit\Framework\TestCase
*/
protected $model;
+ /**
+ * @var \Magento\Catalog\Model\Product\ProductList\ToolbarMemorizer | \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $memorizer;
+
/**
* @var \Magento\Framework\Url | \PHPUnit_Framework_MockObject_MockObject
*/
@@ -62,6 +67,16 @@ protected function setUp()
'getLimit',
'getCurrentPage'
]);
+ $this->memorizer = $this->createPartialMock(
+ \Magento\Catalog\Model\Product\ProductList\ToolbarMemorizer::class,
+ [
+ 'getDirection',
+ 'getOrder',
+ 'getMode',
+ 'getLimit',
+ 'isMemorizingAllowed'
+ ]
+ );
$this->layout = $this->createPartialMock(\Magento\Framework\View\Layout::class, ['getChildName', 'getBlock']);
$this->pagerBlock = $this->createPartialMock(\Magento\Theme\Block\Html\Pager::class, [
'setUseContainer',
@@ -116,6 +131,7 @@ protected function setUp()
'context' => $context,
'catalogConfig' => $this->catalogConfig,
'toolbarModel' => $this->model,
+ 'toolbarMemorizer' => $this->memorizer,
'urlEncoder' => $this->urlEncoder,
'productListHelper' => $this->productListHelper
]
@@ -155,7 +171,7 @@ public function testGetPagerEncodedUrl()
public function testGetCurrentOrder()
{
$order = 'price';
- $this->model->expects($this->once())
+ $this->memorizer->expects($this->once())
->method('getOrder')
->will($this->returnValue($order));
$this->catalogConfig->expects($this->once())
@@ -169,7 +185,7 @@ public function testGetCurrentDirection()
{
$direction = 'desc';
- $this->model->expects($this->once())
+ $this->memorizer->expects($this->once())
->method('getDirection')
->will($this->returnValue($direction));
@@ -183,7 +199,7 @@ public function testGetCurrentMode()
$this->productListHelper->expects($this->once())
->method('getAvailableViewMode')
->will($this->returnValue(['list' => 'List']));
- $this->model->expects($this->once())
+ $this->memorizer->expects($this->once())
->method('getMode')
->will($this->returnValue($mode));
@@ -232,11 +248,11 @@ public function testGetLimit()
$mode = 'list';
$limit = 10;
- $this->model->expects($this->once())
+ $this->memorizer->expects($this->once())
->method('getMode')
->will($this->returnValue($mode));
- $this->model->expects($this->once())
+ $this->memorizer->expects($this->once())
->method('getLimit')
->will($this->returnValue($limit));
$this->productListHelper->expects($this->once())
@@ -266,7 +282,7 @@ public function testGetPagerHtml()
$this->productListHelper->expects($this->exactly(2))
->method('getAvailableLimit')
->will($this->returnValue([10 => 10, 20 => 20]));
- $this->model->expects($this->once())
+ $this->memorizer->expects($this->once())
->method('getLimit')
->will($this->returnValue($limit));
$this->pagerBlock->expects($this->once())
diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php
index ff44a91a64998..c889c58e3df3a 100644
--- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php
@@ -95,6 +95,11 @@ class HelperTest extends \PHPUnit\Framework\TestCase
*/
protected $attributeFilterMock;
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $dateTimeFilterMock;
+
/**
* @inheritdoc
*/
@@ -170,6 +175,11 @@ protected function setUp()
$resolverProperty = $helperReflection->getProperty('linkResolver');
$resolverProperty->setAccessible(true);
$resolverProperty->setValue($this->helper, $this->linkResolverMock);
+
+ $this->dateTimeFilterMock = $this->createMock(\Magento\Framework\Stdlib\DateTime\Filter\DateTime::class);
+ $dateTimeFilterProperty = $helperReflection->getProperty('dateTimeFilter');
+ $dateTimeFilterProperty->setAccessible(true);
+ $dateTimeFilterProperty->setValue($this->helper, $this->dateTimeFilterMock);
}
/**
@@ -211,6 +221,12 @@ public function testInitialize(
if (!empty($tierPrice)) {
$productData = array_merge($productData, ['tier_price' => $tierPrice]);
}
+
+ $this->dateTimeFilterMock->expects($this->once())
+ ->method('filter')
+ ->with($specialFromDate)
+ ->willReturn($specialFromDate);
+
$attributeNonDate = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class)
->disableOriginalConstructor()
->getMock();
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Category/Product/PositionResolverTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Category/Product/PositionResolverTest.php
index 1ff3a1bae5c28..7ad8b1a0ab3f8 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/Category/Product/PositionResolverTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Category/Product/PositionResolverTest.php
@@ -107,7 +107,7 @@ public function testGetPositions()
$this->select->expects($this->once())
->method('where')
->willReturnSelf();
- $this->select->expects($this->once())
+ $this->select->expects($this->exactly(2))
->method('order')
->willReturnSelf();
$this->select->expects($this->once())
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/GalleryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/GalleryTest.php
index dfed4e4f37385..47ef3c999125f 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/GalleryTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/GalleryTest.php
@@ -281,6 +281,9 @@ public function testBindValueToEntityRecordExists()
$this->resource->bindValueToEntity($valueId, $entityId);
}
+ /**
+ * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+ */
public function testLoadGallery()
{
$productId = 5;
@@ -329,7 +332,8 @@ public function testLoadGallery()
'main.value_id = entity.value_id',
['entity_id']
)->willReturnSelf();
- $this->product->expects($this->at(0))->method('getData')->with('entity_id')->willReturn($productId);
+ $this->product->expects($this->at(0))->method('getData')
+ ->with('entity_id')->willReturn($productId);
$this->product->expects($this->at(1))->method('getStoreId')->will($this->returnValue($storeId));
$this->connection->expects($this->exactly(2))->method('quoteInto')->withConsecutive(
['value.store_id = ?'],
@@ -338,26 +342,50 @@ public function testLoadGallery()
'value.store_id = ' . $storeId,
'default_value.store_id = ' . 0
);
+ $this->connection->expects($this->any())->method('getIfNullSql')->will(
+ $this->returnValueMap([
+ [
+ '`value`.`label`',
+ '`default_value`.`label`',
+ 'IFNULL(`value`.`label`, `default_value`.`label`)'
+ ],
+ [
+ '`value`.`position`',
+ '`default_value`.`position`',
+ 'IFNULL(`value`.`position`, `default_value`.`position`)'
+ ],
+ [
+ '`value`.`disabled`',
+ '`default_value`.`disabled`',
+ 'IFNULL(`value`.`disabled`, `default_value`.`disabled`)'
+ ]
+ ])
+ );
$this->select->expects($this->at(2))->method('joinLeft')->with(
['value' => $getTableReturnValue],
$quoteInfoReturnValue,
- [
- 'label',
- 'position',
- 'disabled'
- ]
+ []
)->willReturnSelf();
$this->select->expects($this->at(3))->method('joinLeft')->with(
['default_value' => $getTableReturnValue],
$quoteDefaultInfoReturnValue,
- ['label_default' => 'label', 'position_default' => 'position', 'disabled_default' => 'disabled']
+ []
)->willReturnSelf();
- $this->select->expects($this->at(4))->method('where')->with(
+ $this->select->expects($this->at(4))->method('columns')->with([
+ 'label' => 'IFNULL(`value`.`label`, `default_value`.`label`)',
+ 'position' => 'IFNULL(`value`.`position`, `default_value`.`position`)',
+ 'disabled' => 'IFNULL(`value`.`disabled`, `default_value`.`disabled`)',
+ 'label_default' => 'default_value.label',
+ 'position_default' => 'default_value.position',
+ 'disabled_default' => 'default_value.disabled'
+ ])->willReturnSelf();
+ $this->select->expects($this->at(5))->method('where')->with(
'main.attribute_id = ?',
$attributeId
)->willReturnSelf();
- $this->select->expects($this->at(5))->method('where')->with('main.disabled = 0')->willReturnSelf();
- $this->select->expects($this->at(7))->method('where')
+ $this->select->expects($this->at(6))->method('where')
+ ->with('main.disabled = 0')->willReturnSelf();
+ $this->select->expects($this->at(8))->method('where')
->with('entity.entity_id = ?', $productId)
->willReturnSelf();
$this->select->expects($this->once())->method('order')
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php
new file mode 100644
index 0000000000000..4fce12dc2de89
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php
@@ -0,0 +1,237 @@
+objectManager =
+ new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+ $this->connectionMock = $this->createMock(AdapterInterface::class);
+ $this->resourceMock = $this->createMock(ResourceConnection::class);
+ $this->resourceMock->method('getConnection')
+ ->willReturn($this->connectionMock);
+ $this->resourceMock->method('getTableName')
+ ->willReturnArgument(0);
+ $this->generatorMock = $this->createMock(Generator::class);
+ }
+
+ /**
+ * @return MockObject
+ */
+ protected function getVisibleImagesSelectMock(): MockObject
+ {
+ $selectMock = $this->getMockBuilder(Select::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $selectMock->expects($this->once())
+ ->method('distinct')
+ ->willReturnSelf();
+ $selectMock->expects($this->once())
+ ->method('from')
+ ->with(
+ ['images' => Gallery::GALLERY_TABLE],
+ 'value as filepath'
+ )->willReturnSelf();
+ $selectMock->expects($this->once())
+ ->method('where')
+ ->with('disabled = 0')
+ ->willReturnSelf();
+
+ return $selectMock;
+ }
+
+ /**
+ * @param int $imagesCount
+ * @dataProvider dataProvider
+ */
+ public function testGetCountAllProductImages(int $imagesCount): void
+ {
+ $selectMock = $this->getVisibleImagesSelectMock();
+ $selectMock->expects($this->exactly(2))
+ ->method('reset')
+ ->withConsecutive(
+ ['columns'],
+ ['distinct']
+ )->willReturnSelf();
+ $selectMock->expects($this->once())
+ ->method('columns')
+ ->with(new \Zend_Db_Expr('count(distinct value)'))
+ ->willReturnSelf();
+
+ $this->connectionMock->expects($this->once())
+ ->method('select')
+ ->willReturn($selectMock);
+ $this->connectionMock->expects($this->once())
+ ->method('fetchOne')
+ ->with($selectMock)
+ ->willReturn($imagesCount);
+
+ $imageModel = $this->objectManager->getObject(
+ Image::class,
+ [
+ 'generator' => $this->generatorMock,
+ 'resourceConnection' => $this->resourceMock
+ ]
+ );
+
+ $this->assertSame(
+ $imagesCount,
+ $imageModel->getCountAllProductImages()
+ );
+ }
+
+ /**
+ * @param int $imagesCount
+ * @param int $batchSize
+ * @dataProvider dataProvider
+ */
+ public function testGetAllProductImages(
+ int $imagesCount,
+ int $batchSize
+ ): void {
+ $this->connectionMock->expects($this->once())
+ ->method('select')
+ ->willReturn($this->getVisibleImagesSelectMock());
+
+ $batchCount = (int)ceil($imagesCount / $batchSize);
+ $fetchResultsCallback = $this->getFetchResultCallbackForBatches($imagesCount, $batchSize);
+ $this->connectionMock->expects($this->exactly($batchCount))
+ ->method('fetchAll')
+ ->will($this->returnCallback($fetchResultsCallback));
+
+ /** @var Select | MockObject $selectMock */
+ $selectMock = $this->getMockBuilder(Select::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->generatorMock->expects($this->once())
+ ->method('generate')
+ ->with(
+ 'value_id',
+ $selectMock,
+ $batchSize,
+ BatchIteratorInterface::NON_UNIQUE_FIELD_ITERATOR
+ )->will(
+ $this->returnCallback(
+ $this->getBatchIteratorCallback($selectMock, $batchCount)
+ )
+ );
+
+ $imageModel = $this->objectManager->getObject(
+ Image::class,
+ [
+ 'generator' => $this->generatorMock,
+ 'resourceConnection' => $this->resourceMock,
+ 'batchSize' => $batchSize
+ ]
+ );
+
+ $this->assertCount($imagesCount, $imageModel->getAllProductImages());
+ }
+
+ /**
+ * @param int $imagesCount
+ * @param int $batchSize
+ * @return \Closure
+ */
+ protected function getFetchResultCallbackForBatches(
+ int $imagesCount,
+ int $batchSize
+ ): \Closure {
+ $fetchResultsCallback = function () use (&$imagesCount, $batchSize) {
+ $batchSize =
+ ($imagesCount >= $batchSize) ? $batchSize : $imagesCount;
+ $imagesCount -= $batchSize;
+
+ $getFetchResults = function ($batchSize): array {
+ $result = [];
+ $count = $batchSize;
+ while ($count) {
+ $count--;
+ $result[$count] = $count;
+ }
+
+ return $result;
+ };
+
+ return $getFetchResults($batchSize);
+ };
+
+ return $fetchResultsCallback;
+ }
+
+ /**
+ * @param Select | MockObject $selectMock
+ * @param int $batchCount
+ * @return \Closure
+ */
+ protected function getBatchIteratorCallback(
+ MockObject $selectMock,
+ int $batchCount
+ ): \Closure {
+ $iteratorCallback = function () use ($batchCount, $selectMock): array {
+ $result = [];
+ $count = $batchCount;
+ while ($count) {
+ $count--;
+ $result[$count] = $selectMock;
+ }
+
+ return $result;
+ };
+
+ return $iteratorCallback;
+ }
+
+ /**
+ * Data Provider
+ * @return array
+ */
+ public function dataProvider(): array
+ {
+ return [
+ [300, 300],
+ [300, 100],
+ [139, 100],
+ [67, 10],
+ [154, 47],
+ [0, 100]
+ ];
+ }
+}
diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Listing/Collector/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Listing/Collector/ImageTest.php
index 12bc9acfa4c51..009cd690d4cd4 100644
--- a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Listing/Collector/ImageTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Listing/Collector/ImageTest.php
@@ -15,6 +15,7 @@
use Magento\Catalog\Helper\ImageFactory;
use Magento\Catalog\Api\Data\ProductRender\ImageInterface;
use Magento\Catalog\Helper\Image as ImageHelper;
+use Magento\Framework\View\DesignLoader;
/**
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -33,6 +34,9 @@ class ImageTest extends \PHPUnit\Framework\TestCase
/** @var DesignInterface | \PHPUnit_Framework_MockObject_MockObject */
private $design;
+ /** @var DesignLoader | \PHPUnit_Framework_MockObject_MockObject*/
+ private $designLoader;
+
/** @var Image */
private $model;
@@ -60,13 +64,15 @@ public function setUp()
->getMock();
$this->storeManager = $this->createMock(StoreManagerInterface::class);
$this->design = $this->createMock(DesignInterface::class);
+ $this->designLoader = $this->createMock(DesignLoader::class);
$this->model = new Image(
$this->imageFactory,
$this->state,
$this->storeManager,
$this->design,
$this->imageInterfaceFactory,
- $this->imageCodes
+ $this->imageCodes,
+ $this->designLoader
);
}
diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php
index d84f496e81915..7379600011bcf 100755
--- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php
+++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php
@@ -11,6 +11,7 @@
use Magento\Catalog\Model\Attribute\ScopeOverriddenValue;
use Magento\Catalog\Model\Locator\LocatorInterface;
use Magento\Catalog\Model\Product;
+use Magento\Catalog\Model\Product\Type as ProductType;
use Magento\Catalog\Model\ResourceModel\Eav\Attribute as EavAttribute;
use Magento\Catalog\Model\ResourceModel\Eav\AttributeFactory as EavAttributeFactory;
use Magento\Catalog\Ui\DataProvider\CatalogEavValidationRules;
@@ -419,7 +420,7 @@ public function modifyData(array $data)
foreach ($attributes as $attribute) {
if (null !== ($attributeValue = $this->setupAttributeData($attribute))) {
- if ($attribute->getFrontendInput() === 'price' && is_scalar($attributeValue)) {
+ if ($this->isPriceAttribute($attribute, $attributeValue)) {
$attributeValue = $this->formatPrice($attributeValue);
}
$data[$productId][self::DATA_SOURCE_DEFAULT][$attribute->getAttributeCode()] = $attributeValue;
@@ -430,6 +431,32 @@ public function modifyData(array $data)
return $data;
}
+ /**
+ * Obtain if given attribute is a price
+ *
+ * @param \Magento\Catalog\Api\Data\ProductAttributeInterface $attribute
+ * @param string|integer $attributeValue
+ * @return bool
+ */
+ private function isPriceAttribute(ProductAttributeInterface $attribute, $attributeValue)
+ {
+ return $attribute->getFrontendInput() === 'price'
+ && is_scalar($attributeValue)
+ && !$this->isBundleSpecialPrice($attribute);
+ }
+
+ /**
+ * Obtain if current product is bundle and given attribute is special_price
+ *
+ * @param \Magento\Catalog\Api\Data\ProductAttributeInterface $attribute
+ * @return bool
+ */
+ private function isBundleSpecialPrice(ProductAttributeInterface $attribute)
+ {
+ return $this->locator->getProduct()->getTypeId() === ProductType::TYPE_BUNDLE
+ && $attribute->getAttributeCode() === ProductAttributeInterface::CODE_SPECIAL_PRICE;
+ }
+
/**
* Resolve data persistence
*
diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php
index 0eddca3322205..a529580e29239 100644
--- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php
+++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php
@@ -45,7 +45,7 @@ public function __construct(
}
/**
- * {@inheritdoc}
+ * @inheritdoc
* @since 101.1.0
*/
public function modifyData(array $data)
@@ -54,8 +54,11 @@ public function modifyData(array $data)
}
/**
- * {@inheritdoc}
+ * Add tier price info to meta array.
+ *
* @since 101.1.0
+ * @param array $meta
+ * @return array
*/
public function modifyMeta(array $meta)
{
@@ -150,8 +153,8 @@ private function getUpdatedTierPriceStructure(array $priceMeta)
'dataType' => Price::NAME,
'addbefore' => '%',
'validation' => [
- 'validate-number' => true,
- 'less-than-equals-to' => 100
+ 'required-entry' => true,
+ 'validate-positive-percent-decimal' => true
],
'visible' => $firstOption
&& $firstOption['value'] == ProductPriceOptionsInterface::VALUE_PERCENT,
diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php
index 216bc16968fcb..524927ac1c4b4 100644
--- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php
+++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php
@@ -17,12 +17,14 @@
use Magento\Framework\View\DesignInterface;
use Magento\Store\Model\StoreManager;
use Magento\Store\Model\StoreManagerInterface;
+use Magento\Framework\View\DesignLoader;
/**
* Collect enough information about image rendering on front
* If you want to add new image, that should render on front you need
* to configure this class in di.xml
*
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class Image implements ProductRenderCollectorInterface
{
@@ -51,6 +53,7 @@ class Image implements ProductRenderCollectorInterface
/**
* @var DesignInterface
+ * @deprecated 2.3.0 DesignLoader is used for design theme loading
*/
private $design;
@@ -59,6 +62,11 @@ class Image implements ProductRenderCollectorInterface
*/
private $imageRenderInfoFactory;
+ /**
+ * @var DesignLoader
+ */
+ private $designLoader;
+
/**
* Image constructor.
* @param ImageFactory $imageFactory
@@ -67,6 +75,7 @@ class Image implements ProductRenderCollectorInterface
* @param DesignInterface $design
* @param ImageInterfaceFactory $imageRenderInfoFactory
* @param array $imageCodes
+ * @param DesignLoader $designLoader
*/
public function __construct(
ImageFactory $imageFactory,
@@ -74,7 +83,8 @@ public function __construct(
StoreManagerInterface $storeManager,
DesignInterface $design,
ImageInterfaceFactory $imageRenderInfoFactory,
- array $imageCodes = []
+ array $imageCodes = [],
+ DesignLoader $designLoader = null
) {
$this->imageFactory = $imageFactory;
$this->imageCodes = $imageCodes;
@@ -82,6 +92,8 @@ public function __construct(
$this->storeManager = $storeManager;
$this->design = $design;
$this->imageRenderInfoFactory = $imageRenderInfoFactory;
+ $this->designLoader = $designLoader ?: \Magento\Framework\App\ObjectManager::getInstance()
+ ->get(DesignLoader::class);
}
/**
@@ -124,6 +136,8 @@ public function collect(ProductInterface $product, ProductRenderInterface $produ
}
/**
+ * Callback for emulating image creation
+ *
* Callback in which we emulate initialize default design theme, depends on current store, be settings store id
* from render info
*
@@ -136,7 +150,7 @@ public function collect(ProductInterface $product, ProductRenderInterface $produ
public function emulateImageCreating(ProductInterface $product, $imageCode, $storeId, ImageInterface $image)
{
$this->storeManager->setCurrentStore($storeId);
- $this->design->setDefaultDesignTheme();
+ $this->designLoader->load();
$imageHelper = $this->imageFactory->create();
$imageHelper->init($product, $imageCode);
diff --git a/app/code/Magento/Catalog/etc/adminhtml/system.xml b/app/code/Magento/Catalog/etc/adminhtml/system.xml
index d6c98b92596fd..7a05601fcd666 100644
--- a/app/code/Magento/Catalog/etc/adminhtml/system.xml
+++ b/app/code/Magento/Catalog/etc/adminhtml/system.xml
@@ -10,6 +10,9 @@
+
+
+
separator-top
@@ -93,6 +96,11 @@
Whether to show "All" option in the "Show X Per Page" dropdown
Magento\Config\Model\Config\Source\Yesno
+
@@ -194,5 +202,19 @@
+
+ separator-top
+
+ advanced
+ Magento_Config::config_system
+
+
+
+
+ validate-digits validate-digits-range digits-range-1-100 required-entry
+ Jpeg quality for resized images 1-100%.
+
+
+
diff --git a/app/code/Magento/Catalog/etc/config.xml b/app/code/Magento/Catalog/etc/config.xml
index f52760aa50743..3a842166a3825 100644
--- a/app/code/Magento/Catalog/etc/config.xml
+++ b/app/code/Magento/Catalog/etc/config.xml
@@ -30,6 +30,7 @@
0
position
1
+ 0
@@ -66,6 +67,9 @@
custom_options
+
+ 80
+
diff --git a/app/code/Magento/Catalog/etc/db_schema.xml b/app/code/Magento/Catalog/etc/db_schema.xml
index a366065c89e76..17e3dddc41c3b 100644
--- a/app/code/Magento/Catalog/etc/db_schema.xml
+++ b/app/code/Magento/Catalog/etc/db_schema.xml
@@ -835,6 +835,11 @@
+
+
+
+
+
+
+
+
+ - Magento\Catalog\Model\ProductOptionProcessor
+
+
+
diff --git a/app/code/Magento/Catalog/etc/frontend/di.xml b/app/code/Magento/Catalog/etc/frontend/di.xml
index 55098037191e8..ee9c5b29da894 100644
--- a/app/code/Magento/Catalog/etc/frontend/di.xml
+++ b/app/code/Magento/Catalog/etc/frontend/di.xml
@@ -116,4 +116,8 @@
+
+
+
diff --git a/app/code/Magento/Catalog/i18n/en_US.csv b/app/code/Magento/Catalog/i18n/en_US.csv
index f2a3ab8f83f24..ed27dfd646cb2 100644
--- a/app/code/Magento/Catalog/i18n/en_US.csv
+++ b/app/code/Magento/Catalog/i18n/en_US.csv
@@ -233,7 +233,6 @@ Products,Products
"This attribute set no longer exists.","This attribute set no longer exists."
"You saved the attribute set.","You saved the attribute set."
"Something went wrong while saving the attribute set.","Something went wrong while saving the attribute set."
-"You added product %1 to the comparison list.","You added product %1 to the comparison list."
"You cleared the comparison list.","You cleared the comparison list."
"Something went wrong clearing the comparison list.","Something went wrong clearing the comparison list."
"You removed product %1 from the comparison list.","You removed product %1 from the comparison list."
@@ -808,4 +807,5 @@ Details,Details
"Product Name or SKU", "Product Name or SKU"
"Start typing to find products", "Start typing to find products"
"Product with ID: (%1) doesn't exist", "Product with ID: (%1) doesn't exist"
-"Category with ID: (%1) doesn't exist", "Category with ID: (%1) doesn't exist"
\ No newline at end of file
+"Category with ID: (%1) doesn't exist", "Category with ID: (%1) doesn't exist"
+"You added product %1 to the comparison list.","You added product %1 to the comparison list."
\ No newline at end of file
diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main.phtml
index 54b945b48c104..670a943da0aad 100644
--- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main.phtml
+++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main.phtml
@@ -188,6 +188,15 @@
for( j in config[i].children ) {
if(config[i].children[j].id) {
newNode = new Ext.tree.TreeNode(config[i].children[j]);
+
+ if (typeof newNode.ui.onTextChange === 'function') {
+ newNode.ui.onTextChange = function (_3, _4, _5) {
+ if (this.rendered) {
+ this.textNode.innerText = _4;
+ }
+ }
+ }
+ }
node.appendChild(newNode);
newNode.addListener('click', editSet.unregister);
}
@@ -195,13 +204,20 @@
}
}
}
- }
- editSet = function() {
- return {
- register : function(node) {
- editSet.currentNode = node;
- },
+
+ editSet = function () {
+ return {
+ register: function (node) {
+ editSet.currentNode = node;
+ if (typeof node.ui.onTextChange === 'function') {
+ node.ui.onTextChange = function (_3, _4, _5) {
+ if (this.rendered) {
+ this.textNode.innerText = _4;
+ }
+ }
+ }
+ },
unregister : function() {
editSet.currentNode = false;
@@ -293,6 +309,14 @@
allowDrag : true
});
+ if (typeof newNode.ui.onTextChange === 'function') {
+ newNode.ui.onTextChange = function (_3, _4, _5) {
+ if (this.rendered) {
+ this.textNode.innerText = _4;
+ }
+ }
+ }
+
TreePanels.root.appendChild(newNode);
newNode.addListener('beforemove', editSet.groupBeforeMove);
newNode.addListener('beforeinsert', editSet.groupBeforeInsert);
diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/design_config_form.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/design_config_form.xml
index 1e60823929770..cb0beb67c2711 100644
--- a/app/code/Magento/Catalog/view/adminhtml/ui_component/design_config_form.xml
+++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/design_config_form.xml
@@ -18,7 +18,7 @@
2
-
+
Allowed file types: jpeg, gif, png.
@@ -78,7 +78,7 @@
2
-
+
imageUploader
@@ -137,7 +137,7 @@
2
-
+
imageUploader
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 6903a17bcdcca..a2804a8723ce0 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
@@ -469,26 +469,6 @@ define([
}
},
- /**
- * toggles Selects states (for IE) except those to be shown in popup
- */
- /*_toggleSelectsExceptBlock: function(flag) {
- if(Prototype.Browser.IE){
- if (this.blockForm) {
- var states = new Array;
- var selects = this.blockForm.getElementsByTagName("select");
- for(var i=0; i
isSaleable()): ?>
- getTypeInstance()->hasRequiredOptions($_product)): ?>
+ getTypeInstance()->isPossibleBuyFromList($_product)): ?>
+
+
@@ -86,5 +99,8 @@
+
+
+
diff --git a/app/code/Magento/ProductAlert/etc/db_schema_whitelist.json b/app/code/Magento/ProductAlert/etc/db_schema_whitelist.json
index 266d00e04c5bc..94c9d07c85015 100644
--- a/app/code/Magento/ProductAlert/etc/db_schema_whitelist.json
+++ b/app/code/Magento/ProductAlert/etc/db_schema_whitelist.json
@@ -1,6 +1,7 @@
{
"product_alert_price": {
"column": {
+ "store_id": true,
"alert_price_id": true,
"customer_id": true,
"product_id": true,
@@ -12,11 +13,13 @@
"status": true
},
"index": {
+ "PRODUCT_ALERT_PRICE_STORE_ID": true,
"PRODUCT_ALERT_PRICE_CUSTOMER_ID": true,
"PRODUCT_ALERT_PRICE_PRODUCT_ID": true,
"PRODUCT_ALERT_PRICE_WEBSITE_ID": true
},
"constraint": {
+ "PRODUCT_ALERT_PRICE_STORE_ID_STORE_STORE_ID": true,
"PRIMARY": true,
"PRODUCT_ALERT_PRICE_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID": true,
"PRODUCT_ALERT_PRICE_PRODUCT_ID_CATALOG_PRODUCT_ENTITY_ENTITY_ID": true,
@@ -26,6 +29,7 @@
},
"product_alert_stock": {
"column": {
+ "store_id": true,
"alert_stock_id": true,
"customer_id": true,
"product_id": true,
@@ -36,11 +40,13 @@
"status": true
},
"index": {
+ "PRODUCT_ALERT_STOCK_STORE_ID": true,
"PRODUCT_ALERT_STOCK_CUSTOMER_ID": true,
"PRODUCT_ALERT_STOCK_PRODUCT_ID": true,
"PRODUCT_ALERT_STOCK_WEBSITE_ID": true
},
"constraint": {
+ "PRODUCT_ALERT_STOCK_STORE_ID_STORE_STORE_ID": true,
"PRIMARY": true,
"PRODUCT_ALERT_STOCK_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true,
"PRODUCT_ALERT_STOCK_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID": true,
@@ -48,4 +54,4 @@
"PRODUCT_ALERT_STOCK_PRODUCT_ID_SEQUENCE_PRODUCT_SEQUENCE_VALUE": true
}
}
-}
\ No newline at end of file
+}
diff --git a/app/code/Magento/ProductVideo/Model/Plugin/ExternalVideoResourceBackend.php b/app/code/Magento/ProductVideo/Model/Plugin/ExternalVideoResourceBackend.php
index dc64f03a42d19..04a3d868d14a6 100644
--- a/app/code/Magento/ProductVideo/Model/Plugin/ExternalVideoResourceBackend.php
+++ b/app/code/Magento/ProductVideo/Model/Plugin/ExternalVideoResourceBackend.php
@@ -27,6 +27,8 @@ public function __construct(\Magento\ProductVideo\Model\ResourceModel\Video $vid
}
/**
+ * Plugin for after duplicate action
+ *
* @param Gallery $originalResourceModel
* @param array $valueIdMap
* @return array
@@ -45,6 +47,8 @@ public function afterDuplicate(Gallery $originalResourceModel, array $valueIdMap
}
/**
+ * Plugin for after create batch base select action
+ *
* @param Gallery $originalResourceModel
* @param Select $select
* @return Select
@@ -60,13 +64,7 @@ public function afterCreateBatchBaseSelect(Gallery $originalResourceModel, Selec
'value.store_id = value_video.store_id',
]
),
- [
- 'video_provider' => 'provider',
- 'video_url' => 'url',
- 'video_title' => 'title',
- 'video_description' => 'description',
- 'video_metadata' => 'metadata'
- ]
+ []
)->joinLeft(
[
'default_value_video' => $originalResourceModel->getTable(
@@ -80,14 +78,24 @@ public function afterCreateBatchBaseSelect(Gallery $originalResourceModel, Selec
'default_value.store_id = default_value_video.store_id',
]
),
- [
- 'video_provider_default' => 'provider',
- 'video_url_default' => 'url',
- 'video_title_default' => 'title',
- 'video_description_default' => 'description',
- 'video_metadata_default' => 'metadata',
- ]
- );
+ []
+ )->columns([
+ 'video_provider' => $originalResourceModel->getConnection()
+ ->getIfNullSql('`value_video`.`provider`', '`default_value_video`.`provider`'),
+ 'video_url' => $originalResourceModel->getConnection()
+ ->getIfNullSql('`value_video`.`url`', '`default_value_video`.`url`'),
+ 'video_title' => $originalResourceModel->getConnection()
+ ->getIfNullSql('`value_video`.`title`', '`default_value_video`.`title`'),
+ 'video_description' => $originalResourceModel->getConnection()
+ ->getIfNullSql('`value_video`.`description`', '`default_value_video`.`description`'),
+ 'video_metadata' => $originalResourceModel->getConnection()
+ ->getIfNullSql('`value_video`.`metadata`', '`default_value_video`.`metadata`'),
+ 'video_provider_default' => 'default_value_video.provider',
+ 'video_url_default' => 'default_value_video.url',
+ 'video_title_default' => 'default_value_video.title',
+ 'video_description_default' => 'default_value_video.description',
+ 'video_metadata_default' => 'default_value_video.metadata',
+ ]);
return $select;
}
diff --git a/app/code/Magento/ProductVideo/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/ProductVideo/Test/Mftf/Section/StorefrontProductInfoMainSection.xml
index 564122f71b9f4..c8e1ebcf12d94 100644
--- a/app/code/Magento/ProductVideo/Test/Mftf/Section/StorefrontProductInfoMainSection.xml
+++ b/app/code/Magento/ProductVideo/Test/Mftf/Section/StorefrontProductInfoMainSection.xml
@@ -10,5 +10,9 @@
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd">
diff --git a/app/code/Magento/ProductVideo/Test/Mftf/Test/YoutubeVideoWindowOnProductPageTest.xml b/app/code/Magento/ProductVideo/Test/Mftf/Test/YoutubeVideoWindowOnProductPageTest.xml
new file mode 100644
index 0000000000000..7249a4223503e
--- /dev/null
+++ b/app/code/Magento/ProductVideo/Test/Mftf/Test/YoutubeVideoWindowOnProductPageTest.xml
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Quote/Api/Data/CartInterface.php b/app/code/Magento/Quote/Api/Data/CartInterface.php
index 551833e3effb1..b87869de6b3df 100644
--- a/app/code/Magento/Quote/Api/Data/CartInterface.php
+++ b/app/code/Magento/Quote/Api/Data/CartInterface.php
@@ -223,14 +223,14 @@ public function setBillingAddress(\Magento\Quote\Api\Data\AddressInterface $bill
/**
* Returns the reserved order ID for the cart.
*
- * @return int|null Reserved order ID. Otherwise, null.
+ * @return string|null Reserved order ID. Otherwise, null.
*/
public function getReservedOrderId();
/**
* Sets the reserved order ID for the cart.
*
- * @param int $reservedOrderId
+ * @param string $reservedOrderId
* @return $this
*/
public function setReservedOrderId($reservedOrderId);
diff --git a/app/code/Magento/Quote/Model/Quote/Address.php b/app/code/Magento/Quote/Model/Quote/Address.php
index 3eb5d68885035..bafd6634a94c3 100644
--- a/app/code/Magento/Quote/Model/Quote/Address.php
+++ b/app/code/Magento/Quote/Model/Quote/Address.php
@@ -28,8 +28,8 @@
* @method Address setAddressType(string $value)
* @method int getFreeShipping()
* @method Address setFreeShipping(int $value)
- * @method int getCollectShippingRates()
- * @method Address setCollectShippingRates(int $value)
+ * @method bool getCollectShippingRates()
+ * @method Address setCollectShippingRates(bool $value)
* @method Address setShippingMethod(string $value)
* @method string getShippingDescription()
* @method Address setShippingDescription(string $value)
@@ -965,6 +965,7 @@ public function collectShippingRates()
/**
* Request shipping rates for entire address or specified address item
+ *
* Returns true if current selected shipping method code corresponds to one of the found rates
*
* @param \Magento\Quote\Model\Quote\Item\AbstractItem $item
@@ -1002,8 +1003,14 @@ public function requestShippingRates(\Magento\Quote\Model\Quote\Item\AbstractIte
/**
* Store and website identifiers specified from StoreManager
*/
- $request->setStoreId($this->storeManager->getStore()->getId());
- $request->setWebsiteId($this->storeManager->getWebsite()->getId());
+ if ($this->getQuote()->getStoreId()) {
+ $storeId = $this->getQuote()->getStoreId();
+ $request->setStoreId($storeId);
+ $request->setWebsiteId($this->storeManager->getStore($storeId)->getWebsiteId());
+ } else {
+ $request->setStoreId($this->storeManager->getStore()->getId());
+ $request->setWebsiteId($this->storeManager->getWebsite()->getId());
+ }
$request->setFreeShipping($this->getFreeShipping());
/**
* Currencies need to convert in free shipping
@@ -1348,7 +1355,7 @@ public function getAllBaseTotalAmounts()
/******************************* End Total Collector Interface *******************************************/
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
protected function _getValidationRulesBeforeSave()
{
@@ -1356,7 +1363,7 @@ protected function _getValidationRulesBeforeSave()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getCountryId()
{
@@ -1364,7 +1371,7 @@ public function getCountryId()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setCountryId($countryId)
{
@@ -1372,7 +1379,7 @@ public function setCountryId($countryId)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getStreet()
{
@@ -1381,7 +1388,7 @@ public function getStreet()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setStreet($street)
{
@@ -1389,7 +1396,7 @@ public function setStreet($street)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getCompany()
{
@@ -1397,7 +1404,7 @@ public function getCompany()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setCompany($company)
{
@@ -1405,7 +1412,7 @@ public function setCompany($company)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getTelephone()
{
@@ -1413,7 +1420,7 @@ public function getTelephone()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setTelephone($telephone)
{
@@ -1421,7 +1428,7 @@ public function setTelephone($telephone)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getFax()
{
@@ -1429,7 +1436,7 @@ public function getFax()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setFax($fax)
{
@@ -1437,7 +1444,7 @@ public function setFax($fax)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getPostcode()
{
@@ -1445,7 +1452,7 @@ public function getPostcode()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setPostcode($postcode)
{
@@ -1453,7 +1460,7 @@ public function setPostcode($postcode)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getCity()
{
@@ -1461,7 +1468,7 @@ public function getCity()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setCity($city)
{
@@ -1469,7 +1476,7 @@ public function setCity($city)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getFirstname()
{
@@ -1477,7 +1484,7 @@ public function getFirstname()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setFirstname($firstname)
{
@@ -1485,7 +1492,7 @@ public function setFirstname($firstname)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getLastname()
{
@@ -1493,7 +1500,7 @@ public function getLastname()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setLastname($lastname)
{
@@ -1501,7 +1508,7 @@ public function setLastname($lastname)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getMiddlename()
{
@@ -1509,7 +1516,7 @@ public function getMiddlename()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setMiddlename($middlename)
{
@@ -1517,7 +1524,7 @@ public function setMiddlename($middlename)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getPrefix()
{
@@ -1525,7 +1532,7 @@ public function getPrefix()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setPrefix($prefix)
{
@@ -1533,7 +1540,7 @@ public function setPrefix($prefix)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getSuffix()
{
@@ -1541,7 +1548,7 @@ public function getSuffix()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setSuffix($suffix)
{
@@ -1549,7 +1556,7 @@ public function setSuffix($suffix)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getVatId()
{
@@ -1557,7 +1564,7 @@ public function getVatId()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setVatId($vatId)
{
@@ -1565,7 +1572,7 @@ public function setVatId($vatId)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getCustomerId()
{
@@ -1573,7 +1580,7 @@ public function getCustomerId()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setCustomerId($customerId)
{
@@ -1581,7 +1588,7 @@ public function setCustomerId($customerId)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getEmail()
{
@@ -1594,7 +1601,7 @@ public function getEmail()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setEmail($email)
{
@@ -1602,7 +1609,7 @@ public function setEmail($email)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setRegion($region)
{
@@ -1610,7 +1617,7 @@ public function setRegion($region)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setRegionId($regionId)
{
@@ -1618,7 +1625,7 @@ public function setRegionId($regionId)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setRegionCode($regionCode)
{
@@ -1626,7 +1633,7 @@ public function setRegionCode($regionCode)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getSameAsBilling()
{
@@ -1634,7 +1641,7 @@ public function getSameAsBilling()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setSameAsBilling($sameAsBilling)
{
@@ -1642,7 +1649,7 @@ public function setSameAsBilling($sameAsBilling)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getCustomerAddressId()
{
@@ -1650,7 +1657,7 @@ public function getCustomerAddressId()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setCustomerAddressId($customerAddressId)
{
@@ -1681,7 +1688,7 @@ public function setSaveInAddressBook($saveInAddressBook)
//@codeCoverageIgnoreEnd
/**
- * {@inheritdoc}
+ * @inheritdoc
*
* @return \Magento\Quote\Api\Data\AddressExtensionInterface|null
*/
@@ -1691,7 +1698,7 @@ public function getExtensionAttributes()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*
* @param \Magento\Quote\Api\Data\AddressExtensionInterface $extensionAttributes
* @return $this
@@ -1712,7 +1719,7 @@ public function getShippingMethod()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
protected function getCustomAttributesCodes()
{
diff --git a/app/code/Magento/Quote/Test/Mftf/ActionGroup/ChangeStatusProductUsingProductGridActionGroup.xml b/app/code/Magento/Quote/Test/Mftf/ActionGroup/ChangeStatusProductUsingProductGridActionGroup.xml
deleted file mode 100644
index dba4a94f3db2a..0000000000000
--- a/app/code/Magento/Quote/Test/Mftf/ActionGroup/ChangeStatusProductUsingProductGridActionGroup.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/code/Magento/Quote/Test/Mftf/Section/AdminProductGridSection.xml b/app/code/Magento/Quote/Test/Mftf/Section/AdminProductGridSection.xml
deleted file mode 100644
index 32ac73aca7c03..0000000000000
--- a/app/code/Magento/Quote/Test/Mftf/Section/AdminProductGridSection.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/Address/AddressDataProvider.php b/app/code/Magento/QuoteGraphQl/Model/Cart/Address/AddressDataProvider.php
new file mode 100644
index 0000000000000..fb742477ec99b
--- /dev/null
+++ b/app/code/Magento/QuoteGraphQl/Model/Cart/Address/AddressDataProvider.php
@@ -0,0 +1,94 @@
+dataObjectConverter = $dataObjectConverter;
+ }
+
+ /**
+ * Collect and return information about shipping and billing addresses
+ *
+ * @param CartInterface $cart
+ * @return array
+ */
+ public function getCartAddresses(CartInterface $cart): array
+ {
+ $addressData = [];
+ $shippingAddress = $cart->getShippingAddress();
+ $billingAddress = $cart->getBillingAddress();
+
+ if ($shippingAddress) {
+ $shippingData = $this->dataObjectConverter->toFlatArray($shippingAddress, [], AddressInterface::class);
+ $shippingData['address_type'] = 'SHIPPING';
+ $addressData[] = array_merge($shippingData, $this->extractAddressData($shippingAddress));
+ }
+
+ if ($billingAddress) {
+ $billingData = $this->dataObjectConverter->toFlatArray($billingAddress, [], AddressInterface::class);
+ $billingData['address_type'] = 'BILLING';
+ $addressData[] = array_merge($billingData, $this->extractAddressData($billingAddress));
+ }
+
+ return $addressData;
+ }
+
+ /**
+ * Extract the necessary address fields from address model
+ *
+ * @param QuoteAddress $address
+ * @return array
+ */
+ private function extractAddressData(QuoteAddress $address): array
+ {
+ $addressData = [
+ 'country' => [
+ 'code' => $address->getCountryId(),
+ 'label' => $address->getCountry()
+ ],
+ 'region' => [
+ 'code' => $address->getRegionCode(),
+ 'label' => $address->getRegion()
+ ],
+ 'street' => $address->getStreet(),
+ 'selected_shipping_method' => [
+ 'code' => $address->getShippingMethod(),
+ 'label' => $address->getShippingDescription(),
+ 'free_shipping' => $address->getFreeShipping(),
+ ],
+ 'items_weight' => $address->getWeight(),
+ 'customer_notes' => $address->getCustomerNotes()
+ ];
+
+ return $addressData;
+ }
+}
diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php
new file mode 100644
index 0000000000000..b9fd5c7807d2f
--- /dev/null
+++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php
@@ -0,0 +1,98 @@
+shippingAddressManagement = $shippingAddressManagement;
+ $this->addressRepository = $addressRepository;
+ $this->addressModel = $addressModel;
+ $this->checkCustomerAccount = $checkCustomerAccount;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function execute(ContextInterface $context, CartInterface $cart, array $shippingAddresses): void
+ {
+ if (count($shippingAddresses) > 1) {
+ throw new GraphQlInputException(
+ __('You cannot specify multiple shipping addresses.')
+ );
+ }
+ $shippingAddress = current($shippingAddresses);
+ $customerAddressId = $shippingAddress['customer_address_id'] ?? null;
+ $addressInput = $shippingAddress['address'] ?? null;
+
+ if (null === $customerAddressId && null === $addressInput) {
+ throw new GraphQlInputException(
+ __('The shipping address must contain either "customer_address_id" or "address".')
+ );
+ }
+ if ($customerAddressId && $addressInput) {
+ throw new GraphQlInputException(
+ __('The shipping address cannot contain "customer_address_id" and "address" at the same time.')
+ );
+ }
+ if (null === $customerAddressId) {
+ $shippingAddress = $this->addressModel->addData($addressInput);
+ } else {
+ $this->checkCustomerAccount->execute($context->getUserId(), $context->getUserType());
+
+ /** @var AddressInterface $customerAddress */
+ $customerAddress = $this->addressRepository->getById($customerAddressId);
+ $shippingAddress = $this->addressModel->importCustomerAddressData($customerAddress);
+ }
+
+ $this->shippingAddressManagement->assign($cart->getId(), $shippingAddress);
+ }
+}
diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCartInterface.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCartInterface.php
new file mode 100644
index 0000000000000..c5da3db75add7
--- /dev/null
+++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCartInterface.php
@@ -0,0 +1,32 @@
+shippingInformationManagement = $shippingInformationManagement;
+ $this->quoteAddressResource = $quoteAddressResource;
+ $this->quoteAddressFactory = $quoteAddressFactory;
+ $this->shippingInformationFactory = $shippingInformationFactory;
+ }
+
+ /**
+ * Sets shipping method for a specified shopping cart address
+ *
+ * @param Quote $cart
+ * @param int $cartAddressId
+ * @param string $carrierCode
+ * @param string $methodCode
+ * @throws GraphQlInputException
+ * @throws GraphQlNoSuchEntityException
+ */
+ public function execute(Quote $cart, int $cartAddressId, string $carrierCode, string $methodCode): void
+ {
+ $quoteAddress = $this->quoteAddressFactory->create();
+ $this->quoteAddressResource->load($quoteAddress, $cartAddressId);
+
+ /** @var ShippingInformation $shippingInformation */
+ $shippingInformation = $this->shippingInformationFactory->create();
+
+ /* If the address is not a shipping address (but billing) the system will find the proper shipping address for
+ the selected cart and set the information there (actual for single shipping address) */
+ $shippingInformation->setShippingAddress($quoteAddress);
+ $shippingInformation->setShippingCarrierCode($carrierCode);
+ $shippingInformation->setShippingMethodCode($methodCode);
+
+ try {
+ $this->shippingInformationManagement->saveAddressInformation($cart->getId(), $shippingInformation);
+ } catch (NoSuchEntityException $exception) {
+ throw new GraphQlNoSuchEntityException(__($exception->getMessage()));
+ } catch (StateException $exception) {
+ throw new GraphQlInputException(__($exception->getMessage()));
+ } catch (InputException $exception) {
+ throw new GraphQlInputException(__($exception->getMessage()));
+ }
+ }
+}
diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddresses.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddresses.php
new file mode 100644
index 0000000000000..69544672bf12e
--- /dev/null
+++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddresses.php
@@ -0,0 +1,48 @@
+addressDataProvider = $addressDataProvider;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
+ {
+ if (!isset($value['model'])) {
+ throw new LocalizedException(__('"model" value should be specified'));
+ }
+
+ $cart = $value['model'];
+
+ return $this->addressDataProvider->getCartAddresses($cart);
+ }
+}
diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingAddressesOnCart.php
new file mode 100644
index 0000000000000..b024e7b77af40
--- /dev/null
+++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingAddressesOnCart.php
@@ -0,0 +1,100 @@
+maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId;
+ $this->shippingAddressManagement = $shippingAddressManagement;
+ $this->getCartForUser = $getCartForUser;
+ $this->arrayManager = $arrayManager;
+ $this->setShippingAddressesOnCart = $setShippingAddressesOnCart;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
+ {
+ $shippingAddresses = $this->arrayManager->get('input/shipping_addresses', $args);
+ $maskedCartId = $this->arrayManager->get('input/cart_id', $args);
+
+ if (!$maskedCartId) {
+ throw new GraphQlInputException(__('Required parameter "cart_id" is missing'));
+ }
+ if (!$shippingAddresses) {
+ throw new GraphQlInputException(__('Required parameter "shipping_addresses" is missing'));
+ }
+
+ $maskedCartId = $args['input']['cart_id'];
+ $cart = $this->getCartForUser->execute($maskedCartId, $context->getUserId());
+
+ $this->setShippingAddressesOnCart->execute($context, $cart, $shippingAddresses);
+
+ return [
+ 'cart' => [
+ 'cart_id' => $maskedCartId,
+ 'model' => $cart,
+ ]
+ ];
+ }
+}
diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php
new file mode 100644
index 0000000000000..920829f5d67b1
--- /dev/null
+++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php
@@ -0,0 +1,99 @@
+arrayManager = $arrayManager;
+ $this->getCartForUser = $getCartForUser;
+ $this->setShippingMethodOnCart = $setShippingMethodOnCart;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
+ {
+ $shippingMethods = $this->arrayManager->get('input/shipping_methods', $args);
+ $maskedCartId = $this->arrayManager->get('input/cart_id', $args);
+
+ if (!$maskedCartId) {
+ throw new GraphQlInputException(__('Required parameter "cart_id" is missing'));
+ }
+ if (!$shippingMethods) {
+ throw new GraphQlInputException(__('Required parameter "shipping_methods" is missing'));
+ }
+
+ $shippingMethod = reset($shippingMethods); // This point can be extended for multishipping
+
+ if (!$shippingMethod['cart_address_id']) {
+ throw new GraphQlInputException(__('Required parameter "cart_address_id" is missing'));
+ }
+ if (!$shippingMethod['shipping_carrier_code']) {
+ throw new GraphQlInputException(__('Required parameter "shipping_carrier_code" is missing'));
+ }
+ if (!$shippingMethod['shipping_method_code']) {
+ throw new GraphQlInputException(__('Required parameter "shipping_method_code" is missing'));
+ }
+
+ $userId = $context->getUserId();
+ $cart = $this->getCartForUser->execute((string) $maskedCartId, $userId);
+
+ $this->setShippingMethodOnCart->execute(
+ $cart,
+ $shippingMethod['cart_address_id'],
+ $shippingMethod['shipping_carrier_code'],
+ $shippingMethod['shipping_method_code']
+ );
+
+ return [
+ 'cart' => [
+ 'cart_id' => $maskedCartId,
+ 'model' => $cart
+ ]
+ ];
+ }
+}
diff --git a/app/code/Magento/QuoteGraphQl/composer.json b/app/code/Magento/QuoteGraphQl/composer.json
index c9900dd5f3150..1bf4d581a5fe3 100644
--- a/app/code/Magento/QuoteGraphQl/composer.json
+++ b/app/code/Magento/QuoteGraphQl/composer.json
@@ -6,8 +6,11 @@
"php": "~7.1.3||~7.2.0",
"magento/framework": "*",
"magento/module-quote": "*",
+ "magento/module-checkout": "*",
"magento/module-catalog": "*",
- "magento/module-store": "*"
+ "magento/module-store": "*",
+ "magento/module-customer": "*",
+ "magento/module-customer-graph-ql": "*"
},
"suggest": {
"magento/module-graph-ql": "*"
diff --git a/app/code/Magento/CatalogUrlRewrite/etc/webapi_soap/di.xml b/app/code/Magento/QuoteGraphQl/etc/graphql/di.xml
similarity index 62%
rename from app/code/Magento/CatalogUrlRewrite/etc/webapi_soap/di.xml
rename to app/code/Magento/QuoteGraphQl/etc/graphql/di.xml
index ac8beb362f0fb..86bc954ae4ac4 100644
--- a/app/code/Magento/CatalogUrlRewrite/etc/webapi_soap/di.xml
+++ b/app/code/Magento/QuoteGraphQl/etc/graphql/di.xml
@@ -6,5 +6,6 @@
*/
-->
-
+
diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls
index f692aa57b2180..edc643973ce77 100644
--- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls
+++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls
@@ -1,13 +1,93 @@
# Copyright © Magento, Inc. All rights reserved.
# See COPYING.txt for license details.
+type Query {
+ getAvailableShippingMethodsOnCart(input: AvailableShippingMethodsOnCartInput): AvailableShippingMethodsOnCartOutput @doc(description:"Returns available shipping methods for cart by address/address_id")
+}
+
type Mutation {
- createEmptyCart: String @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CreateEmptyCart") @doc(description:"Creates empty shopping cart for guest or logged in user")
+ createEmptyCart: String @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CreateEmptyCart") @doc(description:"Creates an empty shopping cart for a guest or logged in user")
+ applyCouponToCart(input: ApplyCouponToCartInput): ApplyCouponToCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Coupon\\ApplyCouponToCart")
+ removeCouponFromCart(input: RemoveCouponFromCartInput): RemoveCouponFromCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Coupon\\RemoveCouponFromCart")
+ setShippingAddressesOnCart(input: SetShippingAddressesOnCartInput): SetShippingAddressesOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetShippingAddressesOnCart")
applyCouponToCart(input: ApplyCouponToCartInput): ApplyCouponToCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ApplyCouponToCart")
removeCouponFromCart(input: RemoveCouponFromCartInput): RemoveCouponFromCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\RemoveCouponFromCart")
+ setBillingAddressOnCart(input: SetBillingAddressOnCartInput): SetBillingAddressOnCartOutput
+ setShippingMethodsOnCart(input: SetShippingMethodsOnCartInput): SetShippingMethodsOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetShippingMethodsOnCart")
addSimpleProductsToCart(input: AddSimpleProductsToCartInput): AddSimpleProductsToCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AddSimpleProductsToCart")
}
+input SetShippingAddressesOnCartInput {
+ cart_id: String!
+ shipping_addresses: [ShippingAddressInput!]!
+}
+
+input ShippingAddressInput {
+ customer_address_id: Int # Can be provided in one-page checkout and is required for multi-shipping checkout
+ address: CartAddressInput
+ cart_items: [CartItemQuantityInput!]
+}
+
+input CartItemQuantityInput {
+ cart_item_id: Int!
+ quantity: Float!
+}
+
+input SetBillingAddressOnCartInput {
+ cart_id: String!
+ customer_address_id: Int
+ address: CartAddressInput
+ # TODO: consider adding "Same as shipping" option
+}
+
+input CartAddressInput {
+ firstname: String!
+ lastname: String!
+ company: String
+ street: [String!]!
+ city: String!
+ region: String
+ postcode: String
+ country_code: String!
+ telephone: String!
+ save_in_address_book: Boolean!
+}
+
+input SetShippingMethodsOnCartInput {
+ cart_id: String!
+ shipping_methods: [ShippingMethodForAddressInput!]!
+}
+
+input ShippingMethodForAddressInput {
+ cart_address_id: Int!
+ shipping_carrier_code: String!
+ shipping_method_code: String!
+}
+
+type SetBillingAddressOnCartOutput {
+ cart: Cart!
+}
+
+type SetShippingAddressesOnCartOutput {
+ cart: Cart!
+}
+
+type SetShippingMethodsOnCartOutput {
+ cart: Cart!
+}
+
+# If no address is provided, the system get address assigned to a quote
+# If there's no address at all - the system returns all shipping methods
+input AvailableShippingMethodsOnCartInput {
+ cart_id: String!
+ customer_address_id: Int
+ address: CartAddressInput
+}
+
+type AvailableShippingMethodsOnCartOutput {
+ available_shipping_methods: [CheckoutShippingMethod]
+}
+
input ApplyCouponToCartInput {
cart_id: String!
coupon_code: String!
@@ -18,12 +98,56 @@ type ApplyCouponToCartOutput {
}
type Cart {
+ cart_id: String
items: [CartItemInterface]
applied_coupon: AppliedCoupon
+ addresses: [CartAddress]! @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CartAddresses")
}
type CartAddress {
- applied_coupon: AppliedCoupon
+ firstname: String
+ lastname: String
+ company: String
+ street: [String]
+ city: String
+ region: CartAddressRegion
+ postcode: String
+ country: CartAddressCountry
+ telephone: String
+ address_type: AdressTypeEnum
+ selected_shipping_method: CheckoutShippingMethod
+ available_shipping_methods: [CheckoutShippingMethod]
+ items_weight: Float
+ customer_notes: String
+ cart_items: [CartItemQuantity]
+}
+
+type CartItemQuantity {
+ cart_item_id: String!
+ quantity: Float!
+}
+
+type CartAddressRegion {
+ code: String
+ label: String
+}
+
+type CartAddressCountry {
+ code: String
+ label: String
+}
+
+type CheckoutShippingMethod {
+ code: String
+ label: String
+ free_shipping: Boolean!
+ error_message: String
+ # TODO: Add more complex structure for shipping rates
+}
+
+enum AdressTypeEnum {
+ SHIPPING
+ BILLING
}
type AppliedCoupon {
diff --git a/app/code/Magento/Reports/Model/Product/Index/AbstractIndex.php b/app/code/Magento/Reports/Model/Product/Index/AbstractIndex.php
index 5682892a77c60..7337286149cc3 100644
--- a/app/code/Magento/Reports/Model/Product/Index/AbstractIndex.php
+++ b/app/code/Magento/Reports/Model/Product/Index/AbstractIndex.php
@@ -113,7 +113,7 @@ public function beforeSave()
/**
* Retrieve visitor id
*
- * if don't exists return current visitor id
+ * If don't exists return current visitor id
*
* @return int
*/
@@ -128,7 +128,7 @@ public function getVisitorId()
/**
* Retrieve customer id
*
- * if customer don't logged in return null
+ * If customer don't logged in return null
*
* @return int
*/
@@ -143,7 +143,7 @@ public function getCustomerId()
/**
* Retrieve store id
*
- * default return current store id
+ * Default return current store id
*
* @return int
*/
@@ -246,13 +246,14 @@ public function clean()
/**
* Add product ids to current visitor/customer log
+ *
* @param string[] $productIds
* @return $this
*/
public function registerIds($productIds)
{
$this->_getResource()->registerIds($this, $productIds);
- $this->_getSession()->unsData($this->_countCacheKey);
+ $this->_getSession()->unsetData($this->_countCacheKey);
return $this;
}
}
diff --git a/app/code/Magento/Reports/Test/Mftf/ActionGroup/AdminReviewOrderActionGroup.xml b/app/code/Magento/Reports/Test/Mftf/ActionGroup/AdminReviewOrderActionGroup.xml
new file mode 100644
index 0000000000000..003a5e6655f34
--- /dev/null
+++ b/app/code/Magento/Reports/Test/Mftf/ActionGroup/AdminReviewOrderActionGroup.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Reports/Test/Mftf/Section/OrderedProductsSection.xml b/app/code/Magento/Reports/Test/Mftf/Section/OrderedProductsSection.xml
new file mode 100644
index 0000000000000..89e8497dddcea
--- /dev/null
+++ b/app/code/Magento/Reports/Test/Mftf/Section/OrderedProductsSection.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Review/Model/ResourceModel/Rating.php b/app/code/Magento/Review/Model/ResourceModel/Rating.php
index 3f54c17f6ff7c..78020ce3f8184 100644
--- a/app/code/Magento/Review/Model/ResourceModel/Rating.php
+++ b/app/code/Magento/Review/Model/ResourceModel/Rating.php
@@ -178,6 +178,8 @@ protected function _afterSave(\Magento\Framework\Model\AbstractModel $object)
}
/**
+ * Process rating codes
+ *
* @param \Magento\Framework\Model\AbstractModel $object
* @return $this
*/
@@ -201,6 +203,8 @@ protected function processRatingCodes(\Magento\Framework\Model\AbstractModel $ob
}
/**
+ * Process rating stores
+ *
* @param \Magento\Framework\Model\AbstractModel $object
* @return $this
*/
@@ -224,6 +228,8 @@ protected function processRatingStores(\Magento\Framework\Model\AbstractModel $o
}
/**
+ * Delete rating data
+ *
* @param int $ratingId
* @param string $table
* @param array $storeIds
@@ -247,6 +253,8 @@ protected function deleteRatingData($ratingId, $table, array $storeIds)
}
/**
+ * Insert rating data
+ *
* @param string $table
* @param array $data
* @return void
@@ -269,6 +277,7 @@ protected function insertRatingData($table, array $data)
/**
* Perform actions after object delete
+ *
* Prepare rating data for reaggregate all data for reviews
*
* @param \Magento\Framework\Model\AbstractModel $object
@@ -425,9 +434,11 @@ public function getReviewSummary($object, $onlyForCurrentStore = true)
$data = $connection->fetchAll($select, [':review_id' => $object->getReviewId()]);
+ $currentStore = $this->_storeManager->isSingleStoreMode() ? $this->_storeManager->getStore()->getId() : null;
+
if ($onlyForCurrentStore) {
foreach ($data as $row) {
- if ($row['store_id'] == $this->_storeManager->getStore()->getId()) {
+ if ($row['store_id'] !== $currentStore) {
$object->addData($row);
}
}
diff --git a/app/code/Magento/Review/Model/Review.php b/app/code/Magento/Review/Model/Review.php
index c00af3fc61407..e689d4ed460ac 100644
--- a/app/code/Magento/Review/Model/Review.php
+++ b/app/code/Magento/Review/Model/Review.php
@@ -5,6 +5,7 @@
*/
namespace Magento\Review\Model;
+use Magento\Framework\DataObject;
use Magento\Catalog\Model\Product;
use Magento\Framework\DataObject\IdentityInterface;
use Magento\Review\Model\ResourceModel\Review\Product\Collection as ProductCollection;
@@ -327,6 +328,9 @@ public function appendSummary($collection)
$item->setRatingSummary($summary);
}
}
+ if (!$item->getRatingSummary()) {
+ $item->setRatingSummary(new DataObject());
+ }
}
return $this;
diff --git a/app/code/Magento/Rule/Model/Condition/Sql/Builder.php b/app/code/Magento/Rule/Model/Condition/Sql/Builder.php
index 995999c3a0cde..6267e30a7a6d5 100644
--- a/app/code/Magento/Rule/Model/Condition/Sql/Builder.php
+++ b/app/code/Magento/Rule/Model/Condition/Sql/Builder.php
@@ -32,9 +32,13 @@ class Builder
'==' => ':field = ?',
'!=' => ':field <> ?',
'>=' => ':field >= ?',
+ '>=' => ':field >= ?',
'>' => ':field > ?',
+ '>' => ':field > ?',
'<=' => ':field <= ?',
+ '<=' => ':field <= ?',
'<' => ':field < ?',
+ '<' => ':field < ?',
'{}' => ':field IN (?)',
'!{}' => ':field NOT IN (?)',
'()' => ':field IN (?)',
diff --git a/app/code/Magento/Rule/Test/Unit/Model/Condition/Sql/BuilderTest.php b/app/code/Magento/Rule/Test/Unit/Model/Condition/Sql/BuilderTest.php
index 0a2767a94668a..9dcbbd18c4c20 100644
--- a/app/code/Magento/Rule/Test/Unit/Model/Condition/Sql/BuilderTest.php
+++ b/app/code/Magento/Rule/Test/Unit/Model/Condition/Sql/BuilderTest.php
@@ -72,4 +72,69 @@ public function testAttachConditionToCollection()
$this->_builder->attachConditionToCollection($collection, $combine);
}
+
+ /**
+ * Test for attach condition to collection with operator in html format
+ *
+ * @covers \Magento\Rule\Model\Condition\Sql\Builder::attachConditionToCollection()
+ * @return void;
+ */
+ public function testAttachConditionAsHtmlToCollection()
+ {
+ $abstractCondition = $this->getMockForAbstractClass(
+ \Magento\Rule\Model\Condition\AbstractCondition::class,
+ [],
+ '',
+ false,
+ false,
+ true,
+ ['getOperatorForValidate', 'getMappedSqlField', 'getAttribute', 'getBindArgumentValue']
+ );
+
+ $abstractCondition->expects($this->once())->method('getMappedSqlField')->will($this->returnValue('argument'));
+ $abstractCondition->expects($this->once())->method('getOperatorForValidate')->will($this->returnValue('>'));
+ $abstractCondition->expects($this->at(1))->method('getAttribute')->will($this->returnValue('attribute'));
+ $abstractCondition->expects($this->at(2))->method('getAttribute')->will($this->returnValue('attribute'));
+ $abstractCondition->expects($this->once())->method('getBindArgumentValue')->will($this->returnValue(10));
+
+ $conditions = [$abstractCondition];
+ $collection = $this->createPartialMock(
+ \Magento\Eav\Model\Entity\Collection\AbstractCollection::class,
+ [
+ 'getResource',
+ 'getSelect'
+ ]
+ );
+ $combine = $this->createPartialMock(
+ \Magento\Rule\Model\Condition\Combine::class,
+ [
+ 'getConditions',
+ 'getValue',
+ 'getAggregator'
+ ]
+ );
+
+ $resource = $this->createPartialMock(\Magento\Framework\DB\Adapter\Pdo\Mysql::class, ['getConnection']);
+ $select = $this->createPartialMock(\Magento\Framework\DB\Select::class, ['where']);
+ $select->expects($this->never())->method('where');
+
+ $connection = $this->getMockForAbstractClass(
+ \Magento\Framework\DB\Adapter\AdapterInterface::class,
+ ['quoteInto'],
+ '',
+ false
+ );
+
+ $connection->expects($this->once())->method('quoteInto')->with(' > ?', 10)->will($this->returnValue(' > 10'));
+ $collection->expects($this->once())->method('getResource')->will($this->returnValue($resource));
+ $resource->expects($this->once())->method('getConnection')->will($this->returnValue($connection));
+ $combine->expects($this->once())->method('getValue')->willReturn('attribute');
+ $combine->expects($this->once())->method('getAggregator')->willReturn(' AND ');
+ $combine->expects($this->at(0))->method('getConditions')->will($this->returnValue($conditions));
+ $combine->expects($this->at(1))->method('getConditions')->will($this->returnValue($conditions));
+ $combine->expects($this->at(2))->method('getConditions')->will($this->returnValue($conditions));
+ $combine->expects($this->at(3))->method('getConditions')->will($this->returnValue($conditions));
+
+ $this->_builder->attachConditionToCollection($collection, $combine);
+ }
}
diff --git a/app/code/Magento/Sales/Block/Order/Items.php b/app/code/Magento/Sales/Block/Order/Items.php
index 028544cd56219..d7255a24aead5 100644
--- a/app/code/Magento/Sales/Block/Order/Items.php
+++ b/app/code/Magento/Sales/Block/Order/Items.php
@@ -5,13 +5,13 @@
*/
/**
- * Sales order view items block
- *
* @author Magento Core Team
*/
namespace Magento\Sales\Block\Order;
/**
+ * Sales order view items block.
+ *
* @api
* @since 100.0.2
*/
@@ -71,7 +71,6 @@ protected function _prepareLayout()
$this->itemCollection = $this->itemCollectionFactory->create();
$this->itemCollection->setOrderFilter($this->getOrder());
- $this->itemCollection->filterByParent(null);
/** @var \Magento\Theme\Block\Html\Pager $pagerBlock */
$pagerBlock = $this->getChildBlock('sales_order_item_pager');
@@ -87,8 +86,9 @@ protected function _prepareLayout()
}
/**
- * Determine if the pager should be displayed for order items list
- * To be called from templates(after _prepareLayout())
+ * Determine if the pager should be displayed for order items list.
+ *
+ * To be called from templates(after _prepareLayout()).
*
* @return bool
* @since 100.1.7
@@ -101,7 +101,8 @@ public function isPagerDisplayed()
/**
* Get visible items for current page.
- * To be called from templates(after _prepareLayout())
+ *
+ * To be called from templates(after _prepareLayout()).
*
* @return \Magento\Framework\DataObject[]
* @since 100.1.7
@@ -112,8 +113,9 @@ public function getItems()
}
/**
- * Get pager HTML according to our requirements
- * To be called from templates(after _prepareLayout())
+ * Get pager HTML according to our requirements.
+ *
+ * To be called from templates(after _prepareLayout()).
*
* @return string HTML output
* @since 100.1.7
diff --git a/app/code/Magento/Sales/Controller/AbstractController/Reorder.php b/app/code/Magento/Sales/Controller/AbstractController/Reorder.php
index dd4d73930cc8f..65cb537e89fec 100644
--- a/app/code/Magento/Sales/Controller/AbstractController/Reorder.php
+++ b/app/code/Magento/Sales/Controller/AbstractController/Reorder.php
@@ -1,15 +1,21 @@
]*href\s*?=\s*?([\"\']??)([^\" >]*?)\\1[^>]*>(.*)<\/a>/siU";
+ $regexp = "#(?J).*?)\\1\s*)|(?:\S+\s*=\s*(['\"])(.*?)\\3)\s*)*)|>)"
+ .">?(?:(?:(?.*?)(?:<\/a\s*>?|(?=<\w))|(?.*)))#si";
while (preg_match($regexp, $data, $matches)) {
- //Revert the sprintf escaping
- $url = str_replace('%%', '%', $matches[2]);
- $text = str_replace('%%', '%', $matches[3]);
- //Check for an valid url
- if ($url) {
- $urlScheme = strtolower(parse_url($url, PHP_URL_SCHEME));
- if ($urlScheme !== 'http' && $urlScheme !== 'https') {
- $url = null;
- }
- }
- //Use hash tag as fallback
- if (!$url) {
- $url = '#';
+ $text = '';
+ if (!empty($matches['text'])) {
+ $text = str_replace('%%', '%', $matches['text']);
}
+ $url = $this->filterUrl($matches['link'] ?? '');
//Recreate a minimalistic secure a tag
$links[] = sprintf(
'%s',
@@ -178,4 +176,29 @@ public function escapeHtmlWithLinks($data, $allowedTags = null)
}
return $this->escaper->escapeHtml($data, $allowedTags);
}
+
+ /**
+ * Filter the URL for allowed protocols.
+ *
+ * @param string $url
+ * @return string
+ */
+ private function filterUrl(string $url): string
+ {
+ if ($url) {
+ //Revert the sprintf escaping
+ $url = str_replace('%%', '%', $url);
+ $urlScheme = parse_url($url, PHP_URL_SCHEME);
+ $urlScheme = $urlScheme ? strtolower($urlScheme) : '';
+ if ($urlScheme !== 'http' && $urlScheme !== 'https') {
+ $url = null;
+ }
+ }
+
+ if (!$url) {
+ $url = '#';
+ }
+
+ return $url;
+ }
}
diff --git a/app/code/Magento/Sales/Model/Order.php b/app/code/Magento/Sales/Model/Order.php
index 345ff036a2be6..09f0e5f60c914 100644
--- a/app/code/Magento/Sales/Model/Order.php
+++ b/app/code/Magento/Sales/Model/Order.php
@@ -13,6 +13,7 @@
use Magento\Sales\Api\Data\OrderInterface;
use Magento\Sales\Api\Data\OrderStatusHistoryInterface;
use Magento\Sales\Model\Order\Payment;
+use Magento\Sales\Model\Order\ProductOption;
use Magento\Sales\Model\ResourceModel\Order\Address\Collection;
use Magento\Sales\Model\ResourceModel\Order\Creditmemo\Collection as CreditmemoCollection;
use Magento\Sales\Model\ResourceModel\Order\Invoice\Collection as InvoiceCollection;
@@ -279,6 +280,11 @@ class Order extends AbstractModel implements EntityInterface, OrderInterface
*/
private $localeResolver;
+ /**
+ * @var ProductOption
+ */
+ private $productOption;
+
/**
* @param \Magento\Framework\Model\Context $context
* @param \Magento\Framework\Registry $registry
@@ -308,6 +314,7 @@ class Order extends AbstractModel implements EntityInterface, OrderInterface
* @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection
* @param array $data
* @param ResolverInterface $localeResolver
+ * @param ProductOption|null $productOption
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
@@ -338,7 +345,8 @@ public function __construct(
\Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
\Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
array $data = [],
- ResolverInterface $localeResolver = null
+ ResolverInterface $localeResolver = null,
+ ProductOption $productOption = null
) {
$this->_storeManager = $storeManager;
$this->_orderConfig = $orderConfig;
@@ -361,6 +369,7 @@ public function __construct(
$this->salesOrderCollectionFactory = $salesOrderCollectionFactory;
$this->priceCurrency = $priceCurrency;
$this->localeResolver = $localeResolver ?: ObjectManager::getInstance()->get(ResolverInterface::class);
+ $this->productOption = $productOption ?: ObjectManager::getInstance()->get(ProductOption::class);
parent::__construct(
$context,
@@ -547,12 +556,7 @@ public function canCancel()
}
}
- $allRefunded = true;
- foreach ($this->getAllItems() as $orderItem) {
- $allRefunded = $allRefunded && ((float)$orderItem->getQtyRefunded() == (float)$orderItem->getQtyInvoiced());
- }
-
- if ($allInvoiced && !$allRefunded) {
+ if ($allInvoiced) {
return false;
}
@@ -1347,6 +1351,7 @@ public function getItemsCollection($filterByTypes = [], $nonChildrenOnly = false
if ($this->getId()) {
foreach ($collection as $item) {
$item->setOrder($this);
+ $this->productOption->add($item);
}
}
return $collection;
diff --git a/app/code/Magento/Sales/Model/Order/Address.php b/app/code/Magento/Sales/Model/Order/Address.php
index 77d8330a72550..083ec0089a5c0 100644
--- a/app/code/Magento/Sales/Model/Order/Address.php
+++ b/app/code/Magento/Sales/Model/Order/Address.php
@@ -173,8 +173,8 @@ protected function implodeStreetValue($value)
* Enforce format of the street field
*
* @param array|string $key
- * @param null $value
- * @return \Magento\Framework\DataObject
+ * @param array|string $value
+ * @return $this
*/
public function setData($key, $value = null)
{
@@ -508,7 +508,7 @@ public function getVatRequestSuccess()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setParentId($id)
{
@@ -516,7 +516,7 @@ public function setParentId($id)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setCustomerAddressId($id)
{
@@ -524,7 +524,7 @@ public function setCustomerAddressId($id)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setRegionId($id)
{
@@ -532,7 +532,7 @@ public function setRegionId($id)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setStreet($street)
{
@@ -540,7 +540,7 @@ public function setStreet($street)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setCustomerId($id)
{
@@ -548,7 +548,7 @@ public function setCustomerId($id)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setFax($fax)
{
@@ -556,7 +556,7 @@ public function setFax($fax)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setRegion($region)
{
@@ -564,7 +564,7 @@ public function setRegion($region)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setPostcode($postcode)
{
@@ -572,7 +572,7 @@ public function setPostcode($postcode)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setLastname($lastname)
{
@@ -580,7 +580,7 @@ public function setLastname($lastname)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setCity($city)
{
@@ -588,7 +588,7 @@ public function setCity($city)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setEmail($email)
{
@@ -596,7 +596,7 @@ public function setEmail($email)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setTelephone($telephone)
{
@@ -604,7 +604,7 @@ public function setTelephone($telephone)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setCountryId($id)
{
@@ -612,7 +612,7 @@ public function setCountryId($id)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setFirstname($firstname)
{
@@ -620,7 +620,7 @@ public function setFirstname($firstname)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setAddressType($addressType)
{
@@ -628,7 +628,7 @@ public function setAddressType($addressType)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setPrefix($prefix)
{
@@ -636,7 +636,7 @@ public function setPrefix($prefix)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setMiddlename($middlename)
{
@@ -644,7 +644,7 @@ public function setMiddlename($middlename)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setSuffix($suffix)
{
@@ -652,7 +652,7 @@ public function setSuffix($suffix)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setCompany($company)
{
@@ -660,7 +660,7 @@ public function setCompany($company)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setVatId($id)
{
@@ -668,7 +668,7 @@ public function setVatId($id)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setVatIsValid($vatIsValid)
{
@@ -676,7 +676,7 @@ public function setVatIsValid($vatIsValid)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setVatRequestId($id)
{
@@ -684,7 +684,7 @@ public function setVatRequestId($id)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setRegionCode($regionCode)
{
@@ -692,7 +692,7 @@ public function setRegionCode($regionCode)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setVatRequestDate($vatRequestDate)
{
@@ -700,7 +700,7 @@ public function setVatRequestDate($vatRequestDate)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setVatRequestSuccess($vatRequestSuccess)
{
@@ -708,7 +708,7 @@ public function setVatRequestSuccess($vatRequestSuccess)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*
* @return \Magento\Sales\Api\Data\OrderAddressExtensionInterface|null
*/
@@ -718,7 +718,7 @@ public function getExtensionAttributes()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*
* @param \Magento\Sales\Api\Data\OrderAddressExtensionInterface $extensionAttributes
* @return $this
diff --git a/app/code/Magento/Sales/Model/Order/Creditmemo/Item/Validation/CreationQuantityValidator.php b/app/code/Magento/Sales/Model/Order/Creditmemo/Item/Validation/CreationQuantityValidator.php
index 0c2adfff80a2b..93a4e701e0322 100644
--- a/app/code/Magento/Sales/Model/Order/Creditmemo/Item/Validation/CreationQuantityValidator.php
+++ b/app/code/Magento/Sales/Model/Order/Creditmemo/Item/Validation/CreationQuantityValidator.php
@@ -30,6 +30,7 @@ class CreationQuantityValidator implements ValidatorInterface
/**
* ItemCreationQuantityValidator constructor.
+ *
* @param OrderItemRepositoryInterface $orderItemRepository
* @param mixed $context
*/
@@ -53,6 +54,10 @@ public function validate($entity)
return [__('The creditmemo contains product item that is not part of the original order.')];
}
+ if ($orderItem->isDummy()) {
+ return [__('The creditmemo contains incorrect product items.')];
+ }
+
if (!$this->isQtyAvailable($orderItem, $entity->getQty())) {
return [__('The quantity to refund must not be greater than the unrefunded quantity.')];
}
@@ -61,6 +66,8 @@ public function validate($entity)
}
/**
+ * Check the quantity to refund is greater than the unrefunded quantity
+ *
* @param Item $orderItem
* @param int $qty
* @return bool
@@ -71,6 +78,8 @@ private function isQtyAvailable(Item $orderItem, $qty)
}
/**
+ * Check to see if Item is part of the order
+ *
* @param OrderItemInterface $orderItem
* @return bool
*/
diff --git a/app/code/Magento/Sales/Model/Order/Item.php b/app/code/Magento/Sales/Model/Order/Item.php
index 710c85f8773d6..a0eff45179ac8 100644
--- a/app/code/Magento/Sales/Model/Order/Item.php
+++ b/app/code/Magento/Sales/Model/Order/Item.php
@@ -232,7 +232,7 @@ public function getQtyToShip()
*/
public function getSimpleQtyToShip()
{
- $qty = $this->getQtyOrdered() - $this->getQtyShipped() - $this->getQtyCanceled();
+ $qty = $this->getQtyOrdered() - $this->getQtyShipped() - $this->getQtyRefunded() - $this->getQtyCanceled();
return max(round($qty, 8), 0);
}
diff --git a/app/code/Magento/Sales/Model/Order/ItemRepository.php b/app/code/Magento/Sales/Model/Order/ItemRepository.php
index 7916eb9db2b80..6e029ac468370 100644
--- a/app/code/Magento/Sales/Model/Order/ItemRepository.php
+++ b/app/code/Magento/Sales/Model/Order/ItemRepository.php
@@ -6,10 +6,8 @@
namespace Magento\Sales\Model\Order;
-use Magento\Catalog\Api\Data\ProductOptionExtensionFactory;
-use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface;
-use Magento\Catalog\Model\ProductOptionFactory;
use Magento\Catalog\Model\ProductOptionProcessorInterface;
+use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface;
use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\Framework\DataObject;
use Magento\Framework\DataObject\Factory as DataObjectFactory;
@@ -18,6 +16,7 @@
use Magento\Sales\Api\Data\OrderItemInterface;
use Magento\Sales\Api\Data\OrderItemSearchResultInterfaceFactory;
use Magento\Sales\Api\OrderItemRepositoryInterface;
+use Magento\Sales\Model\Order\ProductOption;
use Magento\Sales\Model\ResourceModel\Metadata;
/**
@@ -41,16 +40,6 @@ class ItemRepository implements OrderItemRepositoryInterface
*/
protected $searchResultFactory;
- /**
- * @var ProductOptionFactory
- */
- protected $productOptionFactory;
-
- /**
- * @var ProductOptionExtensionFactory
- */
- protected $extensionFactory;
-
/**
* @var ProductOptionProcessorInterface[]
*/
@@ -62,40 +51,41 @@ class ItemRepository implements OrderItemRepositoryInterface
protected $registry = [];
/**
- * @var \Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface
+ * @var CollectionProcessorInterface
*/
private $collectionProcessor;
/**
- * ItemRepository constructor.
+ * @var ProductOption
+ */
+ private $productOption;
+
+ /**
* @param DataObjectFactory $objectFactory
* @param Metadata $metadata
* @param OrderItemSearchResultInterfaceFactory $searchResultFactory
- * @param ProductOptionFactory $productOptionFactory
- * @param ProductOptionExtensionFactory $extensionFactory
+ * @param CollectionProcessorInterface $collectionProcessor
+ * @param ProductOption $productOption
* @param array $processorPool
- * @param CollectionProcessorInterface|null $collectionProcessor
*/
public function __construct(
DataObjectFactory $objectFactory,
Metadata $metadata,
OrderItemSearchResultInterfaceFactory $searchResultFactory,
- ProductOptionFactory $productOptionFactory,
- ProductOptionExtensionFactory $extensionFactory,
- array $processorPool = [],
- CollectionProcessorInterface $collectionProcessor = null
+ CollectionProcessorInterface $collectionProcessor,
+ ProductOption $productOption,
+ array $processorPool = []
) {
$this->objectFactory = $objectFactory;
$this->metadata = $metadata;
$this->searchResultFactory = $searchResultFactory;
- $this->productOptionFactory = $productOptionFactory;
- $this->extensionFactory = $extensionFactory;
+ $this->collectionProcessor = $collectionProcessor;
+ $this->productOption = $productOption;
$this->processorPool = $processorPool;
- $this->collectionProcessor = $collectionProcessor ?: $this->getCollectionProcessor();
}
/**
- * load entity
+ * Loads entity.
*
* @param int $id
* @return OrderItemInterface
@@ -116,7 +106,7 @@ public function get($id)
);
}
- $this->addProductOption($orderItem);
+ $this->productOption->add($orderItem);
$this->addParentItem($orderItem);
$this->registry[$id] = $orderItem;
}
@@ -137,7 +127,7 @@ public function getList(SearchCriteriaInterface $searchCriteria)
$this->collectionProcessor->process($searchCriteria, $searchResult);
/** @var OrderItemInterface $orderItem */
foreach ($searchResult->getItems() as $orderItem) {
- $this->addProductOption($orderItem);
+ $this->productOption->add($orderItem);
}
return $searchResult;
@@ -178,7 +168,9 @@ public function save(OrderItemInterface $entity)
{
if ($entity->getProductOption()) {
$request = $this->getBuyRequest($entity);
- $entity->setProductOptions(['info_buyRequest' => $request->toArray()]);
+ $productOptions = $entity->getProductOptions();
+ $productOptions['info_buyRequest'] = $request->toArray();
+ $entity->setProductOptions($productOptions);
}
$this->metadata->getMapper()->save($entity);
@@ -186,37 +178,6 @@ public function save(OrderItemInterface $entity)
return $this->registry[$entity->getEntityId()];
}
- /**
- * Add product option data
- *
- * @param OrderItemInterface $orderItem
- * @return $this
- */
- protected function addProductOption(OrderItemInterface $orderItem)
- {
- /** @var DataObject $request */
- $request = $orderItem->getBuyRequest();
-
- $productType = $orderItem->getProductType();
- if (isset($this->processorPool[$productType])
- && !$orderItem->getParentItemId()) {
- $data = $this->processorPool[$productType]->convertToProductOption($request);
- if ($data) {
- $this->setProductOption($orderItem, $data);
- }
- }
-
- if (isset($this->processorPool['custom_options'])
- && !$orderItem->getParentItemId()) {
- $data = $this->processorPool['custom_options']->convertToProductOption($request);
- if ($data) {
- $this->setProductOption($orderItem, $data);
- }
- }
-
- return $this;
- }
-
/**
* Set parent item.
*
@@ -228,33 +189,15 @@ private function addParentItem(OrderItemInterface $orderItem)
{
if ($parentId = $orderItem->getParentItemId()) {
$orderItem->setParentItem($this->get($parentId));
- }
- }
+ } else {
+ $orderCollection = $orderItem->getOrder()->getItemsCollection()->filterByParent($orderItem->getItemId());
- /**
- * Set product options data
- *
- * @param OrderItemInterface $orderItem
- * @param array $data
- * @return $this
- */
- protected function setProductOption(OrderItemInterface $orderItem, array $data)
- {
- $productOption = $orderItem->getProductOption();
- if (!$productOption) {
- $productOption = $this->productOptionFactory->create();
- $orderItem->setProductOption($productOption);
- }
-
- $extensionAttributes = $productOption->getExtensionAttributes();
- if (!$extensionAttributes) {
- $extensionAttributes = $this->extensionFactory->create();
- $productOption->setExtensionAttributes($extensionAttributes);
+ foreach ($orderCollection->getItems() as $item) {
+ if ($item->getParentItemId() === $orderItem->getItemId()) {
+ $item->setParentItem($orderItem);
+ }
+ }
}
-
- $extensionAttributes->setData(key($data), current($data));
-
- return $this;
}
/**
@@ -288,20 +231,4 @@ protected function getBuyRequest(OrderItemInterface $entity)
return $request;
}
-
- /**
- * Retrieve collection processor
- *
- * @deprecated 100.2.0
- * @return CollectionProcessorInterface
- */
- private function getCollectionProcessor()
- {
- if (!$this->collectionProcessor) {
- $this->collectionProcessor = \Magento\Framework\App\ObjectManager::getInstance()->get(
- \Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface::class
- );
- }
- return $this->collectionProcessor;
- }
}
diff --git a/app/code/Magento/Sales/Model/Order/Pdf/AbstractPdf.php b/app/code/Magento/Sales/Model/Order/Pdf/AbstractPdf.php
index 85e34f560bb7b..8cdc90972bbb0 100644
--- a/app/code/Magento/Sales/Model/Order/Pdf/AbstractPdf.php
+++ b/app/code/Magento/Sales/Model/Order/Pdf/AbstractPdf.php
@@ -363,6 +363,38 @@ protected function _calcAddressHeight($address)
return $y;
}
+ /**
+ * Detect an input string is Arabic
+ *
+ * @param string $subject
+ * @return bool
+ */
+ private function isArabic(string $subject): bool
+ {
+ return (preg_match('/\p{Arabic}/u', $subject) > 0);
+ }
+
+ /**
+ * Reverse text with Arabic characters
+ *
+ * @param string $string
+ * @return string
+ */
+ private function reverseArabicText($string)
+ {
+ $splitText = explode(' ', $string);
+ for ($i = 0; $i < count($splitText); $i++) {
+ if ($this->isArabic($splitText[$i])) {
+ for ($j = $i + 1; $j < count($splitText); $j++) {
+ $tmp = $this->string->strrev($splitText[$j]);
+ $splitText[$j] = $this->string->strrev($splitText[$i]);
+ $splitText[$i] = $tmp;
+ }
+ }
+ }
+ return implode(' ', $splitText);
+ }
+
/**
* Insert order to pdf page
*
@@ -474,7 +506,7 @@ protected function insertOrder(&$page, $obj, $putOrderId = true)
if ($value !== '') {
$text = [];
foreach ($this->string->split($value, 45, true, true) as $_value) {
- $text[] = $_value;
+ $text[] = ($this->isArabic($_value)) ? $this->reverseArabicText($_value) : $_value;
}
foreach ($text as $part) {
$page->drawText(strip_tags(ltrim($part)), 35, $this->y, 'UTF-8');
@@ -491,7 +523,7 @@ protected function insertOrder(&$page, $obj, $putOrderId = true)
if ($value !== '') {
$text = [];
foreach ($this->string->split($value, 45, true, true) as $_value) {
- $text[] = $_value;
+ $text[] = ($this->isArabic($_value)) ? $this->reverseArabicText($_value) : $_value;
}
foreach ($text as $part) {
$page->drawText(strip_tags(ltrim($part)), 285, $this->y, 'UTF-8');
diff --git a/app/code/Magento/Sales/Model/Order/ProductOption.php b/app/code/Magento/Sales/Model/Order/ProductOption.php
new file mode 100644
index 0000000000000..dc9ec42e27e60
--- /dev/null
+++ b/app/code/Magento/Sales/Model/Order/ProductOption.php
@@ -0,0 +1,103 @@
+productOptionFactory = $productOptionFactory;
+ $this->extensionFactory = $extensionFactory;
+ $this->processorPool = $processorPool;
+ }
+
+ /**
+ * Adds product option to the order item.
+ *
+ * @param OrderItemInterface $orderItem
+ */
+ public function add(OrderItemInterface $orderItem): void
+ {
+ /** @var DataObject $request */
+ $request = $orderItem->getBuyRequest();
+
+ $productType = $orderItem->getProductType();
+ if (isset($this->processorPool[$productType])
+ && !$orderItem->getParentItemId()) {
+ $data = $this->processorPool[$productType]->convertToProductOption($request);
+ if ($data) {
+ $this->setProductOption($orderItem, $data);
+ }
+ }
+
+ if (isset($this->processorPool['custom_options'])
+ && !$orderItem->getParentItemId()) {
+ $data = $this->processorPool['custom_options']->convertToProductOption($request);
+ if ($data) {
+ $this->setProductOption($orderItem, $data);
+ }
+ }
+ }
+
+ /**
+ * Sets product options data.
+ *
+ * @param OrderItemInterface $orderItem
+ * @param array $data
+ */
+ private function setProductOption(OrderItemInterface $orderItem, array $data): void
+ {
+ $productOption = $orderItem->getProductOption();
+ if (!$productOption) {
+ $productOption = $this->productOptionFactory->create();
+ $orderItem->setProductOption($productOption);
+ }
+
+ $extensionAttributes = $productOption->getExtensionAttributes();
+ if (!$extensionAttributes) {
+ $extensionAttributes = $this->extensionFactory->create();
+ $productOption->setExtensionAttributes($extensionAttributes);
+ }
+
+ $extensionAttributes->setData(key($data), current($data));
+ }
+}
diff --git a/app/code/Magento/Sales/Model/Order/Shipment.php b/app/code/Magento/Sales/Model/Order/Shipment.php
index ebedc869e14bd..cecee4283648d 100644
--- a/app/code/Magento/Sales/Model/Order/Shipment.php
+++ b/app/code/Magento/Sales/Model/Order/Shipment.php
@@ -354,7 +354,15 @@ public function addItem(\Magento\Sales\Model\Order\Shipment\Item $item)
public function getTracksCollection()
{
if ($this->tracksCollection === null) {
- $this->tracksCollection = $this->_trackCollectionFactory->create()->setShipmentFilter($this->getId());
+ $this->tracksCollection = $this->_trackCollectionFactory->create();
+
+ if ($this->getId()) {
+ $this->tracksCollection->setShipmentFilter($this->getId());
+
+ foreach ($this->tracksCollection as $item) {
+ $item->setShipment($this);
+ }
+ }
}
return $this->tracksCollection;
@@ -400,19 +408,20 @@ public function getTrackById($trackId)
*/
public function addTrack(\Magento\Sales\Model\Order\Shipment\Track $track)
{
- $track->setShipment(
- $this
- )->setParentId(
- $this->getId()
- )->setOrderId(
- $this->getOrderId()
- )->setStoreId(
- $this->getStoreId()
- );
+ $track->setShipment($this)
+ ->setParentId($this->getId())
+ ->setOrderId($this->getOrderId())
+ ->setStoreId($this->getStoreId());
+
if (!$track->getId()) {
$this->getTracksCollection()->addItem($track);
}
+ $tracks = $this->getTracks();
+ // as it's a new track entity, the collection doesn't contain it
+ $tracks[] = $track;
+ $this->setTracks($tracks);
+
/**
* Track saving is implemented in _afterSave()
* This enforces \Magento\Framework\Model\AbstractModel::save() not to skip _afterSave()
@@ -582,14 +591,15 @@ public function setItems($items)
/**
* Returns tracks
*
- * @return \Magento\Sales\Api\Data\ShipmentTrackInterface[]
+ * @return \Magento\Sales\Api\Data\ShipmentTrackInterface[]|null
*/
public function getTracks()
{
+ if (!$this->getId()) {
+ return $this->getData(ShipmentInterface::TRACKS);
+ }
+
if ($this->getData(ShipmentInterface::TRACKS) === null) {
- foreach ($this->getTracksCollection() as $item) {
- $item->setShipment($this);
- }
$this->setData(ShipmentInterface::TRACKS, $this->getTracksCollection()->getItems());
}
return $this->getData(ShipmentInterface::TRACKS);
diff --git a/app/code/Magento/Sales/Model/ResourceModel/Order/Shipment/Relation.php b/app/code/Magento/Sales/Model/ResourceModel/Order/Shipment/Relation.php
index 9c8671d02c578..5851b2d936139 100644
--- a/app/code/Magento/Sales/Model/ResourceModel/Order/Shipment/Relation.php
+++ b/app/code/Magento/Sales/Model/ResourceModel/Order/Shipment/Relation.php
@@ -62,8 +62,8 @@ public function processRelation(\Magento\Framework\Model\AbstractModel $object)
$this->shipmentItemResource->save($item);
}
}
- if (null !== $object->getTracksCollection()) {
- foreach ($object->getTracksCollection() as $track) {
+ if (null !== $object->getTracks()) {
+ foreach ($object->getTracks() as $track) {
$track->setParentId($object->getId());
$this->shipmentTrackResource->save($track);
}
diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml
index 15aff7c751a11..905cb08a401e1 100644
--- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml
+++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml
@@ -38,4 +38,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml
index 53dc52ca58fa7..841ab6a487010 100644
--- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml
+++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml
@@ -94,24 +94,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -287,6 +269,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoicePaymentShippingSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoicePaymentShippingSection.xml
index 918a8e814b555..8d7c64733972e 100644
--- a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoicePaymentShippingSection.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoicePaymentShippingSection.xml
@@ -15,5 +15,7 @@
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml
index 4ab1e3327960c..11d973d1e19de 100644
--- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml
@@ -13,5 +13,6 @@
+
\ No newline at end of file
diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormItemsOrderedSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormItemsOrderedSection.xml
new file mode 100644
index 0000000000000..11673f1f0fe26
--- /dev/null
+++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormItemsOrderedSection.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml
index 4350ffeb03373..60d4c53418dc8 100644
--- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml
@@ -15,4 +15,4 @@
-
\ No newline at end of file
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderStoreScopeTreeSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderStoreScopeTreeSection.xml
index 050e1ba8b2df4..cbe17499319f9 100644
--- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderStoreScopeTreeSection.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderStoreScopeTreeSection.xml
@@ -11,5 +11,6 @@
diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml
index 7ece18fb863b7..53a6cbffcdac6 100644
--- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml
@@ -29,5 +29,7 @@
+
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Section/OrdersGridSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/OrdersGridSection.xml
index 8d99bf4872d0a..717022322698f 100644
--- a/app/code/Magento/Sales/Test/Mftf/Section/OrdersGridSection.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Section/OrdersGridSection.xml
@@ -18,7 +18,7 @@
-
+
@@ -29,5 +29,6 @@
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml
deleted file mode 100644
index e4fd894f608c5..0000000000000
--- a/app/code/Magento/Sales/Test/Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml
+++ /dev/null
@@ -1,168 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml
index 0032a6c987e82..24266b5bcfe9f 100644
--- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml
@@ -61,6 +61,7 @@
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitConfigurableProductOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitConfigurableProductOrderTest.xml
index e32e6b9e6ec5d..ff1e27a2efa08 100644
--- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitConfigurableProductOrderTest.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitConfigurableProductOrderTest.xml
@@ -1,129 +1,132 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml
index 60df3f27fd65b..08e859b11d1bb 100644
--- a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml
@@ -45,6 +45,8 @@
+
+
diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/ItemRepositoryTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/ItemRepositoryTest.php
deleted file mode 100644
index 8be2c3c8612d7..0000000000000
--- a/app/code/Magento/Sales/Test/Unit/Model/Order/ItemRepositoryTest.php
+++ /dev/null
@@ -1,366 +0,0 @@
-objectFactory = $this->getMockBuilder(\Magento\Framework\DataObject\Factory::class)
- ->disableOriginalConstructor()
- ->setMethods(['create'])
- ->getMock();
-
- $this->metadata = $this->getMockBuilder(\Magento\Sales\Model\ResourceModel\Metadata::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- $this->searchResultFactory = $this->getMockBuilder(
- \Magento\Sales\Api\Data\OrderItemSearchResultInterfaceFactory::class
- )
- ->disableOriginalConstructor()
- ->setMethods(['create'])
- ->getMock();
-
- $this->productOptionFactory = $this->getMockBuilder(\Magento\Catalog\Model\ProductOptionFactory::class)
- ->setMethods([
- 'create',
- ])
- ->disableOriginalConstructor()
- ->getMock();
-
- $this->collectionProcessor = $this->createMock(
- \Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface::class
- );
-
- $this->extensionFactory = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductOptionExtensionFactory::class)
- ->setMethods([
- 'create',
- ])
- ->disableOriginalConstructor()
- ->getMock();
- }
-
- /**
- * @expectedException \Magento\Framework\Exception\InputException
- * @expectedExceptionMessage An ID is needed. Set the ID and try again.
- */
- public function testGetWithNoId()
- {
- $model = new ItemRepository(
- $this->objectFactory,
- $this->metadata,
- $this->searchResultFactory,
- $this->productOptionFactory,
- $this->extensionFactory,
- [],
- $this->collectionProcessor
- );
-
- $model->get(null);
- }
-
- /**
- * @expectedException \Magento\Framework\Exception\NoSuchEntityException
- * @expectedExceptionMessage The entity that was requested doesn't exist. Verify the entity and try again.
- */
- public function testGetEmptyEntity()
- {
- $orderItemId = 1;
-
- $orderItemMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Item::class)
- ->disableOriginalConstructor()
- ->getMock();
- $orderItemMock->expects($this->once())
- ->method('load')
- ->with($orderItemId)
- ->willReturn($orderItemMock);
- $orderItemMock->expects($this->once())
- ->method('getItemId')
- ->willReturn(null);
-
- $this->metadata->expects($this->once())
- ->method('getNewInstance')
- ->willReturn($orderItemMock);
-
- $model = new ItemRepository(
- $this->objectFactory,
- $this->metadata,
- $this->searchResultFactory,
- $this->productOptionFactory,
- $this->extensionFactory,
- [],
- $this->collectionProcessor
- );
-
- $model->get($orderItemId);
- }
-
- public function testGet()
- {
- $orderItemId = 1;
- $productType = 'configurable';
-
- $this->productOptionData = ['option1' => 'value1'];
-
- $this->getProductOptionExtensionMock();
- $productOption = $this->getProductOptionMock();
- $orderItemMock = $this->getOrderMock($productType, $productOption);
-
- $orderItemMock->expects($this->once())
- ->method('load')
- ->with($orderItemId)
- ->willReturn($orderItemMock);
- $orderItemMock->expects($this->once())
- ->method('getItemId')
- ->willReturn($orderItemId);
-
- $this->metadata->expects($this->once())
- ->method('getNewInstance')
- ->willReturn($orderItemMock);
-
- $model = $this->getModel($orderItemMock, $productType);
- $this->assertSame($orderItemMock, $model->get($orderItemId));
-
- // Assert already registered
- $this->assertSame($orderItemMock, $model->get($orderItemId));
- }
-
- public function testGetList()
- {
- $productType = 'configurable';
- $this->productOptionData = ['option1' => 'value1'];
- $searchCriteriaMock = $this->getMockBuilder(\Magento\Framework\Api\SearchCriteria::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->getProductOptionExtensionMock();
- $productOption = $this->getProductOptionMock();
- $orderItemMock = $this->getOrderMock($productType, $productOption);
-
- $searchResultMock = $this->getMockBuilder(\Magento\Sales\Model\ResourceModel\Order\Item\Collection::class)
- ->disableOriginalConstructor()
- ->getMock();
- $searchResultMock->expects($this->once())
- ->method('getItems')
- ->willReturn([$orderItemMock]);
-
- $this->searchResultFactory->expects($this->once())
- ->method('create')
- ->willReturn($searchResultMock);
-
- $model = $this->getModel($orderItemMock, $productType);
- $this->assertSame($searchResultMock, $model->getList($searchCriteriaMock));
- }
-
- public function testDeleteById()
- {
- $orderItemId = 1;
- $productType = 'configurable';
-
- $requestMock = $this->getMockBuilder(\Magento\Framework\DataObject::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- $orderItemMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Item::class)
- ->disableOriginalConstructor()
- ->getMock();
- $orderItemMock->expects($this->once())
- ->method('load')
- ->with($orderItemId)
- ->willReturn($orderItemMock);
- $orderItemMock->expects($this->once())
- ->method('getItemId')
- ->willReturn($orderItemId);
- $orderItemMock->expects($this->once())
- ->method('getProductType')
- ->willReturn($productType);
- $orderItemMock->expects($this->once())
- ->method('getBuyRequest')
- ->willReturn($requestMock);
-
- $orderItemResourceMock = $this->getMockBuilder(\Magento\Framework\Model\ResourceModel\Db\AbstractDb::class)
- ->disableOriginalConstructor()
- ->getMock();
- $orderItemResourceMock->expects($this->once())
- ->method('delete')
- ->with($orderItemMock)
- ->willReturnSelf();
-
- $this->metadata->expects($this->once())
- ->method('getNewInstance')
- ->willReturn($orderItemMock);
- $this->metadata->expects($this->exactly(1))
- ->method('getMapper')
- ->willReturn($orderItemResourceMock);
-
- $model = $this->getModel($orderItemMock, $productType);
- $this->assertTrue($model->deleteById($orderItemId));
- }
-
- /**
- * @param \PHPUnit_Framework_MockObject_MockObject $orderItemMock
- * @param string $productType
- * @param array $data
- * @return ItemRepository
- */
- protected function getModel(
- \PHPUnit_Framework_MockObject_MockObject $orderItemMock,
- $productType,
- array $data = []
- ) {
- $requestMock = $this->getMockBuilder(\Magento\Framework\DataObject::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- $requestUpdateMock = $this->getMockBuilder(\Magento\Framework\DataObject::class)
- ->disableOriginalConstructor()
- ->getMock();
- $requestUpdateMock->expects($this->any())
- ->method('getData')
- ->willReturn($data);
-
- $this->productOptionProcessorMock = $this->getMockBuilder(
- \Magento\Catalog\Model\ProductOptionProcessorInterface::class
- )
- ->getMockForAbstractClass();
- $this->productOptionProcessorMock->expects($this->any())
- ->method('convertToProductOption')
- ->with($requestMock)
- ->willReturn($this->productOptionData);
- $this->productOptionProcessorMock->expects($this->any())
- ->method('convertToBuyRequest')
- ->with($orderItemMock)
- ->willReturn($requestUpdateMock);
-
- $model = new ItemRepository(
- $this->objectFactory,
- $this->metadata,
- $this->searchResultFactory,
- $this->productOptionFactory,
- $this->extensionFactory,
- [
- $productType => $this->productOptionProcessorMock,
- 'custom_options' => $this->productOptionProcessorMock
- ],
- $this->collectionProcessor
- );
- return $model;
- }
-
- /**
- * @param string $productType
- * @param \PHPUnit_Framework_MockObject_MockObject $productOption
- * @return \PHPUnit_Framework_MockObject_MockObject
- */
- protected function getOrderMock($productType, $productOption)
- {
- $requestMock = $this->getMockBuilder(\Magento\Framework\DataObject::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- $orderItemMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Item::class)
- ->disableOriginalConstructor()
- ->getMock();
- $orderItemMock->expects($this->once())
- ->method('getProductType')
- ->willReturn($productType);
- $orderItemMock->expects($this->once())
- ->method('getBuyRequest')
- ->willReturn($requestMock);
- $orderItemMock->expects($this->any())
- ->method('getProductOption')
- ->willReturn(null);
- $orderItemMock->expects($this->any())
- ->method('setProductOption')
- ->with($productOption)
- ->willReturnSelf();
-
- return $orderItemMock;
- }
-
- /**
- * @return \PHPUnit_Framework_MockObject_MockObject
- */
- protected function getProductOptionMock()
- {
- $productOption = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductOptionInterface::class)
- ->getMockForAbstractClass();
- $productOption->expects($this->any())
- ->method('getExtensionAttributes')
- ->willReturn(null);
-
- $this->productOptionFactory->expects($this->any())
- ->method('create')
- ->willReturn($productOption);
-
- return $productOption;
- }
-
- protected function getProductOptionExtensionMock()
- {
- $productOptionExtension = $this->getMockBuilder(
- \Magento\Catalog\Api\Data\ProductOptionExtensionInterface::class
- )
- ->setMethods([
- 'setData',
- ])
- ->getMockForAbstractClass();
- $productOptionExtension->expects($this->any())
- ->method('setData')
- ->with(key($this->productOptionData), current($this->productOptionData))
- ->willReturnSelf();
-
- $this->extensionFactory->expects($this->any())
- ->method('create')
- ->willReturn($productOptionExtension);
- }
-}
diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/ItemTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/ItemTest.php
index 3d4a5785d6254..76bfd62a7b889 100644
--- a/app/code/Magento/Sales/Test/Unit/Model/Order/ItemTest.php
+++ b/app/code/Magento/Sales/Test/Unit/Model/Order/ItemTest.php
@@ -295,14 +295,14 @@ public function getItemQtyVariants()
'qty_ordered' => 12, 'qty_invoiced' => 5, 'qty_refunded' => 5, 'qty_shipped' => 0,
'qty_canceled' => 0,
],
- 'expectedResult' => ['to_ship' => 12.0, 'to_invoice' => 7.0]
+ 'expectedResult' => ['to_ship' => 7.0, 'to_invoice' => 7.0]
],
'partially_refunded' => [
'options' => [
'qty_ordered' => 12, 'qty_invoiced' => 12, 'qty_refunded' => 5, 'qty_shipped' => 0,
'qty_canceled' => 0,
],
- 'expectedResult' => ['to_ship' => 12.0, 'to_invoice' => 0.0]
+ 'expectedResult' => ['to_ship' => 7.0, 'to_invoice' => 0.0]
],
'partially_shipped' => [
'options' => [
@@ -316,7 +316,7 @@ public function getItemQtyVariants()
'qty_ordered' => 12, 'qty_invoiced' => 12, 'qty_refunded' => 5, 'qty_shipped' => 4,
'qty_canceled' => 0
],
- 'expectedResult' => ['to_ship' => 8.0, 'to_invoice' => 0.0]
+ 'expectedResult' => ['to_ship' => 3.0, 'to_invoice' => 0.0]
],
'complete' => [
'options' => [
@@ -337,7 +337,7 @@ public function getItemQtyVariants()
'qty_ordered' => 4.4, 'qty_invoiced' => 0.4, 'qty_refunded' => 0.4, 'qty_shipped' => 4,
'qty_canceled' => 0,
],
- 'expectedResult' => ['to_ship' => 0.4, 'to_invoice' => 4.0]
+ 'expectedResult' => ['to_ship' => 0.0, 'to_invoice' => 4.0]
],
'completely_invoiced_using_decimals' => [
'options' => [
diff --git a/app/code/Magento/Sales/Test/Unit/Model/OrderTest.php b/app/code/Magento/Sales/Test/Unit/Model/OrderTest.php
index 49c86b5294f9e..f724136eb5154 100644
--- a/app/code/Magento/Sales/Test/Unit/Model/OrderTest.php
+++ b/app/code/Magento/Sales/Test/Unit/Model/OrderTest.php
@@ -117,8 +117,6 @@ protected function setUp()
'getQuoteItemId',
'getLockedDoInvoice',
'getProductId',
- 'getQtyRefunded',
- 'getQtyInvoiced',
]);
$this->salesOrderCollectionMock = $this->getMockBuilder(
\Magento\Sales\Model\ResourceModel\Order\Collection::class
@@ -638,163 +636,6 @@ public function testCanCancelAllInvoiced()
$this->item->expects($this->any())
->method('getQtyToInvoice')
->willReturn(0);
- $this->item->expects($this->any())
- ->method('getQtyRefunded')
- ->willReturn(0);
- $this->item->expects($this->any())
- ->method('getQtyInvoiced')
- ->willReturn(1);
-
- $this->assertFalse($this->order->canCancel());
- }
-
- public function testCanCancelAllRefunded()
- {
- $paymentMock = $this->getMockBuilder(\Magento\Sales\Model\ResourceModel\Order\Payment::class)
- ->disableOriginalConstructor()
- ->setMethods(['isDeleted', 'canReviewPayment', 'canFetchTransactionInfo', '__wakeUp'])
- ->getMock();
- $paymentMock->expects($this->any())
- ->method('canReviewPayment')
- ->will($this->returnValue(false));
- $paymentMock->expects($this->any())
- ->method('canFetchTransactionInfo')
- ->will($this->returnValue(false));
- $collectionMock = $this->createPartialMock(
- \Magento\Sales\Model\ResourceModel\Order\Item\Collection::class,
- ['getItems', 'setOrderFilter']
- );
- $this->orderItemCollectionFactoryMock->expects($this->any())
- ->method('create')
- ->will($this->returnValue($collectionMock));
- $collectionMock->expects($this->any())
- ->method('setOrderFilter')
- ->willReturnSelf();
- $this->preparePaymentMock($paymentMock);
-
- $this->prepareItemMock(0);
-
- $this->order->setActionFlag(\Magento\Sales\Model\Order::ACTION_FLAG_UNHOLD, false);
- $this->order->setState(\Magento\Sales\Model\Order::STATE_NEW);
-
- $this->item->expects($this->any())
- ->method('isDeleted')
- ->willReturn(false);
- $this->item->expects($this->any())
- ->method('getQtyToInvoice')
- ->willReturn(0);
- $this->item->expects($this->any())
- ->method('getQtyRefunded')
- ->willReturn(10);
- $this->item->expects($this->any())
- ->method('getQtyInvoiced')
- ->willReturn(10);
-
- $this->assertTrue($this->order->canCancel());
- }
-
- /**
- * Test that order can be canceled if some items were partially invoiced with certain qty
- * and then refunded for this qty.
- * Sample:
- * - ordered qty = 20
- * - invoiced = 10
- * - refunded = 10
- */
- public function testCanCancelPartiallyInvoicedAndRefunded()
- {
- $paymentMock = $this->getMockBuilder(\Magento\Sales\Model\ResourceModel\Order\Payment::class)
- ->disableOriginalConstructor()
- ->setMethods(['isDeleted', 'canReviewPayment', 'canFetchTransactionInfo', '__wakeUp'])
- ->getMock();
- $paymentMock->expects($this->any())
- ->method('canReviewPayment')
- ->will($this->returnValue(false));
- $paymentMock->expects($this->any())
- ->method('canFetchTransactionInfo')
- ->will($this->returnValue(false));
- $collectionMock = $this->createPartialMock(
- \Magento\Sales\Model\ResourceModel\Order\Item\Collection::class,
- ['getItems', 'setOrderFilter']
- );
- $this->orderItemCollectionFactoryMock->expects($this->any())
- ->method('create')
- ->will($this->returnValue($collectionMock));
- $collectionMock->expects($this->any())
- ->method('setOrderFilter')
- ->willReturnSelf();
- $this->preparePaymentMock($paymentMock);
-
- $this->prepareItemMock(0);
-
- $this->order->setActionFlag(\Magento\Sales\Model\Order::ACTION_FLAG_UNHOLD, false);
- $this->order->setState(\Magento\Sales\Model\Order::STATE_NEW);
-
- $this->item->expects($this->any())
- ->method('isDeleted')
- ->willReturn(false);
- $this->item->expects($this->any())
- ->method('getQtyToInvoice')
- ->willReturn(10);
- $this->item->expects($this->any())
- ->method('getQtyRefunded')
- ->willReturn(10);
- $this->item->expects($this->any())
- ->method('getQtyInvoiced')
- ->willReturn(10);
-
- $this->assertTrue($this->order->canCancel());
- }
-
- /**
- * Test that order CAN NOT be canceled if some items were partially invoiced with certain qty
- * and then refunded for less than that qty.
- * Sample:
- * - ordered qty = 10
- * - invoiced = 10
- * - refunded = 5
- */
- public function testCanCancelPartiallyInvoicedAndNotFullyRefunded()
- {
- $paymentMock = $this->getMockBuilder(\Magento\Sales\Model\ResourceModel\Order\Payment::class)
- ->disableOriginalConstructor()
- ->setMethods(['isDeleted', 'canReviewPayment', 'canFetchTransactionInfo', '__wakeUp'])
- ->getMock();
- $paymentMock->expects($this->any())
- ->method('canReviewPayment')
- ->will($this->returnValue(false));
- $paymentMock->expects($this->any())
- ->method('canFetchTransactionInfo')
- ->will($this->returnValue(false));
- $collectionMock = $this->createPartialMock(
- \Magento\Sales\Model\ResourceModel\Order\Item\Collection::class,
- ['getItems', 'setOrderFilter']
- );
- $this->orderItemCollectionFactoryMock->expects($this->any())
- ->method('create')
- ->will($this->returnValue($collectionMock));
- $collectionMock->expects($this->any())
- ->method('setOrderFilter')
- ->willReturnSelf();
- $this->preparePaymentMock($paymentMock);
-
- $this->prepareItemMock(0);
-
- $this->order->setActionFlag(\Magento\Sales\Model\Order::ACTION_FLAG_UNHOLD, false);
- $this->order->setState(\Magento\Sales\Model\Order::STATE_NEW);
-
- $this->item->expects($this->any())
- ->method('isDeleted')
- ->willReturn(false);
- $this->item->expects($this->any())
- ->method('getQtyToInvoice')
- ->willReturn(0);
- $this->item->expects($this->any())
- ->method('getQtyRefunded')
- ->willReturn(5);
- $this->item->expects($this->any())
- ->method('getQtyInvoiced')
- ->willReturn(10);
$this->assertFalse($this->order->canCancel());
}
@@ -1258,9 +1099,6 @@ public function testGetCreatedAtFormattedUsesCorrectLocale()
$this->order->getCreatedAtFormatted(\IntlDateFormatter::SHORT);
}
- /**
- * @return array
- */
public function notInvoicingStatesProvider()
{
return [
@@ -1270,9 +1108,6 @@ public function notInvoicingStatesProvider()
];
}
- /**
- * @return array
- */
public function canNotCreditMemoStatesProvider()
{
return [
diff --git a/app/code/Magento/Sales/Test/Unit/Model/ResourceModel/Order/Shipment/RelationTest.php b/app/code/Magento/Sales/Test/Unit/Model/ResourceModel/Order/Shipment/RelationTest.php
index a7a615fb0f508..530306d77d3ed 100644
--- a/app/code/Magento/Sales/Test/Unit/Model/ResourceModel/Order/Shipment/RelationTest.php
+++ b/app/code/Magento/Sales/Test/Unit/Model/ResourceModel/Order/Shipment/RelationTest.php
@@ -3,145 +3,125 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
namespace Magento\Sales\Test\Unit\Model\ResourceModel\Order\Shipment;
+use Magento\Sales\Model\Order\Shipment;
+use Magento\Sales\Model\Order\Shipment\Comment as CommentEntity;
+use Magento\Sales\Model\Order\Shipment\Item as ItemEntity;
+use Magento\Sales\Model\Order\Shipment\Track as TrackEntity;
+use Magento\Sales\Model\ResourceModel\Order\Shipment\Comment;
+use Magento\Sales\Model\ResourceModel\Order\Shipment\Item;
+use Magento\Sales\Model\ResourceModel\Order\Shipment\Relation;
+use Magento\Sales\Model\ResourceModel\Order\Shipment\Track;
+use PHPUnit\Framework\MockObject\MockObject;
+
/**
* Class RelationTest
*/
class RelationTest extends \PHPUnit\Framework\TestCase
{
/**
- * @var \Magento\Sales\Model\ResourceModel\Order\Shipment\Relation
+ * @var Relation
*/
- protected $relationProcessor;
+ private $relationProcessor;
/**
- * @var \Magento\Sales\Model\ResourceModel\Order\Shipment\Item|\PHPUnit_Framework_MockObject_MockObject
+ * @var Item|MockObject
*/
- protected $itemResourceMock;
+ private $itemResource;
/**
- * @var \Magento\Sales\Model\ResourceModel\Order\Shipment\Track|\PHPUnit_Framework_MockObject_MockObject
+ * @var Track|MockObject
*/
- protected $trackResourceMock;
+ private $trackResource;
/**
- * @var \Magento\Sales\Model\ResourceModel\Order\Shipment\Comment|\PHPUnit_Framework_MockObject_MockObject
+ * @var Comment|MockObject
*/
- protected $commentResourceMock;
+ private $commentResource;
/**
- * @var \Magento\Sales\Model\Order\Shipment\Comment|\PHPUnit_Framework_MockObject_MockObject
+ * @var CommentEntity|MockObject
*/
- protected $commentMock;
+ private $comment;
/**
- * @var \Magento\Sales\Model\Order\Shipment\Track|\PHPUnit_Framework_MockObject_MockObject
+ * @var TrackEntity|MockObject
*/
- protected $trackMock;
+ private $track;
/**
- * @var \Magento\Sales\Model\Order\Shipment|\PHPUnit_Framework_MockObject_MockObject
+ * @var Shipment|MockObject
*/
- protected $shipmentMock;
+ private $shipment;
/**
- * @var \Magento\Sales\Model\Order\Shipment\Item|\PHPUnit_Framework_MockObject_MockObject
+ * @var ItemEntity|MockObject
*/
- protected $itemMock;
+ private $item;
- protected function setUp()
+ /**
+ * @inheritdoc
+ */
+ protected function setUp(): void
{
- $this->itemResourceMock = $this->getMockBuilder(\Magento\Sales\Model\ResourceModel\Order\Shipment\Item::class)
+ $this->itemResource = $this->getMockBuilder(Item::class)
->disableOriginalConstructor()
- ->setMethods(
- [
- 'save'
- ]
- )
->getMock();
- $this->commentResourceMock = $this->getMockBuilder(
- \Magento\Sales\Model\ResourceModel\Order\Shipment\Comment::class
- )
+ $this->commentResource = $this->getMockBuilder(Comment::class)
->disableOriginalConstructor()
- ->setMethods(
- [
- 'save'
- ]
- )
->getMock();
- $this->trackResourceMock = $this->getMockBuilder(\Magento\Sales\Model\ResourceModel\Order\Shipment\Track::class)
+ $this->trackResource = $this->getMockBuilder(Track::class)
->disableOriginalConstructor()
- ->setMethods(
- [
- 'save'
- ]
- )
->getMock();
- $this->shipmentMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Shipment::class)
+ $this->shipment = $this->getMockBuilder(Shipment::class)
->disableOriginalConstructor()
- ->setMethods(
- [
- 'getId',
- 'getItems',
- 'getTracks',
- 'getComments',
- 'getTracksCollection',
- ]
- )
->getMock();
- $this->itemMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Item::class)
+ $this->item = $this->getMockBuilder(ItemEntity::class)
->disableOriginalConstructor()
- ->setMethods(
- [
- 'setParentId'
- ]
- )
->getMock();
- $this->trackMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Shipment\Track::class)
+ $this->track = $this->getMockBuilder(TrackEntity::class)
->disableOriginalConstructor()
->getMock();
- $this->commentMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Shipment::class)
+ $this->comment = $this->getMockBuilder(Shipment::class)
->disableOriginalConstructor()
->getMock();
- $this->relationProcessor = new \Magento\Sales\Model\ResourceModel\Order\Shipment\Relation(
- $this->itemResourceMock,
- $this->trackResourceMock,
- $this->commentResourceMock
+ $this->relationProcessor = new Relation(
+ $this->itemResource,
+ $this->trackResource,
+ $this->commentResource
);
}
- public function testProcessRelations()
+ /**
+ * Checks saving shipment relations.
+ *
+ * @throws \Exception
+ */
+ public function testProcessRelations(): void
{
- $this->shipmentMock->expects($this->exactly(3))
- ->method('getId')
+ $this->shipment->method('getId')
->willReturn('shipment-id-value');
- $this->shipmentMock->expects($this->exactly(2))
- ->method('getItems')
- ->willReturn([$this->itemMock]);
- $this->shipmentMock->expects($this->exactly(2))
- ->method('getComments')
- ->willReturn([$this->commentMock]);
- $this->shipmentMock->expects($this->exactly(2))
- ->method('getTracksCollection')
- ->willReturn([$this->trackMock]);
- $this->itemMock->expects($this->once())
- ->method('setParentId')
+ $this->shipment->method('getItems')
+ ->willReturn([$this->item]);
+ $this->shipment->method('getComments')
+ ->willReturn([$this->comment]);
+ $this->shipment->method('getTracks')
+ ->willReturn([$this->track]);
+ $this->item->method('setParentId')
->with('shipment-id-value')
->willReturnSelf();
- $this->itemResourceMock->expects($this->once())
- ->method('save')
- ->with($this->itemMock)
+ $this->itemResource->method('save')
+ ->with($this->item)
->willReturnSelf();
- $this->commentResourceMock->expects($this->once())
- ->method('save')
- ->with($this->commentMock)
+ $this->commentResource->method('save')
+ ->with($this->comment)
->willReturnSelf();
- $this->trackResourceMock->expects($this->once())
- ->method('save')
- ->with($this->trackMock)
+ $this->trackResource->method('save')
+ ->with($this->track)
->willReturnSelf();
- $this->relationProcessor->processRelation($this->shipmentMock);
+ $this->relationProcessor->processRelation($this->shipment);
}
}
diff --git a/app/code/Magento/Sales/etc/db_schema.xml b/app/code/Magento/Sales/etc/db_schema.xml
index 1310204dcc8f3..ced999bb86019 100644
--- a/app/code/Magento/Sales/etc/db_schema.xml
+++ b/app/code/Magento/Sales/etc/db_schema.xml
@@ -9,7 +9,7 @@
xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
+ comment="Entity Id"/>
@@ -297,13 +297,13 @@
+ comment="Entity Id"/>
+ comment="Store Id"/>
+ comment="Customer Id"/>
+ comment="Entity Id"/>
+ comment="Parent Id"/>
+ comment="Customer Address Id"/>
+ comment="Quote Address Id"/>
+ comment="Region Id"/>
+ comment="Customer Id"/>
@@ -431,9 +431,9 @@
+ comment="Entity Id"/>
+ comment="Parent Id"/>
+ comment="Entity Id"/>
+ comment="Parent Id"/>
+ comment="Entity Id"/>
+ comment="Store Id"/>
+ comment="Order Id"/>
+ comment="Customer Id"/>
+ comment="Shipping Address Id"/>
+ comment="Billing Address Id"/>
@@ -762,14 +762,14 @@
+ comment="Entity Id"/>
+ comment="Store Id"/>
-
+
+ comment="Entity Id"/>
+ comment="Parent Id"/>
+ comment="Product Id"/>
+ comment="Order Item Id"/>
@@ -867,14 +867,14 @@
+ comment="Entity Id"/>
+ comment="Parent Id"/>
+ comment="Order Id"/>
@@ -901,9 +901,9 @@
+ comment="Entity Id"/>
+ comment="Parent Id"/>
+ comment="Entity Id"/>
+ comment="Store Id"/>
+ comment="Entity Id"/>
+ comment="Store Id"/>
+ comment="Order Id"/>
@@ -1136,9 +1136,9 @@
+ comment="Entity Id"/>
+ comment="Parent Id"/>
+ comment="Entity Id"/>
+ comment="Parent Id"/>
+ comment="Entity Id"/>
+ comment="Store Id"/>
+ comment="Entity Id"/>
+ comment="Order Id"/>
@@ -1366,7 +1366,7 @@
comment="Base Grand Total"/>
+ comment="Store Id"/>
@@ -1438,9 +1438,9 @@
+ comment="Entity Id"/>
+ comment="Parent Id"/>
+ comment="Product Id"/>
+ comment="Order Item Id"/>
@@ -1494,9 +1494,9 @@
+ comment="Entity Id"/>
+ comment="Parent Id"/>
getItems(); ?>
+
getParentItem()) continue; ?>
-
+
= $block->getItemHtml($item) ?>
helper('Magento\GiftMessage\Helper\Message')->isMessagesAllowed('order_item', $item) && $item->getGiftMessageId()): ?>
helper('Magento\GiftMessage\Helper\Message')->getGiftMessageForEntity($item); ?>
@@ -62,8 +63,8 @@
-
+
isPagerDisplayed()): ?>
diff --git a/app/code/Magento/Sales/view/frontend/templates/reorder/sidebar.phtml b/app/code/Magento/Sales/view/frontend/templates/reorder/sidebar.phtml
index 5ecf1ebe893bc..9b3633fde60b4 100644
--- a/app/code/Magento/Sales/view/frontend/templates/reorder/sidebar.phtml
+++ b/app/code/Magento/Sales/view/frontend/templates/reorder/sidebar.phtml
@@ -26,14 +26,18 @@