2

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!

4 Answers 4

3

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.

8
  • Thank you, Lee! Looks like a solid solution but unfortunately I get the following exception: "Can't retrieve entity config: sales/order_invoice_item"
    – infabo
    Jun 23, 2014 at 14:07
  • Sorry about that, I had a typo that should be fixed now. Jun 23, 2014 at 21:59
  • There is still an error but I don't know why. "You cannot define a correlation name 'oii' more than once"
    – infabo
    Jun 24, 2014 at 7:30
  • Seems like this one is called twice. My fault it seems.
    – infabo
    Jun 24, 2014 at 7:37
  • Okay, got that. But still not working. $item->getInvoiceId() is always empty. $item->debug() shows no "invoice_id" entry at all. Seems like the collection is not loaded correctly.
    – infabo
    Jun 24, 2014 at 8:02
4

Simply get the order from the order item $item->getOrder() and then grab invoices from there.

So:

$order = $item->getOrder();
if ($order->hasInvoices()) {
    $invIncrementIDs = array();
    foreach ($order->getInvoiceCollection() as $inv) {
        $invIncrementIDs[] = $inv->getIncrementId();
    }
}

Reference: http://ka.lpe.sh/2013/04/28/magento-get-all-invoices-and-shipments-of-an-order/

Following from your comments, this example will dump out the invoice information (all in one line probably isn't how you'd want it). 507 is the product I'm looking for in my instance. This would be replaced with the dynamic item product id you're looking for.

$incrementId = "1000000000"; //order number
$order = Mage::getModel('sales/order')->loadByIncrementId($incrementId);

foreach($order->getInvoiceCollection() as $inv) {
    var_dump($inv->getItemsCollection()->addAttributeToFilter("product_id", array("eq" => "507"))->getData());
}
5
  • Thanks for your reply, Douglas. Already got to that point, but: what if, the order has more than one invoice -> how can I determine, to which invoice the item belongs?
    – infabo
    Jun 23, 2014 at 11:01
  • Hi Infabo, thanks for clarifying. In which case, the only thing I could think of doing would be to call ->getAllItems() on the invoice instance ($inv) and then check to see if you product is in there. Jun 23, 2014 at 12:29
  • Infact, getInvoiceCollection should be filterable, so how about adding an addAttributeToFilter to it? Jun 23, 2014 at 12:31
  • good idea! I'll check on this.
    – infabo
    Jun 23, 2014 at 12:38
  • Updated my answer briefly. Jun 23, 2014 at 12:47
2
$order = Mage::getModel('sales/order')->loadByIncrementId($increment_id);
$items = $order->getItemsCollection();

$qtys = array(); //this will be used for processing the invoice

foreach($items as $item){
    $qty_to_invoice = x; //where x is the amount you wish to invoice
    <!-- please note that if you don't want to invoice this product, set this value to 0 -->
    $qtys[$item->getId()] = $qty_to_invoice;
    <!-- Note that the ->getId() method gets the item_id on the order, not the product_id -->
}

$invoice = Mage::getModel('sales/service_order', $order)->prepareInvoice($qtys);
<!-- The rest is only required when handling a partial invoice as in this example-->
$amount = $invoice->getGrandTotal();
$invoice->register()->pay();
$invoice->getOrder()->setIsInProcess(true);

$history = $invoice->getOrder()->addStatusHistoryComment(
    'Partial amount of $' . $amount . ' captured automatically.', false
);

$history->setIsCustomerNotified(true);

$order->save();

Mage::getModel('core/resource_transaction')
    ->addObject($invoice)
    ->addObject($invoice->getOrder())
    ->save();
$invoice->save();
$invoice->sendEmail(true, ''); //set this to false to not send the invoice via email

I am still unsure but hopefully this is what you are looking for:

$order = Mage::getModel('sales/order')->loadByIncrementId($increment_id);
$items = $order->getAllItems();

$qtys = array(); //this will be used for processing the invoice

foreach($items as $item){
    if ($item->getSku() == 'SKU1') {
        $qty_to_invoice = $item->getQtyOrdered(); // now gets order quantity of item
    } else {
        $qty_to_invoice = 0;
    }

    $qtys[$item->getId()] = $qty_to_invoice;
    <!-- Note that the ->getId() method gets the item_id on the order, not the product_id -->
}

$invoice = Mage::getModel('sales/service_order', $order)->prepareInvoice($qtys);
1
  • Thanks for your answer. Unfortunately I may have been unclear in my question. I'm going to redefine my question.
    – infabo
    Jun 23, 2014 at 12:20
0

Use following code to get invoice id

$order = Mage::getModel('sales/order')->load($orderID);
if ($order->hasInvoices()) {
    $invIncrementIDs = array();
    foreach ($order->getInvoiceCollection() as $inv) {
        $invIncrementIDs[] = $inv->getIncrementId();
    //other invoice details...
    } Mage::log($invIncrementIDs);
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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