From c45490d961eab771faf50cc78c8d004a6c627975 Mon Sep 17 00:00:00 2001 From: Michiel Gerritsen Date: Wed, 20 Nov 2019 09:25:31 +0100 Subject: [PATCH 1/6] Fix: When the Terms and Conditions are required, it was not possible to set the payment fee --- .../js/view/checkout/save-payment-method.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/view/frontend/web/js/view/checkout/save-payment-method.js b/view/frontend/web/js/view/checkout/save-payment-method.js index 2a495f3a58f..ef407c5c5a0 100644 --- a/view/frontend/web/js/view/checkout/save-payment-method.js +++ b/view/frontend/web/js/view/checkout/save-payment-method.js @@ -1,4 +1,5 @@ define([ + 'ko', 'uiComponent', 'mage/storage', 'Magento_Checkout/js/model/quote', @@ -6,6 +7,7 @@ define([ 'Magento_Checkout/js/model/totals', 'Magento_Checkout/js/action/get-totals' ], function ( + ko, Component, storage, quote, @@ -54,11 +56,26 @@ define([ var url = resourceUrlManager.getUrl(urls, params); payload.paymentMethod = { - method: method + method: method, + extension_attributes: {} }; payload.billingAddress = quote.billingAddress(); + /** + * Problem: We need to set the payment method, therefor we created this function. The api call requires + * that the agreements are all agreed by before doing any action. That's why we list all agreement ids + * and sent them with the request. In a later point in the checkout this will also be checked. + */ + var config = window.checkoutConfig.checkoutAgreements; + if (config.isEnabled) { + var ids = config.agreements.map( function (agreement) { + return agreement.agreementId; + }); + + payload.paymentMethod.extension_attributes.agreement_ids = ids; + } + totals.isLoading(true); storage.post( url, From f1270c4e9de54665449ac8baf22299cc775a973b Mon Sep 17 00:00:00 2001 From: Michiel Gerritsen Date: Wed, 20 Nov 2019 15:08:04 +0100 Subject: [PATCH 2/6] Feature: Allow to mark Payment Link orders manually as paid --- Controller/Adminhtml/Action/MarkAsPaid.php | 130 ++++++++++++++++++ Observer/OrderCancelAfter.php | 7 + Plugin/Sales/Block/Adminhtml/Order/View.php | 70 ++++++++++ .../Sales/Block/Adminhtml/Order/ViewTest.php | 85 ++++++++++++ etc/adminhtml/di.xml | 6 + 5 files changed, 298 insertions(+) create mode 100644 Controller/Adminhtml/Action/MarkAsPaid.php create mode 100644 Plugin/Sales/Block/Adminhtml/Order/View.php create mode 100644 Test/Integration/Plugin/Sales/Block/Adminhtml/Order/ViewTest.php create mode 100644 etc/adminhtml/di.xml diff --git a/Controller/Adminhtml/Action/MarkAsPaid.php b/Controller/Adminhtml/Action/MarkAsPaid.php new file mode 100644 index 00000000000..aafbbee2e72 --- /dev/null +++ b/Controller/Adminhtml/Action/MarkAsPaid.php @@ -0,0 +1,130 @@ +orderRepository = $orderRepository; + $this->orderCreate = $orderCreate; + $this->quoteSession = $quoteSession; + $this->transactionFactory = $transactionFactory; + } + + /** + * This controller recreates the selected order with the checkmo payment method and marks it as completed. The + * original order is then canceled. + * + * {@inheritDoc} + */ + public function execute() + { + $orderId = $this->getRequest()->getParam('order_id'); + + $originalOrder = $this->orderRepository->get($orderId); + $originalOrder->setReordered(true); + + $resultRedirect = $this->resultRedirectFactory->create(); + try { + $order = $this->recreateOrder($originalOrder); + $this->cancelOriginalOrder($originalOrder, $order->getIncrementId()); + $this->save($order, $originalOrder); + + $this->messageManager->addSuccessMessage( + __( + 'We cancelled order %1, created this order and marked it as complete.', + $originalOrder->getIncrementId() + ) + ); + + return $resultRedirect->setPath('sales/order/view', ['order_id' => $order->getEntityId()]); + } catch (\Exception $exception) { + $this->messageManager->addExceptionMessage($exception); + + return $resultRedirect->setPath('sales/order/view', ['order_id' => $originalOrder->getEntityId()]); + } + } + + /** + * @param OrderInterface $originalOrder + * @return OrderInterface + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function recreateOrder(OrderInterface $originalOrder) + { + $this->quoteSession->setOrderId($originalOrder->getEntityId()); + $this->quoteSession->setUseOldShippingMethod(true); + $this->orderCreate->initFromOrder($originalOrder); + $this->orderCreate->setPaymentMethod('checkmo'); + + $order = $this->orderCreate->createOrder(); + $order->setState(Order::STATE_COMPLETE); + $order->setStatus(Order::STATE_COMPLETE); + + return $order; + } + + /** + * @param OrderInterface $originalOrder + * @param string $newIncrementId + */ + private function cancelOriginalOrder(OrderInterface $originalOrder, $newIncrementId) + { + $comment = __('We created a new order with increment ID: %1', $newIncrementId); + $originalOrder->addCommentToStatusHistory($comment); + $originalOrder->cancel(); + } + + /** + * @param OrderInterface $order + * @param OrderInterface $originalOrder + * @throws \Exception + */ + private function save(OrderInterface $order, OrderInterface $originalOrder) + { + $transaction = $this->transactionFactory->create(); + $transaction->addObject($order); + $transaction->addObject($originalOrder); + $transaction->save(); + } +} diff --git a/Observer/OrderCancelAfter.php b/Observer/OrderCancelAfter.php index 157bffddbbd..dbb11b7559c 100644 --- a/Observer/OrderCancelAfter.php +++ b/Observer/OrderCancelAfter.php @@ -52,6 +52,13 @@ public function execute(Observer $observer) /** @var \Magento\Sales\Model\Order $order */ $order = $observer->getEvent()->getorder(); + /** + * When manually marking an order as paid we don't want to communicate to Mollie as it will throw an exception. + */ + if ($order->getReordered()) { + return; + } + if ($this->mollieHelper->isPaidUsingMollieOrdersApi($order)) { $this->mollieModel->cancelOrder($order); } diff --git a/Plugin/Sales/Block/Adminhtml/Order/View.php b/Plugin/Sales/Block/Adminhtml/Order/View.php new file mode 100644 index 00000000000..ed83f8c7f7c --- /dev/null +++ b/Plugin/Sales/Block/Adminhtml/Order/View.php @@ -0,0 +1,70 @@ +url = $url; + $this->orderRepository = $orderRepository; + $this->paymentHelper = $paymentHelper; + } + + public function beforeSetLayout(Subject $subject) + { + $order = $subject->getOrder(); + $instance = $this->paymentHelper->getMethodInstance(Checkmo::PAYMENT_METHOD_CHECKMO_CODE); + + $isAvailable = !$instance->isAvailable(); + if (!$order->canCancel() || + $order->getPayment()->getMethod() != 'mollie_methods_paymentlink' || + !$instance || + $isAvailable + ) { + return; + } + + $message = __('Are you sure you want to do this? ' . + 'This will cancel the current order and create a new one that is marked as payed.'); + $url = $this->url->getUrl('mollie/action/markAsPaid/'); + + $subject->addButton( + 'mollie_payment_mark_as_payed', + [ + 'label' => __('Mark as paid'), + 'onclick' => 'confirmSetLocation(\'' . $message . '\', \'' . $url . '\', {data: {order_id: ' . $subject->getOrderId() . '}})', + ], + 0, + -10 + ); + } +} diff --git a/Test/Integration/Plugin/Sales/Block/Adminhtml/Order/ViewTest.php b/Test/Integration/Plugin/Sales/Block/Adminhtml/Order/ViewTest.php new file mode 100644 index 00000000000..f02a15faa9e --- /dev/null +++ b/Test/Integration/Plugin/Sales/Block/Adminhtml/Order/ViewTest.php @@ -0,0 +1,85 @@ +createMock(Order::class); + $orderMock->method('canCancel')->willReturn(false); + + $subjectMock = $this->createMock(Subject::class); + $subjectMock->method('getOrder')->willReturn($orderMock); + + $subjectMock->expects($this->never())->method('addButton'); + + /** @var View $instance */ + $instance = $this->objectManager->create(View::class); + $instance->beforeSetLayout($subjectMock); + } + + /** + * @magentoDataFixture Magento/Sales/_files/order.php + */ + public function testDoesNotShowsTheButtonWhenNotPaymentLink() + { + $order = $this->loadOrderById('100000001'); + $order->getPayment()->setMethod('checkmo'); + + $subjectMock = $this->createMock(Subject::class); + $subjectMock->method('getOrder')->willReturn($order); + + $subjectMock->expects($this->never())->method('addButton'); + + /** @var View $instance */ + $instance = $this->objectManager->create(View::class); + $instance->beforeSetLayout($subjectMock); + } + + /** + * @magentoDataFixture Magento/Sales/_files/order.php + * @magentoConfigFixture current_store payment/checkmo/active 0 + */ + public function testDoesNotShowsTheButtonWhenCheckmoIsNotAvailable() + { + $order = $this->loadOrderById('100000001'); + $order->getPayment()->setMethod('mollie_methods_paymentlink'); + + $subjectMock = $this->createMock(Subject::class); + $subjectMock->method('getOrder')->willReturn($order); + + $subjectMock->expects($this->never())->method('addButton'); + + /** @var View $instance */ + $instance = $this->objectManager->create(View::class); + $instance->beforeSetLayout($subjectMock); + } + + /** + * @magentoDataFixture Magento/Sales/_files/order.php + * @magentoConfigFixture current_store payment/checkmo/active 1 + */ + public function testShowTheButtonWhenApplicable() + { + $order = $this->loadOrderById('100000001'); + $order->getPayment()->setMethod('mollie_methods_paymentlink'); + + $subjectMock = $this->createMock(Subject::class); + $subjectMock->method('getOrder')->willReturn($order); + + $subjectMock->expects($this->once())->method('addButton'); + + /** @var View $instance */ + $instance = $this->objectManager->create(View::class); + $instance->beforeSetLayout($subjectMock); + } +} diff --git a/etc/adminhtml/di.xml b/etc/adminhtml/di.xml new file mode 100644 index 00000000000..f638006dbb0 --- /dev/null +++ b/etc/adminhtml/di.xml @@ -0,0 +1,6 @@ + + + + + + From 838f02912929b39d29aa272abe1b83aa04895d01 Mon Sep 17 00:00:00 2001 From: Michiel Gerritsen Date: Thu, 21 Nov 2019 11:21:01 +0100 Subject: [PATCH 3/6] New method: Reorder, on/off method, create invoice and changed status to processing --- Config.php | 20 +++ Controller/Adminhtml/Action/MarkAsPaid.php | 45 ++++--- Model/Methods/Reorder.php | 116 ++++++++++++++++++ Plugin/Sales/Block/Adminhtml/Order/View.php | 12 ++ .../Sales/Block/Adminhtml/Order/ViewTest.php | 27 +++- etc/adminhtml/methods/paymentlink.xml | 25 ++-- etc/config.xml | 9 ++ 7 files changed, 229 insertions(+), 25 deletions(-) create mode 100644 Model/Methods/Reorder.php diff --git a/Config.php b/Config.php index 843e3300fd3..38e096d4d1d 100644 --- a/Config.php +++ b/Config.php @@ -17,6 +17,7 @@ class Config const PAYMENT_KLARNAPAYLATER_PAYMENT_SURCHARGE_TAX_CLASS = 'payment/mollie_methods_klarnapaylater/payment_surcharge_tax_class'; const PAYMENT_KLARNASLICEIT_PAYMENT_SURCHARGE = 'payment/mollie_methods_klarnasliceit/payment_surcharge'; const PAYMENT_KLARNASLICEIT_PAYMENT_SURCHARGE_TAX_CLASS = 'payment/mollie_methods_klarnasliceit/payment_surcharge_tax_class'; + const PAYMENT_PAYMENTLINK_ALLOW_MARK_AS_PAID = 'payment/mollie_methods_paymentlink/allow_mark_as_paid'; /** * @var ScopeConfigInterface @@ -39,6 +40,16 @@ private function getPath($path, $storeId) return $this->config->getValue($path, ScopeInterface::SCOPE_STORE, $storeId); } + /** + * @param $path + * @param $storeId + * @return bool + */ + private function getFlag($path, $storeId) + { + return $this->config->isSetFlag($path, ScopeInterface::SCOPE_STORE, $storeId); + } + public function statusPendingBanktransfer($storeId = null) { return $this->config->getValue( @@ -92,4 +103,13 @@ public function klarnaSliceitPaymentSurchargeTaxClass($storeId = null) { return $this->getPath(static::PAYMENT_KLARNASLICEIT_PAYMENT_SURCHARGE_TAX_CLASS, $storeId); } + + /** + * @param null $storeId + * @return bool + */ + public function paymentlinkAllowMarkAsPaid($storeId = null) + { + return $this->getFlag(static::PAYMENT_PAYMENTLINK_ALLOW_MARK_AS_PAID, $storeId); + } } diff --git a/Controller/Adminhtml/Action/MarkAsPaid.php b/Controller/Adminhtml/Action/MarkAsPaid.php index aafbbee2e72..5a91e9b401b 100644 --- a/Controller/Adminhtml/Action/MarkAsPaid.php +++ b/Controller/Adminhtml/Action/MarkAsPaid.php @@ -8,11 +8,14 @@ use Magento\Backend\App\Action; use Magento\Backend\Model\Session\Quote; +use Magento\Framework\DB\Transaction; use Magento\Framework\DB\TransactionFactory; use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Api\OrderRepositoryInterface; use Magento\Sales\Model\AdminOrder\Create; use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Invoice; +use Magento\Sales\Model\Service\InvoiceService; class MarkAsPaid extends Action { @@ -31,16 +34,27 @@ class MarkAsPaid extends Action */ private $quoteSession; + /** + * @var InvoiceService + */ + private $invoiceService; + /** * @var TransactionFactory */ private $transactionFactory; + /** + * @var Transaction + */ + private $transaction; + public function __construct( Action\Context $context, OrderRepositoryInterface $orderRepository, Create $orderCreate, Quote $quoteSession, + InvoiceService $invoiceService, TransactionFactory $transactionFactory ) { parent::__construct($context); @@ -48,6 +62,7 @@ public function __construct( $this->orderRepository = $orderRepository; $this->orderCreate = $orderCreate; $this->quoteSession = $quoteSession; + $this->invoiceService = $invoiceService; $this->transactionFactory = $transactionFactory; } @@ -66,9 +81,12 @@ public function execute() $resultRedirect = $this->resultRedirectFactory->create(); try { + $this->transaction = $this->transactionFactory->create(); + $order = $this->recreateOrder($originalOrder); + $this->createInvoiceFor($order); $this->cancelOriginalOrder($originalOrder, $order->getIncrementId()); - $this->save($order, $originalOrder); + $this->transaction->save(); $this->messageManager->addSuccessMessage( __( @@ -95,11 +113,14 @@ private function recreateOrder(OrderInterface $originalOrder) $this->quoteSession->setOrderId($originalOrder->getEntityId()); $this->quoteSession->setUseOldShippingMethod(true); $this->orderCreate->initFromOrder($originalOrder); - $this->orderCreate->setPaymentMethod('checkmo'); + $this->orderCreate->setPaymentMethod('mollie_methods_reorder'); $order = $this->orderCreate->createOrder(); - $order->setState(Order::STATE_COMPLETE); - $order->setStatus(Order::STATE_COMPLETE); + $order->setState(Order::STATE_PROCESSING); + $order->setStatus(Order::STATE_PROCESSING); + + $this->transaction->addObject($order); + $this->transaction->addObject($originalOrder); return $order; } @@ -115,16 +136,12 @@ private function cancelOriginalOrder(OrderInterface $originalOrder, $newIncremen $originalOrder->cancel(); } - /** - * @param OrderInterface $order - * @param OrderInterface $originalOrder - * @throws \Exception - */ - private function save(OrderInterface $order, OrderInterface $originalOrder) + private function createInvoiceFor(OrderInterface $order) { - $transaction = $this->transactionFactory->create(); - $transaction->addObject($order); - $transaction->addObject($originalOrder); - $transaction->save(); + $invoice = $this->invoiceService->prepareInvoice($order); + $invoice->setRequestedCaptureCase(Invoice::CAPTURE_OFFLINE); + $invoice->register(); + + $this->transaction->addObject($invoice); } } diff --git a/Model/Methods/Reorder.php b/Model/Methods/Reorder.php new file mode 100644 index 00000000000..088a56e3c60 --- /dev/null +++ b/Model/Methods/Reorder.php @@ -0,0 +1,116 @@ +request = $request; + + parent::__construct( + $context, + $registry, + $extensionFactory, + $customAttributeFactory, + $paymentData, + $scopeConfig, + $logger, + $resource, + $resourceCollection, + $data, + $directory + ); + } + + /** + * @param string $paymentAction + * @param object $stateObject + * + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Mollie\Api\Exceptions\ApiException + */ + public function initialize($paymentAction, $stateObject) + { + /** @var \Magento\Sales\Model\Order\Payment $payment */ + $payment = $this->getInfoInstance(); + + /** @var \Magento\Sales\Model\Order $order */ + $order = $payment->getOrder(); + $order->setCanSendNewEmailFlag(false); + $order->save(); + } + + public function isAvailable(\Magento\Quote\Api\Data\CartInterface $quote = null) + { + return $this->request->getModuleName() == 'mollie' && $this->request->getActionName() == 'markAsPaid'; + } +} diff --git a/Plugin/Sales/Block/Adminhtml/Order/View.php b/Plugin/Sales/Block/Adminhtml/Order/View.php index ed83f8c7f7c..5eb64883ae9 100644 --- a/Plugin/Sales/Block/Adminhtml/Order/View.php +++ b/Plugin/Sales/Block/Adminhtml/Order/View.php @@ -11,9 +11,15 @@ use Magento\Payment\Helper\Data as PaymentHelper; use Magento\Sales\Api\OrderRepositoryInterface; use Magento\Sales\Block\Adminhtml\Order\View as Subject; +use Mollie\Payment\Config; class View { + /** + * @var Config + */ + private $config; + /** * @var UrlInterface */ @@ -30,10 +36,12 @@ class View private $paymentHelper; public function __construct( + Config $config, UrlInterface $url, OrderRepositoryInterface $orderRepository, PaymentHelper $paymentHelper ) { + $this->config = $config; $this->url = $url; $this->orderRepository = $orderRepository; $this->paymentHelper = $paymentHelper; @@ -42,6 +50,10 @@ public function __construct( public function beforeSetLayout(Subject $subject) { $order = $subject->getOrder(); + if (!$this->config->paymentlinkAllowMarkAsPaid($order->getStoreId())) { + return; + } + $instance = $this->paymentHelper->getMethodInstance(Checkmo::PAYMENT_METHOD_CHECKMO_CODE); $isAvailable = !$instance->isAvailable(); diff --git a/Test/Integration/Plugin/Sales/Block/Adminhtml/Order/ViewTest.php b/Test/Integration/Plugin/Sales/Block/Adminhtml/Order/ViewTest.php index f02a15faa9e..ddd97027e3d 100644 --- a/Test/Integration/Plugin/Sales/Block/Adminhtml/Order/ViewTest.php +++ b/Test/Integration/Plugin/Sales/Block/Adminhtml/Order/ViewTest.php @@ -12,6 +12,26 @@ class ViewTest extends IntegrationTestCase { + /** + * @magentoConfigFixture current_store payment/mollie_methods_paymentlink/allow_mark_as_paid 0 + */ + public function testDoesNotShowsTheButtonWhenDisabled() + { + $orderMock = $this->createMock(Order::class); + + $subjectMock = $this->createMock(Subject::class); + $subjectMock->method('getOrder')->willReturn($orderMock); + + $subjectMock->expects($this->never())->method('addButton'); + + /** @var View $instance */ + $instance = $this->objectManager->create(View::class); + $instance->beforeSetLayout($subjectMock); + } + + /** + * @magentoConfigFixture current_store payment/mollie_methods_paymentlink/allow_mark_as_paid 1 + */ public function testDoesNotShowsTheButtonWhenWeCantCancel() { $orderMock = $this->createMock(Order::class); @@ -28,6 +48,7 @@ public function testDoesNotShowsTheButtonWhenWeCantCancel() } /** + * @magentoConfigFixture current_store payment/mollie_methods_paymentlink/allow_mark_as_paid 1 * @magentoDataFixture Magento/Sales/_files/order.php */ public function testDoesNotShowsTheButtonWhenNotPaymentLink() @@ -47,9 +68,9 @@ public function testDoesNotShowsTheButtonWhenNotPaymentLink() /** * @magentoDataFixture Magento/Sales/_files/order.php - * @magentoConfigFixture current_store payment/checkmo/active 0 + * @magentoConfigFixture current_store payment/mollie_methods_paymentlink/allow_mark_as_paid 0 */ - public function testDoesNotShowsTheButtonWhenCheckmoIsNotAvailable() + public function testDoesNotShowsTheButtonWhenMollieReorderIsNotAvailable() { $order = $this->loadOrderById('100000001'); $order->getPayment()->setMethod('mollie_methods_paymentlink'); @@ -66,7 +87,7 @@ public function testDoesNotShowsTheButtonWhenCheckmoIsNotAvailable() /** * @magentoDataFixture Magento/Sales/_files/order.php - * @magentoConfigFixture current_store payment/checkmo/active 1 + * @magentoConfigFixture default_store payment/mollie_methods_paymentlink/allow_mark_as_paid 1 */ public function testShowTheButtonWhenApplicable() { diff --git a/etc/adminhtml/methods/paymentlink.xml b/etc/adminhtml/methods/paymentlink.xml index e00dcc111e1..37ac8427e5f 100644 --- a/etc/adminhtml/methods/paymentlink.xml +++ b/etc/adminhtml/methods/paymentlink.xml @@ -27,7 +27,16 @@ 1 - + + Magento\Config\Model\Config\Source\Yesno + payment/mollie_methods_paymentlink/allow_mark_as_paid + + 1 + + + Magento\Config\Model\Config\Source\Yesno @@ -38,7 +47,7 @@ 1 - + Mollie\Payment\Model\Adminhtml\Source\NewStatus payment/mollie_methods_paymentlink/order_status_new @@ -46,7 +55,7 @@ 1 - Magento\Payment\Model\Config\Source\Allspecificcountries @@ -55,7 +64,7 @@ 1 - Magento\Directory\Model\Config\Source\Country @@ -65,7 +74,7 @@ 1 - payment/mollie_methods_paymentlink/min_order_total @@ -73,7 +82,7 @@ 1 - payment/mollie_methods_paymentlink/max_order_total @@ -81,7 +90,7 @@ 1 - validate-number @@ -90,7 +99,7 @@ 1 - validate-digits-range digits-range-1-100 diff --git a/etc/config.xml b/etc/config.xml index 54afbf0e8f7..20956f31719 100644 --- a/etc/config.xml +++ b/etc/config.xml @@ -172,6 +172,15 @@ here to pay for your purchase.]]> + + 1 + Mollie\Payment\Model\Methods\Reorder + Mollie: Reorder + {ordernumber} + order + order + 0 + 1 Mollie\Payment\Model\Methods\Giftcard From ed3f551a6671511cdfe3200b37ff5c906da2068b Mon Sep 17 00:00:00 2001 From: Michiel Gerritsen Date: Thu, 21 Nov 2019 12:25:54 +0100 Subject: [PATCH 4/6] Bugfix: Partial creditmemos with cart price rules applied would return in an error --- Model/OrderLines.php | 26 +++++++++++++----- Test/Integration/Model/OrderLinesTest.php | 32 +++++++++++++++++++++++ 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/Model/OrderLines.php b/Model/OrderLines.php index 75fd942164f..e701c875aec 100644 --- a/Model/OrderLines.php +++ b/Model/OrderLines.php @@ -12,6 +12,7 @@ use Magento\Framework\Model\ResourceModel\AbstractResource; use Magento\Framework\Data\Collection\AbstractDb; use Magento\Framework\Model\AbstractModel; +use Magento\Sales\Api\Data\CreditmemoInterface; use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\Item; use Mollie\Payment\Model\ResourceModel\OrderLines\Collection as OrderLinesCollection; @@ -416,12 +417,11 @@ public function getOrderLineByItemId($itemId) } /** - * @param Order\Creditmemo $creditmemo - * @param $addShipping - * + * @param CreditmemoInterface $creditmemo + * @param bool $addShipping * @return array */ - public function getCreditmemoOrderLines($creditmemo, $addShipping) + public function getCreditmemoOrderLines(CreditmemoInterface $creditmemo, $addShipping) { $orderLines = []; @@ -429,9 +429,23 @@ public function getCreditmemoOrderLines($creditmemo, $addShipping) foreach ($creditmemo->getAllItems() as $item) { $orderItemId = $item->getOrderItemId(); $lineId = $this->getOrderLineByItemId($orderItemId)->getLineId(); - if ($lineId) { - $orderLines[] = ['id' => $lineId, 'quantity' => round($item->getQty())]; + if (!$lineId) { + continue; } + + $line = [ + 'id' => $lineId, + 'quantity' => round($item->getQty()), + ]; + + if ($item->getBaseDiscountAmount()) { + $line['amount'] = $this->mollieHelper->getAmountArray( + $creditmemo->getBaseCurrencyCode(), + $item->getBaseRowTotalInclTax() - $item->getBaseDiscountAmount() + ); + } + + $orderLines[] = $line; } $orderId = $creditmemo->getOrderId(); diff --git a/Test/Integration/Model/OrderLinesTest.php b/Test/Integration/Model/OrderLinesTest.php index dc2f55a4716..1f7d93e4859 100644 --- a/Test/Integration/Model/OrderLinesTest.php +++ b/Test/Integration/Model/OrderLinesTest.php @@ -3,6 +3,7 @@ namespace Mollie\Payment\Model; use Magento\Sales\Api\Data\CreditmemoInterface; +use Magento\Sales\Api\Data\CreditmemoItemInterface; use Mollie\Payment\Test\Integration\IntegrationTestCase; class OrderLinesTest extends IntegrationTestCase @@ -91,6 +92,37 @@ public function testGetCreditmemoOrderLinesIncludesTheStoreCredit() $this->assertEquals(1, $line['quantity']); } + public function testCreditmemoUsesTheDiscount() + { + /** @var OrderLines $orderLine */ + $orderLine = $this->objectManager->get(\Mollie\Payment\Model\OrderLinesFactory::class)->create(); + $orderLine->setItemId(999); + $orderLine->setLineId('ord_abc123'); + $orderLine->save(); + + /** @var CreditmemoItemInterface $creditmemoItem */ + $creditmemoItem = $this->objectManager->create(CreditmemoItemInterface::class); + $creditmemoItem->setBaseRowTotalInclTax(45); + $creditmemoItem->setBaseDiscountAmount(9); + $creditmemoItem->setQty(1); + $creditmemoItem->setOrderItemId(999); + + /** @var CreditmemoInterface $creditmemo */ + $creditmemo = $this->objectManager->get(CreditmemoInterface::class); + $creditmemo->setOrderId(999); + $creditmemo->setItems([$creditmemoItem]); + + /** @var OrderLines $instance */ + $instance = $this->objectManager->get(OrderLines::class); + $result = $instance->getCreditmemoOrderLines($creditmemo, false); + + $this->assertCount(1, $result['lines']); + + $line = $result['lines'][0]; + $this->assertEquals('36', $line['amount']['value']); + $this->assertEquals(1, $line['quantity']); + } + private function rollbackCreditmemos() { $collection = $this->objectManager->get(\Mollie\Payment\Model\ResourceModel\OrderLines\Collection::class); From 52db87d284e59dcb36af81b0a92be56f3f5e1d10 Mon Sep 17 00:00:00 2001 From: Michiel Gerritsen Date: Thu, 21 Nov 2019 15:23:27 +0100 Subject: [PATCH 5/6] Notify customer about the invoice --- Config.php | 10 +++++ Controller/Adminhtml/Action/MarkAsPaid.php | 48 +++++++++++++++++++++- 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/Config.php b/Config.php index 38e096d4d1d..cab021bd7bd 100644 --- a/Config.php +++ b/Config.php @@ -11,6 +11,7 @@ class Config { + const GENERAL_INVOICE_NOTIFY = 'payment/mollie_general/invoice_notify'; const XML_PATH_STATUS_PENDING_BANKTRANSFER = 'payment/mollie_methods_banktransfer/order_status_pending'; const XML_PATH_STATUS_NEW_PAYMENT_LINK = 'payment/mollie_methods_paymentlink/order_status_new'; const PAYMENT_KLARNAPAYLATER_PAYMENT_SURCHARGE = 'payment/mollie_methods_klarnapaylater/payment_surcharge'; @@ -50,6 +51,15 @@ private function getFlag($path, $storeId) return $this->config->isSetFlag($path, ScopeInterface::SCOPE_STORE, $storeId); } + /** + * @param null $storeId + * @return bool + */ + public function sendInvoiceEmail($storeId = null) + { + return $this->getFlag(static::GENERAL_INVOICE_NOTIFY, $storeId); + } + public function statusPendingBanktransfer($storeId = null) { return $this->config->getValue( diff --git a/Controller/Adminhtml/Action/MarkAsPaid.php b/Controller/Adminhtml/Action/MarkAsPaid.php index 5a91e9b401b..ce366aa34d4 100644 --- a/Controller/Adminhtml/Action/MarkAsPaid.php +++ b/Controller/Adminhtml/Action/MarkAsPaid.php @@ -10,15 +10,24 @@ use Magento\Backend\Model\Session\Quote; use Magento\Framework\DB\Transaction; use Magento\Framework\DB\TransactionFactory; +use Magento\Sales\Api\Data\InvoiceInterface; use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Api\OrderRepositoryInterface; use Magento\Sales\Model\AdminOrder\Create; use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Email\Sender\InvoiceSender; use Magento\Sales\Model\Order\Invoice; use Magento\Sales\Model\Service\InvoiceService; +use Mollie\Payment\Config; +use Mollie\Payment\Service\Order\OrderCommentHistory; class MarkAsPaid extends Action { + /** + * @var Config + */ + private $config; + /** * @var OrderRepositoryInterface */ @@ -44,6 +53,16 @@ class MarkAsPaid extends Action */ private $transactionFactory; + /** + * @var InvoiceSender + */ + private $invoiceSender; + + /** + * @var OrderCommentHistory + */ + private $orderCommentHistory; + /** * @var Transaction */ @@ -51,19 +70,25 @@ class MarkAsPaid extends Action public function __construct( Action\Context $context, + Config $config, OrderRepositoryInterface $orderRepository, Create $orderCreate, Quote $quoteSession, InvoiceService $invoiceService, + InvoiceSender $invoiceSender, + OrderCommentHistory $orderCommentHistory, TransactionFactory $transactionFactory ) { parent::__construct($context); + $this->config = $config; $this->orderRepository = $orderRepository; $this->orderCreate = $orderCreate; $this->quoteSession = $quoteSession; $this->invoiceService = $invoiceService; + $this->invoiceSender = $invoiceSender; $this->transactionFactory = $transactionFactory; + $this->orderCommentHistory = $orderCommentHistory; } /** @@ -84,10 +109,12 @@ public function execute() $this->transaction = $this->transactionFactory->create(); $order = $this->recreateOrder($originalOrder); - $this->createInvoiceFor($order); + $invoice = $this->createInvoiceFor($order); $this->cancelOriginalOrder($originalOrder, $order->getIncrementId()); $this->transaction->save(); + $this->sendInvoice($invoice, $order); + $this->messageManager->addSuccessMessage( __( 'We cancelled order %1, created this order and marked it as complete.', @@ -143,5 +170,24 @@ private function createInvoiceFor(OrderInterface $order) $invoice->register(); $this->transaction->addObject($invoice); + + return $invoice; + } + + private function sendInvoice(InvoiceInterface $invoice, OrderInterface $order) + { + /** @var Order\Invoice $invoice */ + if ($invoice->getEmailSent() || !$this->config->sendInvoiceEmail($invoice->getStoreId())) { + return; + } + + try { + $this->invoiceSender->send($invoice); + $message = __('Notified customer about invoice #%1', $invoice->getIncrementId()); + $this->orderCommentHistory->add($order, $message, true); + } catch (\Throwable $exception) { + $message = __('Unable to send the invoice: %1', $exception->getMessage()); + $this->orderCommentHistory->add($order, $message, true); + } } } From 512487d36bd08a95f56e900dfaf8730fa5779480 Mon Sep 17 00:00:00 2001 From: Marvin-Magmodules Date: Tue, 26 Nov 2019 20:24:56 +0100 Subject: [PATCH 6/6] Small fixes --- Controller/Adminhtml/Action/MarkAsPaid.php | 18 +++++++++++++----- Model/Methods/Reorder.php | 8 ++------ 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/Controller/Adminhtml/Action/MarkAsPaid.php b/Controller/Adminhtml/Action/MarkAsPaid.php index ce366aa34d4..5a57316fd3e 100644 --- a/Controller/Adminhtml/Action/MarkAsPaid.php +++ b/Controller/Adminhtml/Action/MarkAsPaid.php @@ -110,9 +110,10 @@ public function execute() $order = $this->recreateOrder($originalOrder); $invoice = $this->createInvoiceFor($order); - $this->cancelOriginalOrder($originalOrder, $order->getIncrementId()); + $this->cancelOriginalOrder($originalOrder); $this->transaction->save(); + $this->addCommentHistoryOriginalOrder($originalOrder, $order->getIncrementId()); $this->sendInvoice($invoice, $order); $this->messageManager->addSuccessMessage( @@ -152,15 +153,22 @@ private function recreateOrder(OrderInterface $originalOrder) return $order; } + /** + * @param OrderInterface $originalOrder + */ + private function cancelOriginalOrder(OrderInterface $originalOrder) + { + $originalOrder->cancel(); + } + /** * @param OrderInterface $originalOrder * @param string $newIncrementId */ - private function cancelOriginalOrder(OrderInterface $originalOrder, $newIncrementId) + private function addCommentHistoryOriginalOrder(OrderInterface $originalOrder, $newIncrementId) { $comment = __('We created a new order with increment ID: %1', $newIncrementId); - $originalOrder->addCommentToStatusHistory($comment); - $originalOrder->cancel(); + $this->orderCommentHistory->add($originalOrder, $comment, false); } private function createInvoiceFor(OrderInterface $order) @@ -187,7 +195,7 @@ private function sendInvoice(InvoiceInterface $invoice, OrderInterface $order) $this->orderCommentHistory->add($order, $message, true); } catch (\Throwable $exception) { $message = __('Unable to send the invoice: %1', $exception->getMessage()); - $this->orderCommentHistory->add($order, $message, true); + $this->orderCommentHistory->add($order, $message, false); } } } diff --git a/Model/Methods/Reorder.php b/Model/Methods/Reorder.php index 088a56e3c60..c2fb4cad392 100644 --- a/Model/Methods/Reorder.php +++ b/Model/Methods/Reorder.php @@ -6,7 +6,6 @@ namespace Mollie\Payment\Model\Methods; -use Magento\Directory\Helper\Data as DirectoryHelper; use Magento\Framework\Api\AttributeValueFactory; use Magento\Framework\Api\ExtensionAttributesFactory; use Magento\Framework\App\Config\ScopeConfigInterface; @@ -18,7 +17,6 @@ use Magento\Payment\Helper\Data; use Magento\Payment\Model\Method\AbstractMethod; use Magento\Payment\Model\Method\Logger; -use Mollie\Payment\Model\Mollie; /** * Class Reorder @@ -71,8 +69,7 @@ public function __construct( RequestInterface $request, AbstractResource $resource = null, AbstractDb $resourceCollection = null, - array $data = [], - DirectoryHelper $directory = null + array $data = [] ) { $this->request = $request; @@ -86,8 +83,7 @@ public function __construct( $logger, $resource, $resourceCollection, - $data, - $directory + $data ); }