diff --git a/app/code/Magento/ConfigurableProduct/Plugin/SalesRule/Model/Rule/Condition/Product.php b/app/code/Magento/ConfigurableProduct/Plugin/SalesRule/Model/Rule/Condition/Product.php
new file mode 100644
index 0000000000000..183d21b0bddcb
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/Plugin/SalesRule/Model/Rule/Condition/Product.php
@@ -0,0 +1,53 @@
+setProduct(
+ $this->getProductToValidate($subject, $model)
+ );
+ }
+
+ /**
+ * @param \Magento\SalesRule\Model\Rule\Condition\Product $subject
+ * @param \Magento\Framework\Model\AbstractModel $model
+ *
+ * @return \Magento\Catalog\Api\Data\ProductInterface|\Magento\Catalog\Model\Product
+ */
+ private function getProductToValidate(
+ \Magento\SalesRule\Model\Rule\Condition\Product $subject,
+ \Magento\Framework\Model\AbstractModel $model
+ ) {
+ /** @var \Magento\Catalog\Model\Product $product */
+ $product = $model->getProduct();
+
+ $attrCode = $subject->getAttribute();
+
+ /* Check for attributes which are not available for configurable products */
+ if ($product->getTypeId() == Configurable::TYPE_CODE && !$product->hasData($attrCode)) {
+ /** @var \Magento\Catalog\Model\AbstractModel $childProduct */
+ $childProduct = current($model->getChildren())->getProduct();
+ if ($childProduct->hasData($attrCode)) {
+ $product = $childProduct;
+ }
+ }
+
+ return $product;
+ }
+}
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Plugin/SalesRule/Model/Rule/Condition/ProductTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Plugin/SalesRule/Model/Rule/Condition/ProductTest.php
new file mode 100644
index 0000000000000..b1fc09e9676a2
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Plugin/SalesRule/Model/Rule/Condition/ProductTest.php
@@ -0,0 +1,230 @@
+objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+ $this->validator = $this->createValidator();
+ $this->validatorPlugin = $this->objectManager->getObject(ValidatorPlugin::class);
+ }
+
+ /**
+ * @return \Magento\SalesRule\Model\Rule\Condition\Product
+ */
+ private function createValidator(): SalesRuleProduct
+ {
+ /** @var Context|\PHPUnit_Framework_MockObject_MockObject $contextMock */
+ $contextMock = $this->getMockBuilder(Context::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ /** @var Data|\PHPUnit_Framework_MockObject_MockObject $backendHelperMock */
+ $backendHelperMock = $this->getMockBuilder(Data::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ /** @var Config|\PHPUnit_Framework_MockObject_MockObject $configMock */
+ $configMock = $this->getMockBuilder(Config::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ /** @var ProductFactory|\PHPUnit_Framework_MockObject_MockObject $productFactoryMock */
+ $productFactoryMock = $this->getMockBuilder(ProductFactory::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ /** @var ProductRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject $productRepositoryMock */
+ $productRepositoryMock = $this->getMockBuilder(ProductRepositoryInterface::class)
+ ->getMockForAbstractClass();
+ $attributeLoaderInterfaceMock = $this->getMockBuilder(AbstractEntity::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getAttributesByCode'])
+ ->getMock();
+ $attributeLoaderInterfaceMock
+ ->expects($this->any())
+ ->method('getAttributesByCode')
+ ->willReturn([]);
+ /** @var Product|\PHPUnit_Framework_MockObject_MockObject $productMock */
+ $productMock = $this->getMockBuilder(Product::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['loadAllAttributes', 'getConnection', 'getTable'])
+ ->getMock();
+ $productMock->expects($this->any())
+ ->method('loadAllAttributes')
+ ->willReturn($attributeLoaderInterfaceMock);
+ /** @var Collection|\PHPUnit_Framework_MockObject_MockObject $collectionMock */
+ $collectionMock = $this->getMockBuilder(Collection::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ /** @var FormatInterface|\PHPUnit_Framework_MockObject_MockObject $formatMock */
+ $formatMock = new Format(
+ $this->getMockBuilder(ScopeResolverInterface::class)->disableOriginalConstructor()->getMock(),
+ $this->getMockBuilder(ResolverInterface::class)->disableOriginalConstructor()->getMock(),
+ $this->getMockBuilder(CurrencyFactory::class)->disableOriginalConstructor()->getMock()
+ );
+
+ return new SalesRuleProduct(
+ $contextMock,
+ $backendHelperMock,
+ $configMock,
+ $productFactoryMock,
+ $productRepositoryMock,
+ $productMock,
+ $collectionMock,
+ $formatMock
+ );
+ }
+
+ public function testChildIsUsedForValidation()
+ {
+ $configurableProductMock = $this->createProductMock();
+ $configurableProductMock
+ ->expects($this->any())
+ ->method('getTypeId')
+ ->willReturn(Configurable::TYPE_CODE);
+ $configurableProductMock
+ ->expects($this->any())
+ ->method('hasData')
+ ->with($this->equalTo('special_price'))
+ ->willReturn(false);
+
+ /* @var AbstractItem|\PHPUnit_Framework_MockObject_MockObject $item */
+ $item = $this->getMockBuilder(AbstractItem::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['setProduct', 'getProduct', 'getChildren'])
+ ->getMockForAbstractClass();
+ $item->expects($this->any())
+ ->method('getProduct')
+ ->willReturn($configurableProductMock);
+
+ $simpleProductMock = $this->createProductMock();
+ $simpleProductMock
+ ->expects($this->any())
+ ->method('getTypeId')
+ ->willReturn(Type::TYPE_SIMPLE);
+ $simpleProductMock
+ ->expects($this->any())
+ ->method('hasData')
+ ->with($this->equalTo('special_price'))
+ ->willReturn(true);
+
+ $childItem = $this->getMockBuilder(AbstractItem::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getProduct'])
+ ->getMockForAbstractClass();
+ $childItem->expects($this->any())
+ ->method('getProduct')
+ ->willReturn($simpleProductMock);
+
+ $item->expects($this->any())
+ ->method('getChildren')
+ ->willReturn([$childItem]);
+ $item->expects($this->once())
+ ->method('setProduct')
+ ->with($this->identicalTo($simpleProductMock));
+
+ $this->validator->setAttribute('special_price');
+
+ $this->validatorPlugin->beforeValidate($this->validator, $item);
+ }
+
+ /**
+ * @return Product|\PHPUnit_Framework_MockObject_MockObject
+ */
+ private function createProductMock(): \PHPUnit_Framework_MockObject_MockObject
+ {
+ $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class)
+ ->disableOriginalConstructor()
+ ->setMethods([
+ 'getAttribute',
+ 'getId',
+ 'setQuoteItemQty',
+ 'setQuoteItemPrice',
+ 'getTypeId',
+ 'hasData',
+ ])
+ ->getMock();
+ $productMock
+ ->expects($this->any())
+ ->method('setQuoteItemQty')
+ ->willReturnSelf();
+ $productMock
+ ->expects($this->any())
+ ->method('setQuoteItemPrice')
+ ->willReturnSelf();
+
+ return $productMock;
+ }
+
+ public function testChildIsNotUsedForValidation()
+ {
+ $simpleProductMock = $this->createProductMock();
+ $simpleProductMock
+ ->expects($this->any())
+ ->method('getTypeId')
+ ->willReturn(Type::TYPE_SIMPLE);
+ $simpleProductMock
+ ->expects($this->any())
+ ->method('hasData')
+ ->with($this->equalTo('special_price'))
+ ->willReturn(true);
+
+ /* @var AbstractItem|\PHPUnit_Framework_MockObject_MockObject $item */
+ $item = $this->getMockBuilder(AbstractItem::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['setProduct', 'getProduct'])
+ ->getMockForAbstractClass();
+ $item->expects($this->any())
+ ->method('getProduct')
+ ->willReturn($simpleProductMock);
+
+ $item->expects($this->once())
+ ->method('setProduct')
+ ->with($this->identicalTo($simpleProductMock));
+
+ $this->validator->setAttribute('special_price');
+
+ $this->validatorPlugin->beforeValidate($this->validator, $item);
+ }
+}
diff --git a/app/code/Magento/ConfigurableProduct/composer.json b/app/code/Magento/ConfigurableProduct/composer.json
index 4c583d79ce418..f2818883a131b 100644
--- a/app/code/Magento/ConfigurableProduct/composer.json
+++ b/app/code/Magento/ConfigurableProduct/composer.json
@@ -19,6 +19,7 @@
"suggest": {
"magento/module-webapi": "100.2.*",
"magento/module-sales": "101.0.*",
+ "magento/module-sales-rule": "101.0.*",
"magento/module-product-video": "100.2.*",
"magento/module-configurable-sample-data": "Sample Data version:100.2.*",
"magento/module-product-links-sample-data": "Sample Data version:100.2.*"
diff --git a/app/code/Magento/ConfigurableProduct/etc/di.xml b/app/code/Magento/ConfigurableProduct/etc/di.xml
index dfbad0dd5a764..de6856bd1ba1a 100644
--- a/app/code/Magento/ConfigurableProduct/etc/di.xml
+++ b/app/code/Magento/ConfigurableProduct/etc/di.xml
@@ -221,6 +221,9 @@
+
+
+
diff --git a/app/code/Magento/SalesRule/Model/Rule/Condition/Product.php b/app/code/Magento/SalesRule/Model/Rule/Condition/Product.php
index d2e7cabe473f4..0b8b7fc046a24 100644
--- a/app/code/Magento/SalesRule/Model/Rule/Condition/Product.php
+++ b/app/code/Magento/SalesRule/Model/Rule/Condition/Product.php
@@ -10,6 +10,8 @@
* Product rule condition data model
*
* @author Magento Core Team
+ *
+ * @method string getAttribute()
*/
class Product extends \Magento\Rule\Model\Condition\Product\AbstractProduct
{
@@ -31,7 +33,9 @@ protected function _addSpecialAttributes(array &$attributes)
* Validate Product Rule Condition
*
* @param \Magento\Framework\Model\AbstractModel $model
+ *
* @return bool
+ * @throws \Magento\Framework\Exception\NoSuchEntityException
*/
public function validate(\Magento\Framework\Model\AbstractModel $model)
{