5
votes

We've got some API integrations that will periodically create shipments for orders.

What I'd like to do is create an observer to also create an appropriate invoice & capture payment when this shipment is created. I have this tied to sales_order_shipment_save_after:

public function autoInvoice($observer){

    $shipment = $observer->getEvent()->getShipment();
    $order = $shipment->getOrder();

    $items = $shipment->getItemsCollection();

    $qty = array();

    foreach($items as $item)
        $qty[$item['order_item_id']] = $item['qty'];

    $invoice = Mage::getModel('sales/order_invoice_api');

    $invoiceId = $invoice->create($order->getIncrementId(), $qty);

    $invoice->capture($invoiceId);

}

(The code for the actual capture is somewhat naive, but bear with me.)

What's strange is that this code works just fine -- the shipment is created, the invoice is created and marked as 'Paid.' However, the order itself stays in limbo and retains a status 'Pending.'

Looking into it further, the items on the order itself have the correct quantities for both Ordered and Shipped, but there's no listing of the quantity Invoiced. I think this is what's causing the status hangup. It's as though the qty_invoiced on the sales_order_item table is getting reverted somehow.

Again, the Invoice shows the right items, so I'm quite confused here.

Any ideas?

2
I give up! I've looked at this problem for two days and decided to take a different approach. My code seems fine; I think it's the order of operations that might have been causing this to go awry. So instead of using an observer, I just overrode the shipment creation API to include a little code for auto-invoicing. munyah's code below did the trick. If anyone can find a cleaner way to accomplish this via observers, I'd love to hear it. Thanks everyone!bahoo

2 Answers

2
votes

This is indeed a very interesting one @bahoo.

maybe try:

$shipment = $observer->getEvent()->getShipment();
$order = $shipment->getOrder();

$qty = array();

$invoice = Mage::getModel('sales/order_invoice_api');
$invoiceId = $invoice->create($order->getIncrementId(), $qty);

$invoice->capture($invoiceId);
2
votes

After lots of testing using the API, I found if I created the invoice first, then the shipment, Magento Enterprise 1.13.01. would correctly set the order status to Complete. Trying to make the shipment first, then the invoice, would result in the Pending Order status remaining even if all items had been invoiced and shipped.

The bridging system code below uses information about orders placed in Magento, routed to the bridging system via an Observer on checkout_submit_all_after, sent to NetSuite via web services, and fulfilled in NetSuite. The bridging system gets sales order fulfillments from NetSuite via web service, and saves items shipped, package, and tracking information, to use in the code below. Code shows creating invoice, then shipment and tracking.

Note that while testing, I just saw Magento incorrectly create an invoice for all three items in an order, even though it was passed an array that just contained two of the items. Magento did correctly create a shipment record for just the two items. Puzzling that the invoice API and the shipment API when passed the same array of items and quantities have such different behavior. Anyone else seen this?

        $proxy = new SoapClient($proxyUrl); /* V2 version */
        $sessionId = $proxy->login($apiUser, $apiKey); 

        try 
        {   /* try to create invoice in Magento */
            $invoiceIncrementId = $proxy->salesOrderInvoiceCreate($sessionId, $orderIncrementId, $shipItemsQty, 'invoice created', false, false); 
        } 
        catch( SoapFault $fault ) 
        {
            $error = $fault->getMessage(); /* will return 'Cannot do invoice for order' if invoice already exists for these items */        
        }

        if (!stristr($error,'Cannot do invoice') and !empty($error))
        {  /* some other invoicing problem, log what returned, on to next order */
            $ins = "insert into order_error_log values(NULL, ".$orderId.", '".date("Y-m-d H:i:s")."', '".$program."', '".$error."')";
            $result = $mysqli->query($ins); 
            $upd = "update orders set orderStatusId = ".ERROR_ADDING_MAGENTO_INVOICE.",
                dateStatusUpdated = '".date("Y-m-d H:i:s")."'
                where id = ".$orderId;
            $result = $mysqli->query($upd);
            continue;   
        }

        if ((stristr($error,'Cannot do invoice') or empty($error)) and $complete)
        {   /*  if all fulfilled, may change status */
            $upd = "update orders set orderStatusId = ".STORE_INVOICE_CREATED.",
                        dateStatusUpdated = '".date("Y-m-d H:i:s")."'
                        where id = ".$orderId;
            $result = $mysqli->query($upd);
        }

        /* send Magento salesOrderShipmentCreate and get returned shipment Id, re-using proxy login and session */
        $comment = 'Fulfillment(s) shipped on: '.$netsuiteShipDate;

        try 
        {   /* returns value such as string(9) "100002515" */
            $shipmentIncrementId = $proxy->salesOrderShipmentCreate($sessionId, $orderIncrementId, $shipItemsQty, $comment);
        }
        catch( SoapFault $fault ) 
        {
            $error = $fault->getMessage().': SOAP error received when trying to add shipment to Magento for morocco order id '.$orderId.
                ' store order id '.$orderIncrementId.' with items qty array of: '.var_dump($itemsQty);
            $ins = "insert into order_error_log values(NULL, ".$orderId.", '".date("Y-m-d H:i:s")."', '".$program."', '".$error."')";
            $result = $mysqli->query($ins);
            $upd = "update orders set orderStatusId = ".MAGENTO_SOAP_EXCEPTION.",
                dateStatusUpdated = '".date("Y-m-d H:i:s")."'
                where id = ".$orderId;
            $result = $mysqli->query($upd);
            continue;  /* on to next order */
        }

        /*  Using that shipmentId, send info re each package shipped for these fulfillments to Magento via salesOrderShipmentAddTrack. */   
        foreach ($packageIds as $packageId => $package)
        {
            try 
            {
                $trackingNumberId = $proxy->salesOrderShipmentAddTrack($sessionId, $shipmentIncrementId, 
                    $package['carrier'], 'tracking number', $package['trackNumber']);
            }
            catch( SoapFault $fault ) 
            {
                $error = $fault->getMessage().': SOAP error received when trying to add tracking number '.$package['trackNumber'].' to 
                    Magento for morocco order id '.$orderId.' store order id '.$orderIncrementId;;
                $ins = "insert into order_error_log values(NULL, ".$orderId.", '".date("Y-m-d H:i:s")."', '".$program."', '".$error."')";
                $result = $mysqli->query($ins);
                $upd = "update orders set orderStatusId = ".MAGENTO_SOAP_EXCEPTION.",
                    dateStatusUpdated = '".date("Y-m-d H:i:s")."'
                    where id = ".$orderId;
                $result = $mysqli->query($upd);
                continue;
            }
        }