Skip to content

Commit 6374500

Browse files
MAGETWO-62405: Ignoring product visibility for SalesRules
1 parent 70c8c2e commit 6374500

File tree

6 files changed

+223
-6
lines changed

6 files changed

+223
-6
lines changed

app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@ public function validate(\Magento\Framework\Model\AbstractModel $model)
518518
$attrCode = $this->getAttribute();
519519

520520
if ('category_ids' == $attrCode) {
521-
return $this->validateAttribute($model->getAvailableInCategories());
521+
return $this->validateAttribute($this->getAvailableInCategories($model));
522522
} elseif (!isset($this->_entityAttributeValues[$model->getId()])) {
523523
if (!$model->getResource()) {
524524
return false;
@@ -621,7 +621,7 @@ public function getMappedSqlField()
621621
public function validateByEntityId($productId)
622622
{
623623
if ('category_ids' == $this->getAttribute()) {
624-
$result = $this->validateAttribute($this->_getAvailableInCategories($productId));
624+
$result = $this->validateAttribute($this->_getAvailableInCategoriesById($productId));
625625
} elseif ('attribute_set_id' == $this->getAttribute()) {
626626
$result = $this->validateAttribute($this->_getAttributeSetId($productId));
627627
} else {
@@ -635,10 +635,21 @@ public function validateByEntityId($productId)
635635
/**
636636
* Retrieve category ids where product is available
637637
*
638+
* @param \Magento\Framework\Model\AbstractModel $model
639+
* @return array
640+
*/
641+
protected function getAvailableInCategories($model)
642+
{
643+
return $model->getAvailableInCategories();
644+
}
645+
646+
/**
647+
* Retrieve category ids where productId is available
648+
*
638649
* @param int $productId
639650
* @return array
640651
*/
641-
protected function _getAvailableInCategories($productId)
652+
protected function _getAvailableInCategoriesById($productId)
642653
{
643654
return $this->_productResource->getConnection()
644655
->fetchCol(

app/code/Magento/SalesRule/Model/Rule/Condition/Product.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,17 @@ public function validate(\Magento\Framework\Model\AbstractModel $model)
5252
return parent::validate($product);
5353
}
5454

55+
/**
56+
* Retrieve category ids where product is available
57+
*
58+
* @param \Magento\Framework\Model\AbstractModel $model
59+
* @return array
60+
*/
61+
protected function getAvailableInCategories($model)
62+
{
63+
return $this->_getAvailableInCategoriesById($model->getId());
64+
}
65+
5566
/**
5667
* Retrieve value element chooser URL
5768
*

app/code/Magento/SalesRule/Test/Unit/Model/Rule/Condition/ProductTest.php

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
*/
66
namespace Magento\SalesRule\Test\Unit\Model\Rule\Condition;
77

8+
use \Magento\Framework\DB\Adapter\AdapterInterface;
9+
use \Magento\Framework\DB\Select;
10+
use \Magento\Framework\Model\AbstractModel;
11+
use Magento\Quote\Model\Quote\Item\AbstractItem;
812
use \Magento\Rule\Model\Condition\Context;
913
use \Magento\Backend\Helper\Data;
1014
use \Magento\Eav\Model\Config;
@@ -52,6 +56,12 @@ class ProductTest extends \PHPUnit_Framework_TestCase
5256
/** @var AttributeLoaderInterface|\PHPUnit_Framework_MockObject_MockObject */
5357
protected $attributeLoaderInterfaceMock;
5458

59+
/** @var AdapterInterface|\PHPUnit_Framework_MockObject_MockObject */
60+
protected $adapterInterfaceMock;
61+
62+
/** @var Select|\PHPUnit_Framework_MockObject_MockObject */
63+
protected $selectMock;
64+
5565
/**
5666
* Setup the test
5767
*/
@@ -78,15 +88,45 @@ protected function setUp()
7888
$this->attributeLoaderInterfaceMock
7989
->expects($this->any())
8090
->method('getAttributesByCode')
81-
->will($this->returnValue([]));
91+
->willReturn([]);
92+
$this->selectMock = $this->getMockBuilder(Select::class)
93+
->disableOriginalConstructor()
94+
->setMethods(['distinct', 'from', 'where'])
95+
->getMock();
96+
$this->selectMock
97+
->expects($this->any())
98+
->method('distinct')
99+
->willReturnSelf();
100+
$this->selectMock
101+
->expects($this->any())
102+
->method('from')
103+
->with($this->anything(),$this->anything())
104+
->willReturnSelf();
105+
$this->adapterInterfaceMock = $this->getMockBuilder(AdapterInterface::class)
106+
->disableOriginalConstructor()
107+
->setMethods(['fetchCol', 'select'])
108+
->getMockForAbstractClass();
109+
$this->adapterInterfaceMock
110+
->expects($this->any())
111+
->method('select')
112+
->willReturn($this->selectMock);
82113
$this->productMock = $this->getMockBuilder(Product::class)
83114
->disableOriginalConstructor()
84-
->setMethods(['loadAllAttributes'])
115+
->setMethods(['loadAllAttributes', 'getConnection', 'getTable'])
85116
->getMock();
86117
$this->productMock
87118
->expects($this->any())
88119
->method('loadAllAttributes')
89-
->will($this->returnValue($this->attributeLoaderInterfaceMock));
120+
->willReturn($this->attributeLoaderInterfaceMock);
121+
$this->productMock
122+
->expects($this->any())
123+
->method('getConnection')
124+
->willReturn($this->adapterInterfaceMock);
125+
$this->productMock
126+
->expects($this->any())
127+
->method('getTable')
128+
->with($this->anything())
129+
->willReturn('table_name');
90130
$this->collectionMock = $this->getMockBuilder(Collection::class)
91131
->disableOriginalConstructor()
92132
->getMock();
@@ -158,4 +198,37 @@ public function testGetValueElementChooserUrl($attribute, $url, $jsObject = '')
158198

159199
$this->assertEquals($url, $this->model->getValueElementChooserUrl());
160200
}
201+
202+
public function testValidateCategoriesIgnoresVisibility()
203+
{
204+
/* @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject $product */
205+
$product = $this->getMockBuilder(\Magento\Catalog\Model\Product::class)
206+
->disableOriginalConstructor()
207+
->setMethods(['getAttribute', 'getId', 'setQuoteItemQty', 'setQuoteItemPrice'])
208+
->getMock();
209+
$product
210+
->expects($this->any())
211+
->method('setQuoteItemQty')
212+
->willReturnSelf();
213+
$product
214+
->expects($this->any())
215+
->method('setQuoteItemPrice')
216+
->willReturnSelf();
217+
/* @var AbstractItem|\PHPUnit_Framework_MockObject_MockObject $item */
218+
$item = $this->getMockBuilder(AbstractItem::class)
219+
->disableOriginalConstructor()
220+
->setMethods(['getProduct'])
221+
->getMockForAbstractClass();
222+
$item->expects($this->any())
223+
->method('getProduct')
224+
->willReturn($product);
225+
$this->model->setAttribute('category_ids');
226+
227+
$this->selectMock
228+
->expects($this->once())
229+
->method('where')
230+
->with($this->logicalNot($this->stringContains('visibility')), $this->anything(), $this->anything());
231+
232+
$this->model->validate($item);
233+
}
161234
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\SalesRule\Model\Rule\Condition;
8+
9+
class ProductTest extends \PHPUnit_Framework_TestCase
10+
{
11+
/**
12+
* @var \Magento\Framework\ObjectManagerInterface
13+
*/
14+
private $objectManager;
15+
16+
protected function setUp()
17+
{
18+
$this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
19+
}
20+
21+
/**
22+
* @magentoAppIsolation enabled
23+
* @param int $categoryId
24+
* @param int $visibility
25+
* @param bool $expectedResult
26+
* @magentoDataFixture Magento/Checkout/_files/quote_with_simple_product.php
27+
* @magentoDataFixture Magento/SalesRule/_files/rules_category.php
28+
* @magentoDataFixture Magento/SalesRule/_files/category.php
29+
* @dataProvider validateProductConditionDataProvider
30+
*/
31+
public function testValidateCategorySalesRuleIgnoresVisibility($categoryId, $visibility, $expectedResult)
32+
{
33+
/** @var $session \Magento\Checkout\Model\Session */
34+
$session = $this->objectManager->create(\Magento\Checkout\Model\Session::class);
35+
36+
/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */
37+
$productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
38+
/** @var $product \Magento\Catalog\Model\Product */
39+
$product = $productRepository->get('simple');
40+
$product->setVisibility($visibility);
41+
$product->setCategoryIds([$categoryId]);
42+
$product->save();
43+
44+
/** @var $rule \Magento\SalesRule\Model\Rule */
45+
$rule = $this->objectManager->get(\Magento\Framework\Registry::class)
46+
->registry('_fixture/Magento_SalesRule_Category');
47+
48+
$this->assertEquals($expectedResult, $rule->validate($session->getQuote()));
49+
}
50+
51+
/**
52+
* @return array
53+
*/
54+
public function validateProductConditionDataProvider()
55+
{
56+
$validCategoryId = 66;
57+
$invalidCategoryId = 2;
58+
$visible = \Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH;
59+
$invisible = \Magento\Catalog\Model\Product\Visibility::VISIBILITY_NOT_VISIBLE;
60+
return [
61+
[
62+
'categoryId' => $validCategoryId,
63+
'visibility' => $visible,
64+
'expectedResult' => true
65+
],
66+
[
67+
'categoryId' => $validCategoryId,
68+
'visibility' => $invisible,
69+
'expectedResult' => true
70+
],
71+
[
72+
'categoryId' => $invalidCategoryId,
73+
'visibility' => $visible,
74+
'expectedResult' => false
75+
],
76+
[
77+
'categoryId' => $invalidCategoryId,
78+
'visibility' => $invisible,
79+
'expectedResult' => false
80+
],
81+
];
82+
}
83+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
$category = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Category::class);
8+
$category->isObjectNew(true);
9+
$category->setId(
10+
66
11+
)->setCreatedAt(
12+
'2014-06-23 09:50:07'
13+
)->setName(
14+
'Category 1'
15+
)->setParentId(
16+
2
17+
)->setPath(
18+
'1/2/333'
19+
)->setLevel(
20+
2
21+
)->setAvailableSortBy(
22+
['position', 'name']
23+
)->setDefaultSortBy(
24+
'name'
25+
)->setIsActive(
26+
true
27+
)->setPosition(
28+
1
29+
)->save();
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
include '../../Checkout/_files/simple_product.php';
8+
/** @var $product \Magento\Catalog\Model\Product */
9+
$product->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_NOT_VISIBLE);
10+
$product->save();

0 commit comments

Comments
 (0)