The Root Problem
The root of all this is actually pretty straightforward - the store has free downloadable software products, and they need invoices to be created for them when they're ordered. I've turned on this option for the Zero Subtotal payment method, and the invoices are being created as expected. However, emails weren't being sent for them, as they would for any other invoice. To fix this, I made a simple event observer on the sales_order_invoice_save_after
event, which just checks to see if $order->getPayment()->getMethodInstance()->getCode()
== 'free', and if so, call $invoice->sendEmail()
. It now sends the emails.
However, for other payment methods, the invoice email has a list of the downloadable files with their links included with the Item's other options. For emails sent by the method described above, the title for this shows up (e.g. "Downloads", in our case), but that's all. If I do exactly the same order again, but change the product's price to $1 and pay for it, it works fine.
My Futile Attempts
For reference, I'm on Magento Enterprise 1.12.0.2.
First, I thought maybe the email was just being generated and sent too early in the process, before the links had been made active. To try and fix that, I tried changing the event that the observer was called for. I didn't think to save a list of ones I tried, but I did pretty much anything related to the payment or invoice that seemed relevant, and all produced the same result - no worse (the email was still sent, etc), but no better (still missing the downloadable links).
Second, I thought maybe I'd just be a little more aggressive with it, and actually make the links active myself right before sending the email, using this:
function make_links_available($order) {
foreach ($order->getAllItems() as $item) {
if (
$item->getProductType() == Mage_Downloadable_Model_Product_Type::TYPE_DOWNLOADABLE ||
$item->getRealProductType() == Mage_Downloadable_Model_Product_Type::TYPE_DOWNLOADABLE
) {
$linkPurchased = Mage::getResourceModel('downloadable/link_purchased_item_collection')
->addFieldToFilter('order_item_id', array('in' => array($item->getId()) ) );
foreach ($linkPurchased as $link) {
if ($link->getStatus() != Mage_Downloadable_Model_Link_Purchased_Item::LINK_STATUS_EXPIRED) {
$link->setStatus(Mage_Downloadable_Model_Link_Purchased_Item::LINK_STATUS_AVAILABLE)->save();
}
}
}
}
}
I actually scavanged this from another script I wrote some time ago, and it could probably be done better, but for the moment I was just trying to see if that was the problem in the first place before worrying about that. Sadly though, it doesn't seen to have helped. The result is the same as before, no better, no worse.
Lastly, I've started exploring the templates and whatnot to try and find out for sure what might be causing the problem, rather than making (hopefully educated) guesses, before wasting more time chasing red herrings. The template used in that section is contained in template/email/order/items.phtml
, where it looks over $_order->getAllItems()
, calling $this->getItemHtml($_item)
on each item.
I looked for the template that looked like it could be responsible for the actual HTML of the link itself, thinking a clue as to what's causing it could be nearby (some condition not being met, etc). I only found two candidates:
- app/design/frontend/base/default/template/downloadable/email/order/items/order/downloadable.phtml
- app/design/frontend/base/default/template/downloadable/email/order/items/invoice/downloadable.phtml
The relevant parts here appear to be the same, though:
<?php if ($links = $this->getLinks()->getPurchasedItems()): ?>
<dl style="margin:0; padding:0;">
<dt><strong><em><?php echo $this->getLinksTitle() ?></em></strong></dt>
<?php foreach ($links as $link): ?>
<dd style="margin:0; padding:0 0 0 9px;">
<?php echo $this->escapeHtml($link->getLinkTitle()); ?>
(<a href="<?php echo $this->getPurchasedLinkUrl($link) ?>" style="color:#1E7EC8;"><?php echo Mage::helper('downloadable')->__('download') ?></a>)
</dd>
<?php endforeach; ?>
</dl>
<?php endif; ?>
The title returned by $this->getLinksTitle()
does appear in the emails, so ($links = $this->getLinks()->getPurchasedItems())
is true, but the foreach doesn't run - there appear to be no actual links returned by getPurchasedItems()
. I would have thought that would be taken care of when I was trying to force them to be set to active, but maybe that "Purchased" is more literal than I had thought? I would have thought that having an invoice, regardless of the price or total, would have been enough but I could be wrong.
In any case, at the moment I'm still digging, but thought I'd post this all so far in the hopes that someone has done this before and already has the answer here. Any help or insight is appreciated, even if it's just a nudge in the right direction.
Edit: I've found that if I load the order after everything's done (as in, I go through the process normally) and run a script that just loads the order, gets the invoice, and calls sendEmail()
on it, the email works - everything looks correct, and the links are included. I'm currently thinking maybe it really is a timing issue. I could probably just have a cronjob that, for these affected invoices, runs every minute and calls sendEmail()
on them, but that seems horribly sloppy.