How to Get Invoice from Order Item in Magento

ce-1.7.0.2invoiceprogrammaticallysales-order

Is it possible to get the invoice from an order item?

To be more specific:
I'm iterating the order ItemsCollection and if the order-item has been invoiced

$item->getStatusId() == Mage_Sales_Model_Order_Item::STATUS_INVOICED 

I'd like to get the corresponding invoice for it. Especially the invoice ID.

Has anyone a solution for this?

Thank you!

edit:
I'm iterating an Order-Item-Collection. If an order-item has been already invoiced, then I would like to look up, in which invoice it has been invoiced. If the order item has not been invoiced – I leave it as is.
It is just the opposite way of iterating an invoice item collection to look up the corresponding order. There I would just say

$invoiceItem->getOrder()->getId();

and I would be perfectly done.
It may seems ridiculous to do it the opposite way (looking up an invoice for an order item instead of iterating the invoice collection of the specific order), but I'd like to avoid refactoring the whole logic.

edit:

Thanks to Lee Saferite I have found the solution for my task:

$orderItems = $order->getItemsCollection();
try {
    if (!$orderItems->hasFlag('invoice_join')){
        $orderItems->getSelect()->joinLeft(
            array('oii' => $orderItems->getTable('sales/invoice_item')),
            'main_table.item_id = oii.order_item_id',
            array('invoice_id' => 'parent_id')
        );
        $orderItems->getSelect()->group('main_table.item_id');
        $orderItems->clear();
        $orderItems->setFlag('invoice_join');
    }
}
catch(Exception $e){
    Mage::logException($e);
}

Due to the fact, that I'm calling this code inside a big foreach loop (I'm just extending an existing module – had to deal with it) I set a flag, to avoid the problem, that the join get's applied multiple times (which would end in an exception). Thanks Lee!

Best Answer

There isn't a built-in way to do this, but there are many paths to the information you want.

This is one of the many ways to do this.

/** @var Mage_Sales_Model_Order $order */
$order = Mage::getModel('sales/order')->load($orderId);

/** @var Mage_Sales_Model_Resource_Order_Item_Collection $items */
$items = Mage::getSingleton('sales/order_item')->getCollection()->setOrderFilter($order);
$items->getSelect()->joinLeft(
    array('oii' => $items->getTable('sales/invoice_item')),
    'main_table.item_id = oii.order_item_id',
    array('invoice_id' => 'parent_id')
);
$items->getSelect()->group('main_table.item_id');

foreach ($items as $item) {
    /** @var Mage_Sales_Model_Order_Item $item */
    $item->setOrder($order);
    if ($item->getInvoiceId()) {
        // Invoiced and this is the invoice number
    } else {
        // Not invoiced (or missing invoice item)
    }
}

You are basically left-joining the invoice items to the order items so you can add the parent_id column from the invoice item as 'invoice_id' when you hydrate the order item record. The 'invoice_id' data will now exist on the order item directly. If there was no matching invoice item then the 'invoice_id' will be null.

I had to directly access the select object as Magento collections do not support a joinLeft directly and if you use the join method on the collection you are doing an inner join which would exclude any order items without matching invoice items.

Lastly, I used the group method on the select to prevent the possibility, however remote, that you get duplicate order items due to the join.

Edit: I updated the way the item collection is gathered because the getItemCollection method on an order is braindead and iterates the order items internally before returning, preventing you from making changes to the collection SQL without doing a reset.

Related Topic