diff --git a/app/code/Magento/Sales/Model/Order/Invoice.php b/app/code/Magento/Sales/Model/Order/Invoice.php index 014ad8fd5fe3a..3f2fa1f72f6e5 100644 --- a/app/code/Magento/Sales/Model/Order/Invoice.php +++ b/app/code/Magento/Sales/Model/Order/Invoice.php @@ -407,6 +407,9 @@ public function void() */ public function cancel() { + if (!$this->canCancel()) { + return $this; + } $order = $this->getOrder(); $order->getPayment()->cancelInvoice($this); foreach ($this->getAllItems() as $item) { diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/InvoiceTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/InvoiceTest.php index 0962e32dfb6ed..a115f22dd548e 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/InvoiceTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/InvoiceTest.php @@ -8,12 +8,12 @@ namespace Magento\Sales\Test\Unit\Model\Order; +use Magento\Sales\Api\Data\InvoiceInterface; +use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\Invoice; +use Magento\Sales\Model\ResourceModel\Order\Invoice\Collection as InvoiceCollection; use Magento\Sales\Model\ResourceModel\OrderFactory; -use Magento\Sales\Model\Order; -use Magento\TestFramework\Helper\Bootstrap; use PHPUnit_Framework_MockObject_MockObject as MockObject; -use Magento\Sales\Model\ResourceModel\Order\Invoice\Collection as InvoiceCollection; /** * Class InvoiceTest @@ -72,7 +72,7 @@ protected function setUp() ->setMethods( [ 'getPayment', '__wakeup', 'load', 'setHistoryEntityName', 'getStore', 'getBillingAddress', - 'getShippingAddress' + 'getShippingAddress', 'getConfig', ] ) ->getMock(); @@ -83,7 +83,7 @@ protected function setUp() $this->paymentMock = $this->getMockBuilder( \Magento\Sales\Model\Order\Payment::class )->disableOriginalConstructor()->setMethods( - ['canVoid', '__wakeup', 'canCapture', 'capture', 'pay'] + ['canVoid', '__wakeup', 'canCapture', 'capture', 'pay', 'cancelInvoice'] )->getMock(); $this->orderFactory = $this->createPartialMock(\Magento\Sales\Model\OrderFactory::class, ['create']); @@ -407,4 +407,68 @@ private function getOrderInvoiceCollection() return $collection; } + + /** + * Assert open invoice can be canceled, and its status changes + */ + public function testCancelOpenInvoice() + { + $orderConfigMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Config::class) + ->disableOriginalConstructor()->setMethods( + ['getStateDefaultStatus'] + )->getMock(); + + $orderConfigMock->expects($this->once())->method('getStateDefaultStatus') + ->with(Order::STATE_PROCESSING) + ->willReturn(Order::STATE_PROCESSING); + + $this->order->expects($this->once())->method('getPayment')->willReturn($this->paymentMock); + $this->order->expects($this->once())->method('getConfig')->willReturn($orderConfigMock); + + $this->paymentMock->expects($this->once()) + ->method('cancelInvoice') + ->willReturn($this->paymentMock); + + $this->eventManagerMock->expects($this->once()) + ->method('dispatch') + ->with('sales_order_invoice_cancel'); + + $this->model->setData(InvoiceInterface::ITEMS, []); + $this->model->setState(Invoice::STATE_OPEN); + $this->model->cancel(); + + self::assertEquals(Invoice::STATE_CANCELED, $this->model->getState()); + } + + /** + * Assert open invoice can be canceled, and its status changes + * + * @param $initialInvoiceStatus + * @param $finalInvoiceStatus + * @dataProvider getNotOpenedInvoiceStatuses + */ + public function testCannotCancelNotOpenedInvoice($initialInvoiceStatus, $finalInvoiceStatus) + { + $this->order->expects($this->never())->method('getPayment'); + $this->paymentMock->expects($this->never())->method('cancelInvoice'); + $this->eventManagerMock->expects($this->never()) + ->method('dispatch') + ->with('sales_order_invoice_cancel'); + + $this->model->setState($initialInvoiceStatus); + $this->model->cancel(); + + self::assertEquals($finalInvoiceStatus, $this->model->getState()); + } + + /** + * @return array + */ + public function getNotOpenedInvoiceStatuses() + { + return [ + [Invoice::STATE_PAID, Invoice::STATE_PAID], + [Invoice::STATE_CANCELED, Invoice::STATE_CANCELED], + ]; + } }