Skip to content

Commit 7d68038

Browse files
committed
AC-3178::Order remains in status 'processing' after shipping, if items is get partially refunded
1 parent bddae56 commit 7d68038

File tree

3 files changed

+155
-30
lines changed

3 files changed

+155
-30
lines changed

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

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66
namespace Magento\Sales\Model;
77

8+
use Magento\Catalog\Api\ProductRepositoryInterface;
89
use Magento\Config\Model\Config\Source\Nooptreq;
910
use Magento\Directory\Model\Currency;
1011
use Magento\Directory\Model\RegionFactory;
@@ -196,10 +197,9 @@ class Order extends AbstractModel implements EntityInterface, OrderInterface
196197
protected $_orderConfig;
197198

198199
/**
199-
* @var \Magento\Catalog\Api\ProductRepositoryInterface
200-
* @deprecated 100.1.0 Remove unused dependency.
200+
* @var ProductRepositoryInterface
201201
*/
202-
protected $productRepository;
202+
protected ProductRepositoryInterface $productRepository;
203203

204204
/**
205205
* @var \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory
@@ -339,7 +339,7 @@ class Order extends AbstractModel implements EntityInterface, OrderInterface
339339
* @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $timezone
340340
* @param \Magento\Store\Model\StoreManagerInterface $storeManager
341341
* @param Order\Config $orderConfig
342-
* @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository
342+
* @param ProductRepositoryInterface $productRepository
343343
* @param \Magento\Sales\Model\ResourceModel\Order\Item\CollectionFactory $orderItemCollectionFactory
344344
* @param \Magento\Catalog\Model\Product\Visibility $productVisibility
345345
* @param \Magento\Sales\Api\InvoiceManagementInterface $invoiceManagement
@@ -378,7 +378,7 @@ public function __construct(
378378
\Magento\Framework\Stdlib\DateTime\TimezoneInterface $timezone,
379379
\Magento\Store\Model\StoreManagerInterface $storeManager,
380380
\Magento\Sales\Model\Order\Config $orderConfig,
381-
\Magento\Catalog\Api\ProductRepositoryInterface $productRepository,
381+
ProductRepositoryInterface $productRepository,
382382
\Magento\Sales\Model\ResourceModel\Order\Item\CollectionFactory $orderItemCollectionFactory,
383383
\Magento\Catalog\Model\Product\Visibility $productVisibility,
384384
\Magento\Sales\Api\InvoiceManagementInterface $invoiceManagement,
@@ -750,11 +750,11 @@ private function canCreditmemoForZeroTotal($totalRefunded)
750750
$hasDueAmount = $this->canInvoice() && ($checkAmtTotalPaid);
751751
//case when paid amount is refunded and order has creditmemo created
752752
$creditmemos = ($this->getCreditmemosCollection() === false) ?
753-
true : ($this->_memoCollectionFactory->create()->setOrderFilter($this)->getTotalCount() > 0);
753+
true : ($this->_memoCollectionFactory->create()->setOrderFilter($this)->getTotalCount() > 0);
754754
$paidAmtIsRefunded = $this->getTotalRefunded() == $totalPaid && $creditmemos;
755755
if (($hasDueAmount || $paidAmtIsRefunded) ||
756756
(!$checkAmtTotalPaid &&
757-
abs($totalRefunded - $this->getAdjustmentNegative()) < .0001)) {
757+
abs($totalRefunded - $this->getAdjustmentNegative()) < .0001)) {
758758
return false;
759759
}
760760
return true;
@@ -839,6 +839,49 @@ public function canShip()
839839
return false;
840840
}
841841

842+
/**
843+
* Check if all items are remaining items after partially refunded are shipped
844+
*
845+
* @return bool
846+
*/
847+
public function isPartiallyRefundedOrderShipped(): bool
848+
{
849+
if ($this->getShippedItems() > 0
850+
&& $this->getTotalQtyOrdered() <= $this->getRefundedItems() + $this->getShippedItems()) {
851+
return true;
852+
}
853+
return false;
854+
}
855+
856+
/**
857+
* get all refunded items number
858+
*
859+
* @return int
860+
*/
861+
private function getRefundedItems(): int
862+
{
863+
$num_of_refunded_items = 0;
864+
foreach ($this->getAllItems() as $item) {
865+
if ($item->getProductType() == 'simple') {
866+
$num_of_refunded_items += (int)$item->getQtyRefunded();
867+
}
868+
}
869+
return $num_of_refunded_items;
870+
}
871+
872+
/**
873+
* get all shipped items number
874+
*
875+
* @return int
876+
*/
877+
private function getShippedItems(): int
878+
{
879+
$num_of_shipped_items= 0;
880+
foreach ($this->getAllItems() as $item) {
881+
$num_of_shipped_items += (int)$item->getQtyShipped();
882+
}
883+
return $num_of_shipped_items;
884+
}
842885
/**
843886
* Check if item is refunded.
844887
*

app/code/Magento/Sales/Model/ResourceModel/Order/Handler/State.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,11 @@ public function check(Order $order)
3838
) {
3939
$order->setState(Order::STATE_CLOSED)
4040
->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_CLOSED));
41-
} elseif ($currentState === Order::STATE_PROCESSING && !$order->canShip()) {
41+
} elseif ($currentState === Order::STATE_PROCESSING
42+
&& (!$order->canShip() || $order->isPartiallyRefundedOrderShipped())
43+
) {
4244
$order->setState(Order::STATE_COMPLETE)
4345
->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_COMPLETE));
44-
} elseif ($order->getIsVirtual() && $order->getStatus() === Order::STATE_CLOSED) {
45-
$order->setState(Order::STATE_CLOSED);
4646
}
4747
}
4848
return $this;

app/code/Magento/Sales/Test/Unit/Model/ResourceModel/Order/Handler/StateTest.php

Lines changed: 102 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ protected function setUp(): void
4545
'getIsVirtual',
4646
'getIsNotVirtual',
4747
'getStatus',
48+
'getAllItems',
49+
'isPartiallyRefundedOrderShipped'
4850
]
4951
)
5052
->disableOriginalConstructor()
@@ -81,7 +83,8 @@ public function testCheck(
8183
int $callGetIsInProcessNum,
8284
bool $isCanceled,
8385
bool $canUnhold,
84-
bool $isNotVirtual
86+
bool $isNotVirtual,
87+
bool $isPartiallyRefundedOrderShipped
8588
) {
8689
$this->orderMock->setState($currentState);
8790
$this->orderMock->expects($this->any())
@@ -110,6 +113,12 @@ public function testCheck(
110113
$this->orderMock->method('getStatus')
111114
->willReturn($expectedState);
112115
}
116+
117+
$this->orderMock->method('getAllItems')
118+
->willReturn([]);
119+
$this->orderMock->method('isPartiallyRefundedOrderShipped')
120+
->willReturn($isPartiallyRefundedOrderShipped);
121+
113122
$this->state->check($this->orderMock);
114123
$this->assertEquals($expectedState, $this->orderMock->getState());
115124
}
@@ -134,7 +143,8 @@ public function stateCheckDataProvider()
134143
'get_is_in_process_invoke_count' => 0,
135144
'is_canceled' => false,
136145
'can_unhold' => false,
137-
'is_not_virtual' => true
146+
'is_not_virtual' => true,
147+
'isPartiallyRefundedOrderShipped' => false
138148
],
139149
'complete - !canCreditmemo,!canShip -> closed' => [
140150
'can_credit_memo' => false,
@@ -147,7 +157,8 @@ public function stateCheckDataProvider()
147157
'get_is_in_process_invoke_count' => 0,
148158
'is_canceled' => false,
149159
'can_unhold' => false,
150-
'is_not_virtual' => true
160+
'is_not_virtual' => true,
161+
'isPartiallyRefundedOrderShipped' => false
151162
],
152163
'processing - !canCreditmemo,canShip -> processing' => [
153164
'can_credit_memo' => false,
@@ -160,7 +171,8 @@ public function stateCheckDataProvider()
160171
'get_is_in_process_invoke_count' => 0,
161172
'is_canceled' => false,
162173
'can_unhold' => false,
163-
'is_not_virtual' => true
174+
'is_not_virtual' => true,
175+
'isPartiallyRefundedOrderShipped' => false
164176
],
165177
'complete - !canCreditmemo,canShip -> complete' => [
166178
'can_credit_memo' => false,
@@ -173,7 +185,8 @@ public function stateCheckDataProvider()
173185
'get_is_in_process_invoke_count' => 0,
174186
'is_canceled' => false,
175187
'can_unhold' => false,
176-
'is_not_virtual' => true
188+
'is_not_virtual' => true,
189+
'isPartiallyRefundedOrderShipped' => false
177190
],
178191
'processing - canCreditmemo,!canShip -> complete' => [
179192
'can_credit_memo' => true,
@@ -186,7 +199,8 @@ public function stateCheckDataProvider()
186199
'get_is_in_process_invoke_count' => 0,
187200
'is_canceled' => false,
188201
'can_unhold' => false,
189-
'is_not_virtual' => true
202+
'is_not_virtual' => true,
203+
'isPartiallyRefundedOrderShipped' => true
190204
],
191205
'complete - canCreditmemo,!canShip -> complete' => [
192206
'can_credit_memo' => true,
@@ -199,7 +213,8 @@ public function stateCheckDataProvider()
199213
'get_is_in_process_invoke_count' => 0,
200214
'is_canceled' => false,
201215
'can_unhold' => false,
202-
'is_not_virtual' => true
216+
'is_not_virtual' => true,
217+
'isPartiallyRefundedOrderShipped' => false
203218
],
204219
'processing - canCreditmemo, canShip -> processing' => [
205220
'can_credit_memo' => true,
@@ -212,7 +227,8 @@ public function stateCheckDataProvider()
212227
'get_is_in_process_invoke_count' => 0,
213228
'is_canceled' => false,
214229
'can_unhold' => false,
215-
'is_not_virtual' => true
230+
'is_not_virtual' => true,
231+
'isPartiallyRefundedOrderShipped' => false
216232
],
217233
'complete - canCreditmemo, canShip -> complete' => [
218234
'can_credit_memo' => true,
@@ -225,7 +241,8 @@ public function stateCheckDataProvider()
225241
'get_is_in_process_invoke_count' => 0,
226242
'is_canceled' => false,
227243
'can_unhold' => false,
228-
'is_not_virtual' => true
244+
'is_not_virtual' => true,
245+
'isPartiallyRefundedOrderShipped' => false
229246
],
230247
'new - canCreditmemo, canShip, IsInProcess -> processing' => [
231248
'can_credit_memo' => true,
@@ -238,7 +255,8 @@ public function stateCheckDataProvider()
238255
'get_is_in_process_invoke_count' => 1,
239256
'is_canceled' => false,
240257
'can_unhold' => false,
241-
'is_not_virtual' => true
258+
'is_not_virtual' => true,
259+
'isPartiallyRefundedOrderShipped' => false
242260
],
243261
'new - canCreditmemo, !canShip, IsInProcess -> processing' => [
244262
'can_credit_memo' => true,
@@ -251,7 +269,36 @@ public function stateCheckDataProvider()
251269
'get_is_in_process_invoke_count' => 1,
252270
'is_canceled' => false,
253271
'can_unhold' => false,
254-
'is_not_virtual' => true
272+
'is_not_virtual' => true,
273+
'isPartiallyRefundedOrderShipped' => false
274+
],
275+
'processing - canCreditmemo, !canShip, isPartiallyRefundedOrderShipped -> complete' => [
276+
'can_credit_memo' => true,
277+
'can_credit_memo_invoke_count' => 1,
278+
'can_ship' => false,
279+
'call_can_skip_num' => 1,
280+
'current_state' => Order::STATE_PROCESSING,
281+
'expected_state' => Order::STATE_COMPLETE,
282+
'is_in_process' => true,
283+
'get_is_in_process_invoke_count' => 0,
284+
'is_canceled' => false,
285+
'can_unhold' => false,
286+
'is_not_virtual' => true,
287+
'isPartiallyRefundedOrderShipped' => true
288+
],
289+
'processing - canCreditmemo, !canShip, !isPartiallyRefundedOrderShipped -> processing' => [
290+
'can_credit_memo' => true,
291+
'can_credit_memo_invoke_count' => 1,
292+
'can_ship' => true,
293+
'call_can_skip_num' => 1,
294+
'current_state' => Order::STATE_PROCESSING,
295+
'expected_state' => Order::STATE_PROCESSING,
296+
'is_in_process' => true,
297+
'get_is_in_process_invoke_count' => 0,
298+
'is_canceled' => false,
299+
'can_unhold' => false,
300+
'is_not_virtual' => true,
301+
'isPartiallyRefundedOrderShipped' => false
255302
],
256303
'new - canCreditmemo, canShip, !IsInProcess -> new' => [
257304
'can_credit_memo' => true,
@@ -264,7 +311,8 @@ public function stateCheckDataProvider()
264311
'get_is_in_process_invoke_count' => 1,
265312
'is_canceled' => false,
266313
'can_unhold' => false,
267-
'is_not_virtual' => true
314+
'is_not_virtual' => true,
315+
'isPartiallyRefundedOrderShipped' => false
268316
],
269317
'hold - canUnhold -> hold' => [
270318
'can_credit_memo' => true,
@@ -277,7 +325,8 @@ public function stateCheckDataProvider()
277325
'get_is_in_process_invoke_count' => 0,
278326
'is_canceled' => false,
279327
'can_unhold' => true,
280-
'is_not_virtual' => true
328+
'is_not_virtual' => true,
329+
'isPartiallyRefundedOrderShipped' => false
281330
],
282331
'payment_review - canUnhold -> payment_review' => [
283332
'can_credit_memo' => true,
@@ -290,7 +339,8 @@ public function stateCheckDataProvider()
290339
'get_is_in_process_invoke_count' => 0,
291340
'is_canceled' => false,
292341
'can_unhold' => true,
293-
'is_not_virtual' => true
342+
'is_not_virtual' => true,
343+
'isPartiallyRefundedOrderShipped' => false
294344
],
295345
'pending_payment - canUnhold -> pending_payment' => [
296346
'can_credit_memo' => true,
@@ -303,7 +353,8 @@ public function stateCheckDataProvider()
303353
'get_is_in_process_invoke_count' => 0,
304354
'is_canceled' => false,
305355
'can_unhold' => true,
306-
'is_not_virtual' => true
356+
'is_not_virtual' => true,
357+
'isPartiallyRefundedOrderShipped' => false
307358
],
308359
'cancelled - isCanceled -> cancelled' => [
309360
'can_credit_memo' => true,
@@ -316,20 +367,36 @@ public function stateCheckDataProvider()
316367
'get_is_in_process_invoke_count' => 0,
317368
'is_canceled' => true,
318369
'can_unhold' => false,
319-
'is_not_virtual' => true
370+
'is_not_virtual' => true,
371+
'isPartiallyRefundedOrderShipped' => false
320372
],
321-
'processing - !canCreditmemo!canShip -> complete(virtual product)' => [
373+
'processing - !canCreditmemo !canShip -> complete(virtual product)' => [
322374
'can_credit_memo' => false,
323375
'can_credit_memo_invoke_count' => 1,
324376
'can_ship' => false,
325-
'call_can_skip_num' => 2,
377+
'call_can_skip_num' =>2,
326378
'current_state' => Order::STATE_PROCESSING,
327379
'expected_state' => Order::STATE_COMPLETE,
328380
'is_in_process' => false,
329381
'get_is_in_process_invoke_count' => 0,
330382
'is_canceled' => false,
331383
'can_unhold' => false,
332-
'is_not_virtual' => false
384+
'is_not_virtual' => false,
385+
'isPartiallyRefundedOrderShipped' => true
386+
],
387+
'complete - !canCreditmemo, !canShip !isPartiallyRefundedOrderShipped - closed(virtual product)' => [
388+
'can_credit_memo' => false,
389+
'can_credit_memo_invoke_count' => 1,
390+
'can_ship' => false,
391+
'call_can_skip_num' => 1,
392+
'current_state' => Order::STATE_COMPLETE,
393+
'expected_state' => Order::STATE_CLOSED,
394+
'is_in_process' => false,
395+
'get_is_in_process_invoke_count' => 0,
396+
'is_canceled' => false,
397+
'can_unhold' => false,
398+
'is_not_virtual' => true,
399+
'isPartiallyRefundedOrderShipped' => true
333400
],
334401
'complete - !canCreditmemo, !canShip - closed(virtual product)' => [
335402
'can_credit_memo' => false,
@@ -342,7 +409,22 @@ public function stateCheckDataProvider()
342409
'get_is_in_process_invoke_count' => 0,
343410
'is_canceled' => false,
344411
'can_unhold' => false,
345-
'is_not_virtual' => false,
412+
'is_not_virtual' => true,
413+
'isPartiallyRefundedOrderShipped' => false
414+
],
415+
'complete - !canCreditmemo, !canShip - closed(virtual product)' => [
416+
'can_credit_memo' => false,
417+
'can_credit_memo_invoke_count' => 1,
418+
'can_ship' => false,
419+
'call_can_skip_num' => 1,
420+
'current_state' => Order::STATE_COMPLETE,
421+
'expected_state' => Order::STATE_CLOSED,
422+
'is_in_process' => false,
423+
'get_is_in_process_invoke_count' => 0,
424+
'is_canceled' => false,
425+
'can_unhold' => false,
426+
'is_not_virtual' => true,
427+
'isPartiallyRefundedOrderShipped' => false
346428
],
347429
];
348430
}

0 commit comments

Comments
 (0)