Skip to content

Commit e9255f4

Browse files
committed
MAGETWO-57880: [GITHUB] Cancel an order of bundle product #5194
1 parent b255003 commit e9255f4

File tree

5 files changed

+167
-1
lines changed

5 files changed

+167
-1
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\Bundle\Model\Sales\Order\Plugin;
8+
9+
/**
10+
* Plugin to calculate bundle item qty available for cancel
11+
*/
12+
class Item
13+
{
14+
/**
15+
* Retrieve item qty available for cancel
16+
*
17+
* @param \Magento\Sales\Model\Order\Item $subject
18+
* @param float|integer $result
19+
* @return float|integer
20+
*/
21+
public function afterGetQtyToCancel(\Magento\Sales\Model\Order\Item $subject, $result)
22+
{
23+
if ($subject->getProductType() === \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE || $subject->getParentItem()
24+
&& $subject->getParentItem()->getProductType() === \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE
25+
) {
26+
$qtyToCancel = $this->getQtyToCancelBundle($subject);
27+
return max($qtyToCancel, 0);
28+
}
29+
return $result;
30+
}
31+
32+
/**
33+
* Retrieve item qty available for ship
34+
*
35+
* @param \Magento\Sales\Model\Order\Item $subject
36+
* @param float|integer $result
37+
* @return bool
38+
*/
39+
public function afterIsProcessingAvailable(\Magento\Sales\Model\Order\Item $subject, $result)
40+
{
41+
if ($subject->getParentItem()
42+
&& $subject->getParentItem()->getProductType() === \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE
43+
) {
44+
return $subject->getSimpleQtyToShip() > $subject->getQtyToCancel();
45+
}
46+
return $result;
47+
}
48+
49+
/**
50+
* Retrieve Bundle child item qty available for cancel
51+
* getQtyToShip() always returns 0 for BundleItems that ship together
52+
*
53+
* @param \Magento\Sales\Model\Order\Item $item
54+
* @return float|integer
55+
*/
56+
private function getQtyToCancelBundle($item)
57+
{
58+
if ($item->isDummy(true)) {
59+
return min($item->getQtyToInvoice(), $item->getSimpleQtyToShip());
60+
}
61+
return min($item->getQtyToInvoice(), $item->getQtyToShip());
62+
}
63+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\Bundle\Test\Unit\Model\Sales\Order\Plugin;
8+
9+
class ItemTest extends \PHPUnit_Framework_TestCase
10+
{
11+
private $plugin;
12+
13+
private $itemMock;
14+
15+
protected function setUp()
16+
{
17+
$this->itemMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Item::class)
18+
->disableOriginalConstructor()
19+
->getMock();
20+
$this->plugin = new \Magento\Bundle\Model\Sales\Order\Plugin\Item();
21+
}
22+
23+
public function testAfterGetQtyToCancelIfProductIsBundle()
24+
{
25+
$qtyToCancel = 10;
26+
$result = 5;
27+
28+
$this->itemMock
29+
->expects($this->once())
30+
->method('getProductType')
31+
->willReturn(\Magento\Catalog\Model\Product\Type::TYPE_BUNDLE);
32+
$this->itemMock->expects($this->once())->method('isDummy')->willReturn(true);
33+
$this->itemMock->expects($this->once())->method('getQtyToInvoice')->willReturn(15);
34+
$this->itemMock->expects($this->once())->method('getSimpleQtyToShip')->willReturn($qtyToCancel);
35+
$this->assertEquals($qtyToCancel, $this->plugin->afterGetQtyToCancel($this->itemMock, $result));
36+
}
37+
38+
public function testAfterGetQtyToCancelIfParentProductIsBundle()
39+
{
40+
$qtyToCancel = 10;
41+
$result = 5;
42+
$parentItemMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Item::class)
43+
->disableOriginalConstructor()
44+
->getMock();
45+
$this->itemMock
46+
->expects($this->once())
47+
->method('getProductType')
48+
->willReturn(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE);
49+
$this->itemMock->expects($this->any())->method('getParentItem')->willReturn($parentItemMock);
50+
$parentItemMock->expects($this->once())
51+
->method('getProductType')
52+
->willReturn(\Magento\Catalog\Model\Product\Type::TYPE_BUNDLE);
53+
$this->itemMock->expects($this->once())->method('isDummy')->willReturn(false);
54+
$this->itemMock->expects($this->once())->method('getQtyToInvoice')->willReturn(15);
55+
$this->itemMock->expects($this->once())->method('getQtyToShip')->willReturn($qtyToCancel);
56+
$this->assertEquals($qtyToCancel, $this->plugin->afterGetQtyToCancel($this->itemMock, $result));
57+
}
58+
public function testAfterGetQtyToCancelForSimpleProduct()
59+
{
60+
$result = 5;
61+
$this->itemMock
62+
->expects($this->once())
63+
->method('getProductType')
64+
->willReturn(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE);
65+
$this->itemMock->expects($this->any())->method('getParentItem')->willReturn(false);
66+
$this->itemMock->expects($this->never())->method('isDummy');
67+
$this->itemMock->expects($this->never())->method('getQtyToInvoice');
68+
$this->assertEquals($result, $this->plugin->afterGetQtyToCancel($this->itemMock, $result));
69+
}
70+
71+
public function testAfterIsProcessingAvailableForProductWithoutParent()
72+
{
73+
$this->itemMock->expects($this->once())->method('getParentItem')->willReturn(false);
74+
$this->assertFalse($this->plugin->afterIsProcessingAvailable($this->itemMock, false));
75+
}
76+
77+
public function testAfterIsProcessingAvailableForProductWhenParentIsBundle()
78+
{
79+
$parentItemMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Item::class)
80+
->disableOriginalConstructor()
81+
->getMock();
82+
$this->itemMock->expects($this->any())->method('getParentItem')->willReturn($parentItemMock);
83+
$parentItemMock->expects($this->once())
84+
->method('getProductType')
85+
->willReturn(\Magento\Catalog\Model\Product\Type::TYPE_BUNDLE);
86+
$this->itemMock->expects($this->once())->method('getSimpleQtyToShip')->willReturn(10);
87+
$this->itemMock->expects($this->once())->method('getQtyToCancel')->willReturn(5);
88+
$this->assertTrue($this->plugin->afterIsProcessingAvailable($this->itemMock, false));
89+
}
90+
}

app/code/Magento/Bundle/etc/di.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@
8181
<type name="Magento\Catalog\Model\Product">
8282
<plugin name="bundle" type="Magento\Bundle\Model\Plugin\Product" sortOrder="100" />
8383
</type>
84+
<type name="Magento\Sales\Model\Order\Item">
85+
<plugin name="bundle" type="Magento\Bundle\Model\Sales\Order\Plugin\Item" sortOrder="100" />
86+
</type>
8487
<type name="Magento\Framework\EntityManager\Operation\ExtensionPool">
8588
<arguments>
8689
<argument name="extensionActions" xsi:type="array">

app/code/Magento/Sales/Model/Order.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1126,7 +1126,7 @@ public function registerCancellation($comment = '', $graceful = true)
11261126
$state = self::STATE_CANCELED;
11271127
foreach ($this->getAllItems() as $item) {
11281128
if ($state != self::STATE_PROCESSING && $item->getQtyToRefund()) {
1129-
if ($item->getQtyToShip() > $item->getQtyToCancel()) {
1129+
if ($item->isProcessingAvailable()) {
11301130
$state = self::STATE_PROCESSING;
11311131
} else {
11321132
$state = self::STATE_COMPLETE;

app/code/Magento/Sales/Model/Order/Item.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2391,4 +2391,14 @@ public function setExtensionAttributes(\Magento\Sales\Api\Data\OrderItemExtensio
23912391
}
23922392

23932393
//@codeCoverageIgnoreEnd
2394+
2395+
/**
2396+
* Check if it is possible to process item after cancellation
2397+
*
2398+
* @return bool
2399+
*/
2400+
public function isProcessingAvailable()
2401+
{
2402+
return $this->getQtyToShip() > $this->getQtyToCancel();
2403+
}
23942404
}

0 commit comments

Comments
 (0)