Skip to content

Commit d0e1712

Browse files
committed
MC-35065: Catalog pricerules are not working with custom options as expected in Magento 2.3.0 product details page
1 parent 913943f commit d0e1712

File tree

3 files changed

+115
-36
lines changed

3 files changed

+115
-36
lines changed

app/code/Magento/Catalog/Model/Product/Option.php

Lines changed: 74 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
* Copyright © Magento, Inc. All rights reserved.
44
* See COPYING.txt for license details.
55
*/
6-
declare(strict_types=1);
76

87
namespace Magento\Catalog\Model\Product;
98

@@ -205,7 +204,10 @@ public function __construct(
205204
}
206205

207206
/**
208-
* @inheritdoc
207+
* Get resource instance
208+
*
209+
* @return \Magento\Framework\Model\ResourceModel\Db\AbstractDb
210+
* @deprecated 102.0.0 because resource models should be used directly
209211
*/
210212
protected function _getResource()
211213
{
@@ -255,7 +257,7 @@ public function getValueById($valueId)
255257
*
256258
* @param string $type
257259
* @return bool
258-
* @since 101.1.0
260+
* @since 102.0.0
259261
*/
260262
public function hasValues($type = null)
261263
{
@@ -570,7 +572,9 @@ public function getSearchableData($productId, $storeId)
570572
}
571573

572574
/**
573-
* @inheritdoc
575+
* Clearing object's data
576+
*
577+
* @return $this
574578
*/
575579
protected function _clearData()
576580
{
@@ -580,7 +584,9 @@ protected function _clearData()
580584
}
581585

582586
/**
583-
* @inheritdoc
587+
* Clearing cyclic references
588+
*
589+
* @return $this
584590
*/
585591
protected function _clearReferences()
586592
{
@@ -601,7 +607,9 @@ protected function _getValidationRulesBeforeSave()
601607
}
602608

603609
/**
604-
* @inheritdoc
610+
* Get product SKU
611+
*
612+
* @return string
605613
*/
606614
public function getProductSku()
607615
{
@@ -613,7 +621,9 @@ public function getProductSku()
613621
}
614622

615623
/**
616-
* @inheritdoc
624+
* Get option id
625+
*
626+
* @return int|null
617627
* @codeCoverageIgnoreStart
618628
*/
619629
public function getOptionId()
@@ -622,47 +632,60 @@ public function getOptionId()
622632
}
623633

624634
/**
625-
* @inheritdoc
635+
* Get option title
636+
*
637+
* @return string
626638
*/
627639
public function getTitle()
628640
{
629641
return $this->_getData(self::KEY_TITLE);
630642
}
631643

632644
/**
633-
* @inheritdoc
645+
* Get option type
646+
*
647+
* @return string
634648
*/
635649
public function getType()
636650
{
637651
return $this->_getData(self::KEY_TYPE);
638652
}
639653

640654
/**
641-
* @inheritdoc
655+
* Get sort order
656+
*
657+
* @return int
642658
*/
643659
public function getSortOrder()
644660
{
645661
return $this->_getData(self::KEY_SORT_ORDER);
646662
}
647663

648664
/**
649-
* @inheritdoc
665+
* Get is require
666+
*
667+
* @return bool
668+
* @SuppressWarnings(PHPMD.BooleanGetMethodName)
650669
*/
651670
public function getIsRequire()
652671
{
653672
return $this->_getData(self::KEY_IS_REQUIRE);
654673
}
655674

656675
/**
657-
* @inheritdoc
676+
* Get price type
677+
*
678+
* @return string|null
658679
*/
659680
public function getPriceType()
660681
{
661682
return $this->_getData(self::KEY_PRICE_TYPE);
662683
}
663684

664685
/**
665-
* @inheritdoc
686+
* Get Sku
687+
*
688+
* @return string|null
666689
*/
667690
public function getSku()
668691
{
@@ -710,71 +733,98 @@ public function getImageSizeY()
710733
}
711734

712735
/**
713-
* @inheritdoc
736+
* Set product SKU
737+
*
738+
* @param string $productSku
739+
* @return $this
714740
*/
715741
public function setProductSku($productSku)
716742
{
717743
return $this->setData(self::KEY_PRODUCT_SKU, $productSku);
718744
}
719745

720746
/**
721-
* @inheritdoc
747+
* Set option id
748+
*
749+
* @param int $optionId
750+
* @return $this
722751
*/
723752
public function setOptionId($optionId)
724753
{
725754
return $this->setData(self::KEY_OPTION_ID, $optionId);
726755
}
727756

728757
/**
729-
* @inheritdoc
758+
* Set option title
759+
*
760+
* @param string $title
761+
* @return $this
730762
*/
731763
public function setTitle($title)
732764
{
733765
return $this->setData(self::KEY_TITLE, $title);
734766
}
735767

736768
/**
737-
* @inheritdoc
769+
* Set option type
770+
*
771+
* @param string $type
772+
* @return $this
738773
*/
739774
public function setType($type)
740775
{
741776
return $this->setData(self::KEY_TYPE, $type);
742777
}
743778

744779
/**
745-
* @inheritdoc
780+
* Set sort order
781+
*
782+
* @param int $sortOrder
783+
* @return $this
746784
*/
747785
public function setSortOrder($sortOrder)
748786
{
749787
return $this->setData(self::KEY_SORT_ORDER, $sortOrder);
750788
}
751789

752790
/**
753-
* @inheritdoc
791+
* Set is require
792+
*
793+
* @param bool $isRequired
794+
* @return $this
754795
*/
755796
public function setIsRequire($isRequired)
756797
{
757798
return $this->setData(self::KEY_IS_REQUIRE, $isRequired);
758799
}
759800

760801
/**
761-
* @inheritdoc
802+
* Set price
803+
*
804+
* @param float $price
805+
* @return $this
762806
*/
763807
public function setPrice($price)
764808
{
765809
return $this->setData(self::KEY_PRICE, $price);
766810
}
767811

768812
/**
769-
* @inheritdoc
813+
* Set price type
814+
*
815+
* @param string $priceType
816+
* @return $this
770817
*/
771818
public function setPriceType($priceType)
772819
{
773820
return $this->setData(self::KEY_PRICE_TYPE, $priceType);
774821
}
775822

776823
/**
777-
* @inheritdoc
824+
* Set Sku
825+
*
826+
* @param string $sku
827+
* @return $this
778828
*/
779829
public function setSku($sku)
780830
{
@@ -915,7 +965,7 @@ public function setExtensionAttributes(
915965
private function getOptionRepository()
916966
{
917967
if (null === $this->optionRepository) {
918-
$this->optionRepository = ObjectManager::getInstance()
968+
$this->optionRepository = \Magento\Framework\App\ObjectManager::getInstance()
919969
->get(\Magento\Catalog\Model\Product\Option\Repository::class);
920970
}
921971
return $this->optionRepository;
@@ -929,7 +979,7 @@ private function getOptionRepository()
929979
private function getMetadataPool()
930980
{
931981
if (null === $this->metadataPool) {
932-
$this->metadataPool = ObjectManager::getInstance()
982+
$this->metadataPool = \Magento\Framework\App\ObjectManager::getInstance()
933983
->get(\Magento\Framework\EntityManager\MetadataPool::class);
934984
}
935985
return $this->metadataPool;

app/code/Magento/Catalog/Pricing/Price/CalculateCustomOptionCatalogRule.php

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
use Magento\Catalog\Model\Product;
1111
use Magento\Catalog\Model\Product\PriceModifierInterface;
12+
use Magento\CatalogRule\Pricing\Price\CatalogRulePrice;
13+
use Magento\Framework\Pricing\Price\BasePriceProviderInterface;
1214
use Magento\Framework\Pricing\PriceCurrencyInterface;
1315

1416
/**
@@ -54,23 +56,53 @@ public function execute(
5456
$regularPrice = (float)$product->getPriceInfo()
5557
->getPrice(RegularPrice::PRICE_CODE)
5658
->getValue();
57-
$catalogRulePrice = $product->getPriceInfo()->getPrice('final_price')->getValue();
59+
$catalogRulePrice = $this->priceModifier->modifyPrice(
60+
$regularPrice,
61+
$product
62+
);
5863
// Apply catalog price rules to product options only if catalog price rules are applied to product.
5964
if ($catalogRulePrice < $regularPrice) {
6065
$optionPrice = $this->getOptionPriceWithoutPriceRule($optionPriceValue, $isPercent, $regularPrice);
61-
$discount = $catalogRulePrice / $regularPrice;
62-
$finalOptionPrice = $optionPrice*$discount;
66+
$totalCatalogRulePrice = $this->priceModifier->modifyPrice(
67+
$regularPrice + $optionPrice,
68+
$product
69+
);
70+
$finalOptionPrice = $totalCatalogRulePrice - $catalogRulePrice;
6371
} else {
6472
$finalOptionPrice = $this->getOptionPriceWithoutPriceRule(
6573
$optionPriceValue,
6674
$isPercent,
67-
$regularPrice
75+
$this->getGetBasePriceWithOutCatalogRules($product)
6876
);
6977
}
7078

7179
return $this->priceCurrency->convertAndRound($finalOptionPrice);
7280
}
7381

82+
/**
83+
* Get product base price without catalog rules applied.
84+
*
85+
* @param Product $product
86+
* @return float
87+
*/
88+
private function getGetBasePriceWithOutCatalogRules(Product $product): float
89+
{
90+
$basePrice = null;
91+
foreach ($product->getPriceInfo()->getPrices() as $price) {
92+
if ($price instanceof BasePriceProviderInterface
93+
&& $price->getPriceCode() !== CatalogRulePrice::PRICE_CODE
94+
&& $price->getValue() !== false
95+
) {
96+
$basePrice = min(
97+
$price->getValue(),
98+
$basePrice ?? $price->getValue()
99+
);
100+
}
101+
}
102+
103+
return $basePrice ?? $product->getPrice();
104+
}
105+
74106
/**
75107
* Calculate option price without catalog price rule discount.
76108
*

app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductWithSelectFixedMethodTest.xml

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<description value="Admin should be able to apply the catalog price rule for simple product with custom options"/>
1616
<severity value="CRITICAL"/>
1717
<testCaseId value="MC-14771"/>
18-
<group value="CatalogRule"/>
18+
<group value="catalogRule"/>
1919
<group value="mtf_migrated"/>
2020
</annotations>
2121
<before>
@@ -33,7 +33,7 @@
3333

3434
<!-- Update all products to have custom options -->
3535
<updateData createDataKey="createProduct1" entity="productWithFixedOptions" stepKey="updateProductWithOptions1"/>
36-
<magentoCron stepKey="runCronIndex" groups="index"/>
36+
<magentoCron groups="index" stepKey="runCronIndex"/>
3737
</before>
3838
<after>
3939
<!-- Delete products and category -->
@@ -42,7 +42,7 @@
4242

4343
<!-- Delete the catalog price rule -->
4444
<actionGroup ref="AdminOpenCatalogPriceRulePageActionGroup" stepKey="goToPriceRulePage"/>
45-
<actionGroup stepKey="deletePriceRule" ref="deleteEntitySecondaryGrid">
45+
<actionGroup ref="deleteEntitySecondaryGrid" stepKey="deletePriceRule">
4646
<argument name="name" value="{{CatalogRuleByFixed.name}}"/>
4747
<argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/>
4848
</actionGroup>
@@ -60,13 +60,10 @@
6060
<actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/>
6161

6262
<!-- Save and apply the new catalog price rule -->
63-
<actionGroup ref="SaveAndApplyCatalogPriceRuleActionGroup" stepKey="saveAndApplyCatalogPriceRule"/>
64-
<actionGroup ref="CreateCatalogRuleStagingUpdateWithItsStartActionGroup" stepKey="fillOutActionGroup">
65-
<argument name="stagingUpdate" value="_defaultCatalogRule"/>
66-
</actionGroup>
63+
<actionGroup ref="AdminEnableCatalogPriceRuleActionGroup" stepKey="saveAndApplyCatalogPriceRule"/>
6764

6865
<!-- Navigate to category on store front -->
69-
<amOnPage url="{{StorefrontProductPage.url($createCategory.name$)}}" stepKey="goToCategoryPage"/>
66+
<amOnPage url="{{StorefrontProductPage.url($createCategory.name$)}}" stepKey="goToStorefrontCategoryPage"/>
7067

7168
<!-- Check product 1 name on store front category page -->
7269
<actionGroup ref="AssertProductDetailsOnStorefrontActionGroup" stepKey="storefrontProduct1Name">

0 commit comments

Comments
 (0)