diff --git a/app/code/Magento/Quote/Api/Data/CartItemInterface.php b/app/code/Magento/Quote/Api/Data/CartItemInterface.php index 1c651045aa58d..20702d82dab28 100644 --- a/app/code/Magento/Quote/Api/Data/CartItemInterface.php +++ b/app/code/Magento/Quote/Api/Data/CartItemInterface.php @@ -19,6 +19,8 @@ interface CartItemInterface extends \Magento\Framework\Api\ExtensibleDataInterfa const KEY_SKU = 'sku'; + const KEY_ORIGINAL_PRODUCT_SKU = 'original_product_sku'; + const KEY_QTY = 'qty'; const KEY_NAME = 'name'; @@ -49,20 +51,35 @@ public function getItemId(); public function setItemId($itemId); /** - * Returns the product SKU. + * Returns the product options SKU. * * @return string|null Product SKU. Otherwise, null. */ public function getSku(); /** - * Sets the product SKU. + * Sets the product options SKU. * * @param string $sku * @return $this */ public function setSku($sku); + /** + * Returns the product SKU. + * + * @return string|null Product SKU. Otherwise, null. + */ + public function getOriginalProductSku(); + + /** + * Sets the product SKU. + * + * @param string $sku + * @return $this + */ + public function setOriginalProductSku($sku); + /** * Returns the product quantity. * diff --git a/app/code/Magento/Quote/Model/Quote/Item.php b/app/code/Magento/Quote/Model/Quote/Item.php index 22554380ca61e..a3e223b13175f 100644 --- a/app/code/Magento/Quote/Model/Quote/Item.php +++ b/app/code/Magento/Quote/Model/Quote/Item.php @@ -431,6 +431,7 @@ public function setProduct($product) ->setProductId($product->getId()) ->setProductType($product->getTypeId()) ->setSku($this->getProduct()->getSku()) + ->setOriginalProductSku($this->getProduct()->getData('sku')) ->setName($product->getName()) ->setWeight($this->getProduct()->getWeight()) ->setTaxClassId($product->getTaxClassId()) @@ -1075,4 +1076,20 @@ public function setExtensionAttributes(\Magento\Quote\Api\Data\CartItemExtension { return $this->_setExtensionAttributes($extensionAttributes); } + + /** + * @inheritDoc + */ + public function getOriginalProductSku() + { + return $this->getData(self::KEY_ORIGINAL_PRODUCT_SKU); + } + + /** + * @inheritDoc + */ + public function setOriginalProductSku($sku) + { + return $this->setData(self::KEY_ORIGINAL_PRODUCT_SKU, $sku); + } } diff --git a/app/code/Magento/Quote/etc/db_schema.xml b/app/code/Magento/Quote/etc/db_schema.xml index ff183e3150894..f2ad16b707b0f 100644 --- a/app/code/Magento/Quote/etc/db_schema.xml +++ b/app/code/Magento/Quote/etc/db_schema.xml @@ -230,7 +230,8 @@ comment="Parent Item ID"/> - + + diff --git a/app/code/Magento/Quote/etc/fieldset.xml b/app/code/Magento/Quote/etc/fieldset.xml index 85ee20c7f8520..078639f8fecb5 100644 --- a/app/code/Magento/Quote/etc/fieldset.xml +++ b/app/code/Magento/Quote/etc/fieldset.xml @@ -195,6 +195,9 @@ + + + diff --git a/app/code/Magento/Sales/Api/Data/OrderItemInterface.php b/app/code/Magento/Sales/Api/Data/OrderItemInterface.php index 2aee648ef7c9f..2a44b03283908 100644 --- a/app/code/Magento/Sales/Api/Data/OrderItemInterface.php +++ b/app/code/Magento/Sales/Api/Data/OrderItemInterface.php @@ -67,6 +67,10 @@ interface OrderItemInterface extends \Magento\Framework\Api\ExtensibleDataInterf * SKU. */ const SKU = 'sku'; + /* + * Original product SKU. + */ + const ORIGINAL_PRODUCT_SKU = 'original_product_sku'; /* * Name. */ @@ -389,7 +393,7 @@ interface OrderItemInterface extends \Magento\Framework\Api\ExtensibleDataInterf const BASE_WEEE_TAX_ROW_DISPOSITION = 'base_weee_tax_row_disposition'; /** - * Parent Item + * Parent order Item */ const PARENT_ITEM = 'parent_item'; @@ -954,6 +958,13 @@ public function getRowWeight(); */ public function getSku(); + /** + * Gets the original product SKU for the order item. + * + * @return string + */ + public function getOriginalProductSku(); + /** * Gets the store ID for the order item. * @@ -1155,6 +1166,14 @@ public function setIsVirtual($isVirtual); */ public function setSku($sku); + /** + * Sets the SKU for the order item. + * + * @param string $sku + * @return $this + */ + public function setOriginalProductSku($sku); + /** * Sets the name for the order item. * diff --git a/app/code/Magento/Sales/Block/Adminhtml/Items/Column/DefaultColumn.php b/app/code/Magento/Sales/Block/Adminhtml/Items/Column/DefaultColumn.php index 81f670de91805..4a03799713e09 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Items/Column/DefaultColumn.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Items/Column/DefaultColumn.php @@ -114,6 +114,16 @@ public function getSku() return $this->getItem()->getSku(); } + /** + * Get original product sku + * + * @return string + */ + public function getOriginalProductSku() + { + return $this->getItem()->getOriginalProductSku(); + } + /** * Calculate total amount for the item * diff --git a/app/code/Magento/Sales/Model/Order/Item.php b/app/code/Magento/Sales/Model/Order/Item.php index bc55b2229770d..6321e110eec5e 100644 --- a/app/code/Magento/Sales/Model/Order/Item.php +++ b/app/code/Magento/Sales/Model/Order/Item.php @@ -2415,4 +2415,20 @@ public function isProcessingAvailable() { return $this->getQtyToShip() > $this->getQtyToCancel(); } + + /** + * @inheritDoc + */ + public function getOriginalProductSku() + { + return $this->getData(OrderItemInterface::ORIGINAL_PRODUCT_SKU); + } + + /** + * @inheritDoc + */ + public function setOriginalProductSku($sku) + { + return $this->setData(OrderItemInterface::ORIGINAL_PRODUCT_SKU, $sku); + } } diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminVerifyProductSKUInOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminVerifyProductSKUInOrderTest.xml new file mode 100644 index 0000000000000..625122ac4c02d --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminVerifyProductSKUInOrderTest.xml @@ -0,0 +1,131 @@ + + + + + + + + + + <description value="Verify that order contains real SKU of products and not options sku"/> + <severity value="MAJOR"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <!-- Create simple with options product --> + <createData entity="SimpleProduct" stepKey="createSimpleProductWithCustomOptions"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <updateData createDataKey="createSimpleProductWithCustomOptions" entity="productWithDropdownOption" stepKey="updateProductWithCustomOption"/> + <!-- Create configurable product --> + <createData entity="BaseConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <updateData createDataKey="createConfigProduct" entity="productWithOptionRadiobutton" stepKey="updateConfigurableProductWithCustomOption"/> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeFirstOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeSecondOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeFirstOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeSecondOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <createData entity="ApiSimpleOne" stepKey="createConfigFirstChildProduct"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeFirstOption"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigSecondChildProduct"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeSecondOption"/> + </createData> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeFirstOption"/> + <requiredEntity createDataKey="getConfigAttributeSecondOption"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddFirstChild"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigFirstChildProduct"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddSecondChild"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigSecondChildProduct"/> + </createData> + <!-- Create the customer --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <!-- Reindex and flush the cache to display products on the category page --> + <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex"> + <argument name="indices" value=""/> + </actionGroup> + <actionGroup ref="CliCacheFlushActionGroup" stepKey="flushCache"> + <argument name="tags" value=""/> + </actionGroup> + </before> + <after> + <!-- Delete created data --> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="navigateToProductIndexPage"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridToDefaultView"/> + <actionGroup ref="DeleteProductsIfTheyExistActionGroup" stepKey="deleteAllProducts"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <!-- Login to Storefront as Customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginAsCustomer"> + <argument name="Customer" value="$$createCustomer$$" /> + </actionGroup> + <!-- Place the order --> + <!-- Add simple with options to cart --> + <amOnPage url="{{StorefrontProductPage.url($$createSimpleProductWithCustomOptions.custom_attributes[url_key]$$)}}" stepKey="goSimpleProductPage"/> + <waitForPageLoad stepKey="waitForSecondProductPageLoad"/> + <actionGroup ref="StorefrontProductPageSelectDropDownOptionValueActionGroup" stepKey="selectFirstOption"> + <argument name="attributeLabel" value="{{ProductOptionValueDropdown.title}}"/> + <argument name="optionLabel" value="{{ProductOptionValueWithSkuDropdown1.title}}"/> + </actionGroup> + <!-- Add the product to the shopping cart --> + <actionGroup ref="StorefrontAddToCartCustomOptionsProductPageActionGroup" stepKey="addSimpleProductToCart"> + <argument name="productName" value="$$createSimpleProductWithCustomOptions.name$$"/> + </actionGroup> + <!-- Add configurable with options to cart --> + <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="goConfigurableProductPage"> + <argument name="productUrl" value="$$createConfigProduct.custom_attributes[url_key]$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductWithSelectedConfigurableAndCustomOptionsToCartActionGroup" stepKey="addConfigurableToCart"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="option" value="$$getConfigAttributeSecondOption.label$$"/> + <argument name="customizableOption" value="{{ProductOptionValueRadioButtons1.title}}"/> + </actionGroup> + <!-- Place the Order --> + <actionGroup ref="StorefrontOpenCheckoutPageActionGroup" stepKey="onCheckout"/> + <actionGroup ref="StorefrontCheckoutClickNextOnShippingStepActionGroup" stepKey="clickNextButton"/> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="placeOrder"/> + <waitForPageLoad stepKey="waitForSuccess"/> + <!--Open Order View Page--> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminOrdersPageOpenActionGroup" stepKey="openOrdersGridPage"/> + <actionGroup ref="AdminOrderGridClickFirstRowActionGroup" stepKey="openOrderViewPage"/> + <!--Check if order has correct products sku--> + <actionGroup ref="SeeProductInItemsOrderedActionGroup" stepKey="verifySimpleProductSku"> + <argument name="product" value="SimpleProduct"/> + </actionGroup> + <actionGroup ref="SeeProductInItemsOrderedActionGroup" stepKey="verifyConfigurableProductSku"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Sales/etc/db_schema.xml b/app/code/Magento/Sales/etc/db_schema.xml index 491772e7e65a0..393b0198393f1 100644 --- a/app/code/Magento/Sales/etc/db_schema.xml +++ b/app/code/Magento/Sales/etc/db_schema.xml @@ -481,6 +481,7 @@ <column xsi:type="smallint" name="is_virtual" unsigned="true" nullable="true" identity="false" comment="Is Virtual"/> <column xsi:type="varchar" name="sku" nullable="true" length="255" comment="Sku"/> + <column xsi:type="varchar" name="original_product_sku" nullable="true" length="255" comment="Product sku"/> <column xsi:type="varchar" name="name" nullable="true" length="255" comment="Name"/> <column xsi:type="text" name="description" nullable="true" comment="Description"/> <column xsi:type="text" name="applied_rule_ids" nullable="true" comment="Applied Rule Ids"/> diff --git a/app/code/Magento/Sales/view/adminhtml/templates/items/column/name.phtml b/app/code/Magento/Sales/view/adminhtml/templates/items/column/name.phtml index a3904ac09c6b4..e28699b6443a6 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/items/column/name.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/items/column/name.phtml @@ -23,7 +23,8 @@ $catalogHelper = $block->getData('catalogHelper'); </div> <div class="product-sku-block"> <span><?= $block->escapeHtml(__('SKU'))?>:</span> - <?= /* @noEscape */ implode('<br />', $catalogHelper->splitSku($block->escapeHtml($block->getSku()))) ?> + <?= /* @noEscape */ + implode('<br />', $catalogHelper->splitSku($block->escapeHtml($block->getOriginalProductSku()))) ?> </div> <?php if ($block->getOrderOptions()): ?> diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CartItemRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CartItemRepositoryTest.php index e3680c573750d..586c3fec3302a 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CartItemRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CartItemRepositoryTest.php @@ -65,6 +65,7 @@ public function testAddProductToCartWithCustomOptions() [ 'item_id' => $item->getItemId(), 'sku' => $item->getSku(), + 'original_product_sku' => $item->getOriginalProductSku(), 'qty' => $item->getQty(), 'name' => $item->getName(), 'price' => $item->getPrice(), diff --git a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/CartItemRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/CartItemRepositoryTest.php index 8af1d2a02d4e6..1c6896627521a 100644 --- a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/CartItemRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/CartItemRepositoryTest.php @@ -247,6 +247,7 @@ public function testGetList() $expectedResult = [[ 'item_id' => $item->getItemId(), 'sku' => $item->getSku(), + 'original_product_sku' => $item->getOriginalProductSku(), 'name' => $item->getName(), 'price' => $item->getPrice(), 'qty' => $item->getQty(), diff --git a/dev/tests/api-functional/testsuite/Magento/Quote/Api/CartItemRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Quote/Api/CartItemRepositoryTest.php index bf5421b5ca2be..5a89643618143 100644 --- a/dev/tests/api-functional/testsuite/Magento/Quote/Api/CartItemRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Quote/Api/CartItemRepositoryTest.php @@ -45,6 +45,7 @@ public function testGetList() $data = [ 'item_id' => (int)$item->getItemId(), 'sku' => $item->getSku(), + 'original_product_sku' => $item->getOriginalProductSku(), 'name' => $item->getName(), 'price' => (float)$item->getPrice(), 'qty' => (float)$item->getQty(), diff --git a/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartItemRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartItemRepositoryTest.php index 42d21afae8274..16c9e82c22c29 100644 --- a/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartItemRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartItemRepositoryTest.php @@ -66,6 +66,7 @@ public function testGetList() $data = [ 'item_id' => $item->getItemId(), 'sku' => $item->getSku(), + 'original_product_sku' => $item->getOriginalProductSku(), 'name' => $item->getName(), 'price' => $item->getPrice(), 'qty' => $item->getQty(), diff --git a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderItemGetTest.php b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderItemGetTest.php index 25795f2cdd747..6de927058d193 100644 --- a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderItemGetTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderItemGetTest.php @@ -56,7 +56,7 @@ public function testGet() //check that nullable fields were marked as optional and were not sent foreach ($response as $fieldName => $value) { - if ($fieldName == 'sku') { + if ($fieldName === 'sku' || $fieldName === 'original_product_sku') { continue; } $this->assertNotNull($value);