2

Magento 2 allows me to create multiple invoices for a order that has already been invoiced(using REST Api) which should not be the case.

So, if anyone can give a light to this part, it will be helpful.

  • 1
    Why shouldnt it be the case? It was already like this in Magento 1 – Raphael at Digital Pianism May 18 '16 at 9:26
  • Lets say, u have ordered one item and for that one invoice will be created right. Thats the correct way.But when you are using REST Api for creating invoice for a particular order, it is allowing to create multiple which should not be case right?My question was bit wrong, sorry for that. – Arijit De May 23 '16 at 8:51
  • Oh you mean you're able to invoice an order with one item that has already been invoiced ? – Raphael at Digital Pianism May 23 '16 at 8:51
  • Yes exactly using API not through Magento 2 admin panel – Arijit De May 23 '16 at 8:52
1

I really had to brainstorm about it and later came up by creating a new custom module that will help to create invoice using api that will resolve your issue.

Considering You know how to create a new module.Start by creating a new module: Lets say we have a namespace: "ABC" and module "Customapi"

Inside ABC/Customapi/etc : create a webapi.xml and do the following

   <?xml version="1.0"?>
 <!--
 /**
 * Copyright © 2016 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */
 -->
 <routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Webapi:etc/webapi.xsd">
 <route url="/V1/customInvoices" method="POST">
    <service class="ABC\Customapi\Api\InvoiceRepositoryInterface" method="save"/>
    <resources>
         <resource ref="Magento_Sales::sales" />
    </resources>
   </route>

 <route url="/V1/customInvoices/:id" method="GET">
    <service class="ABC\Customapi\Api\InvoiceRepositoryInterface" method="get"/>
    <resources>
         <resource ref="Magento_Sales::sales" />
    </resources>
 </route>

 </routes>

This is where you declare your routes.

Now we need to declare the interface and its corresponding class containing the repository where we will be defining our methods.

So inside ABC/Customapi/etc : create a di.xml and do the following

<?xml version="1.0"?>
<!--
/**
* Copyright © 2016 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
   <preference for="ABC\Customapi\Api\InvoiceRepositoryInterface" type="ABC\Customapi\Model\Order\InvoiceRepository"/>

 </config>

Now we will be declaring the interface for our api. Under ABC/Customapi/Api : create InvoiceRepositoryInterface.php

<?php
/**
* Copyright © 2016 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
namespace ABC\Customapi\Api;

/**
* Order repository interface.
*
* An order is a document that a web store issues to a customer. Magento     generates a sales order that lists the product
* items, billing and shipping addresses, and shipping and payment  methods. A corresponding external document, known as
 * a purchase order, is emailed to the customer.
 * @api
  */
 interface InvoiceRepositoryInterface
 { 
/**
 * Loads a specified invoice.
 *
 * @param int $id The invoice ID.
 * @return \Magento\Sales\Api\Data\InvoiceInterface Invoice interface.
 */
public function get($id);


/**
 * Performs persist operations for a specified invoice.
 *
 * @param \Magento\Sales\Api\Data\InvoiceInterface $entity The invoice.
 * @return \Magento\Sales\Api\Data\InvoiceInterface Invoice interface.
 */
 public function save(\Magento\Sales\Api\Data\InvoiceInterface $entity);
 }

All required methods for the API are called in this file which are defined in the InvoiceRepository.php file under ABC\Customapi\Model\Order.

So our InvoiceRepository.php will have all the methods that will be called accordingly, based on the api route.

 public function get($id)
{
    if (!$id) {
        throw new \Magento\Framework\Exception\InputException(__('ID required'));
    }
    if (!isset($this->registry[$id])) {
        $entity = $this->_objectManager->get('Magento\Sales\Model\Order\Invoice')->load($id);
        if (!$entity->getEntityId()) {
            throw new NoSuchEntityException(__('Requested entity doesn\'t exist'));
        }
        $this->registry[$id] = $entity;
    }
    return $this->registry[$id];
}

 public function save(\Magento\Sales\Api\Data\InvoiceInterface $entity)

{

    $invoiceItems=array();

    foreach ($entity->getItems() as $val){
        $quantity=$val->getQty();
        $orderItemId=$val->getOrderItemId();
        $invoiceItems[$orderItemId]=$quantity;
    }       
    $orderId=$entity->getOrderId();

    $order = $this->_objectManager->get('Magento\Sales\Model\Order')->load($orderId);


    if (!$order->getId()) {
        throw new \Magento\Framework\Exception\LocalizedException(__('The order no longer exists.'));
    }

    if (!$order->canInvoice()) {
        throw new \Magento\Framework\Exception\LocalizedException(
                __('The order does not allow an invoice to be created.')
        );
    }

    $invoice = $this->invoiceService->prepareInvoice($order, $invoiceItems);

    if (!$invoice) {
        throw new \Magento\Framework\Exception\LocalizedException(__('We can\'t save the invoice right now.'));
    }

    if (!$invoice->getTotalQty()) {
        throw new \Magento\Framework\Exception\LocalizedException(
                __('You can\'t create an invoice without products.')
        );
    }


     $invoice->register();
    $invoice->getOrder()->setIsInProcess(true);

    $transactionSave = $this->_objectManager->create(
            'Magento\Framework\DB\Transaction'
    )->addObject(
            $invoice
    )->addObject(
            $invoice->getOrder()
    );

    $transactionSave->save();

    $incrementId = $invoice->getIncrementId();

    $invoiceId = $invoice->getEntityId();

     return $this->get($invoiceId);

}

Hope this will work for you as well. Do comment if you have any query

  • When I use POST V1/customapi,it show error in exception.log Message: Warning: Invalid argument supplied for foreach() in \ABC\Customapi\Model\Order\InvoiceRepository.php. – Lovely Setia Sep 14 '18 at 8:04
  • If I created invoice using /V1/customInvoices,why "V1/order/:order_id/invoice" generate invoice? – Lovely Setia Sep 17 '18 at 9:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.