Skip to content

Commit 2dbab41

Browse files
MC-35675: Orders with Zero Payment Information required are Closed after being invoiced
1 parent 70c5c2f commit 2dbab41

File tree

2 files changed

+192
-12
lines changed

2 files changed

+192
-12
lines changed

app/code/Magento/Sales/Model/Order/Invoice/Total/Discount.php

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,20 @@
55
*/
66
namespace Magento\Sales\Model\Order\Invoice\Total;
77

8+
use Magento\Sales\Model\Order\Invoice;
9+
10+
/**
11+
* Discount invoice
12+
*/
813
class Discount extends AbstractTotal
914
{
1015
/**
11-
* @param \Magento\Sales\Model\Order\Invoice $invoice
16+
* Collect invoice
17+
*
18+
* @param Invoice $invoice
1219
* @return $this
1320
*/
14-
public function collect(\Magento\Sales\Model\Order\Invoice $invoice)
21+
public function collect(Invoice $invoice)
1522
{
1623
$invoice->setDiscountAmount(0);
1724
$invoice->setBaseDiscountAmount(0);
@@ -24,14 +31,7 @@ public function collect(\Magento\Sales\Model\Order\Invoice $invoice)
2431
* So basically if we have invoice with positive discount and it
2532
* was not canceled we don't add shipping discount to this one.
2633
*/
27-
$addShippingDiscount = true;
28-
foreach ($invoice->getOrder()->getInvoiceCollection() as $previousInvoice) {
29-
if ($previousInvoice->getDiscountAmount()) {
30-
$addShippingDiscount = false;
31-
}
32-
}
33-
34-
if ($addShippingDiscount) {
34+
if ($this->isShippingDiscount($invoice)) {
3535
$totalDiscountAmount = $totalDiscountAmount + $invoice->getOrder()->getShippingDiscountAmount();
3636
$baseTotalDiscountAmount = $baseTotalDiscountAmount +
3737
$invoice->getOrder()->getBaseShippingDiscountAmount();
@@ -71,8 +71,29 @@ public function collect(\Magento\Sales\Model\Order\Invoice $invoice)
7171
$invoice->setDiscountAmount(-$totalDiscountAmount);
7272
$invoice->setBaseDiscountAmount(-$baseTotalDiscountAmount);
7373

74-
$invoice->setGrandTotal($invoice->getGrandTotal() - $totalDiscountAmount);
75-
$invoice->setBaseGrandTotal($invoice->getBaseGrandTotal() - $baseTotalDiscountAmount);
74+
$grandTotal = $invoice->getGrandTotal() - $totalDiscountAmount < 0.0001
75+
? 0 : $invoice->getGrandTotal() - $totalDiscountAmount;
76+
$baseGrandTotal = $invoice->getBaseGrandTotal() - $baseTotalDiscountAmount < 0.0001
77+
? 0 : $invoice->getBaseGrandTotal() - $baseTotalDiscountAmount;
78+
$invoice->setGrandTotal($grandTotal);
79+
$invoice->setBaseGrandTotal($baseGrandTotal);
7680
return $this;
7781
}
82+
83+
/**
84+
* Checking if shipping discount was added in previous invoices.
85+
*
86+
* @param Invoice $invoice
87+
* @return bool
88+
*/
89+
private function isShippingDiscount(Invoice $invoice): bool
90+
{
91+
$addShippingDiscount = true;
92+
foreach ($invoice->getOrder()->getInvoiceCollection() as $previousInvoice) {
93+
if ($previousInvoice->getDiscountAmount()) {
94+
$addShippingDiscount = false;
95+
}
96+
}
97+
return $addShippingDiscount;
98+
}
7899
}
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Sales\Test\Unit\Model\Order\Invoice\Total;
9+
10+
use Magento\Sales\Model\Order\Invoice\Total\Discount;
11+
use PHPUnit\Framework\TestCase;
12+
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
13+
use PHPUnit\Framework\MockObject\MockObject;
14+
use Magento\Sales\Model\Order;
15+
use Magento\Sales\Model\Order\Invoice;
16+
use Magento\Sales\Model\Order\Invoice\Item as InvoiceItem;
17+
use Magento\Sales\Model\Order\Item as OrderItem;
18+
19+
/**
20+
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
21+
*/
22+
class DiscountTest extends TestCase
23+
{
24+
/**
25+
* @var Discount
26+
*/
27+
protected $model;
28+
29+
/**
30+
* @var Order|MockObject
31+
*/
32+
protected $order;
33+
34+
/**
35+
* @var ObjectManager
36+
*/
37+
protected $objectManager;
38+
39+
/**
40+
* @var Invoice|MockObject
41+
*/
42+
protected $invoice;
43+
44+
/**
45+
* @inheritdoc
46+
*/
47+
protected function setUp(): void
48+
{
49+
$this->objectManager = new ObjectManager($this);
50+
$this->model = $this->objectManager->getObject(Discount::class);
51+
$this->order = $this->createPartialMock(Order::class, [
52+
'getInvoiceCollection',
53+
]);
54+
$this->invoice = $this->createPartialMock(Invoice::class, [
55+
'getAllItems',
56+
'getOrder',
57+
'roundPrice',
58+
'isLast',
59+
'getGrandTotal',
60+
'getBaseGrandTotal',
61+
'setGrandTotal',
62+
'setBaseGrandTotal'
63+
]);
64+
}
65+
66+
/**
67+
* Test for collect invoice
68+
*
69+
* @param array $invoiceData
70+
* @dataProvider collectInvoiceData
71+
* @return void
72+
*/
73+
public function testCollectInvoiceWithZeroGrandTotal(array $invoiceData): void
74+
{
75+
//Set up invoice mock
76+
/** @var InvoiceItem[] $invoiceItems */
77+
$invoiceItems = [];
78+
foreach ($invoiceData as $invoiceItemData) {
79+
$invoiceItems[] = $this->getInvoiceItem($invoiceItemData);
80+
}
81+
$this->invoice->method('getOrder')
82+
->willReturn($this->order);
83+
$this->order->method('getInvoiceCollection')
84+
->willReturn([]);
85+
$this->invoice->method('getAllItems')
86+
->willReturn($invoiceItems);
87+
$this->invoice->method('getGrandTotal')
88+
->willReturn(15.6801);
89+
$this->invoice->method('getBaseGrandTotal')
90+
->willReturn(15.6801);
91+
92+
$this->invoice->expects($this->exactly(1))
93+
->method('setGrandTotal')
94+
->with(0);
95+
$this->invoice->expects($this->exactly(1))
96+
->method('setBaseGrandTotal')
97+
->with(0);
98+
$this->model->collect($this->invoice);
99+
}
100+
101+
/**
102+
* @return array
103+
*/
104+
public function collectInvoiceData(): array
105+
{
106+
return [
107+
[
108+
[
109+
[
110+
'order_item' => [
111+
'qty_ordered' => 1,
112+
'discount_amount' => 5.34,
113+
'base_discount_amount' => 5.34,
114+
],
115+
'is_last' => true,
116+
'qty' => 1,
117+
],
118+
[
119+
'order_item' => [
120+
'qty_ordered' => 1,
121+
'discount_amount' => 10.34,
122+
'base_discount_amount' => 10.34,
123+
],
124+
'is_last' => true,
125+
'qty' => 1,
126+
],
127+
],
128+
],
129+
];
130+
}
131+
132+
/**
133+
* Get InvoiceItem
134+
*
135+
* @param $invoiceItemData array
136+
* @return InvoiceItem|MockObject
137+
*/
138+
protected function getInvoiceItem($invoiceItemData)
139+
{
140+
/** @var OrderItem|MockObject $orderItem */
141+
$orderItem = $this->createPartialMock(OrderItem::class, [
142+
'isDummy',
143+
]);
144+
foreach ($invoiceItemData['order_item'] as $key => $value) {
145+
$orderItem->setData($key, $value);
146+
}
147+
/** @var InvoiceItem|MockObject $invoiceItem */
148+
$invoiceItem = $this->createPartialMock(InvoiceItem::class, [
149+
'getOrderItem',
150+
'isLast',
151+
]);
152+
$invoiceItem->method('getOrderItem')
153+
->willReturn($orderItem);
154+
$invoiceItem->method('isLast')
155+
->willReturn($invoiceItemData['is_last']);
156+
$invoiceItem->getData('qty', $invoiceItemData['qty']);
157+
return $invoiceItem;
158+
}
159+
}

0 commit comments

Comments
 (0)