I recently had a need to resend Magento order confirmation emails for orders placed within a particular time period. Magento provides an interface for resending these emails out of the box (navigate to Sales → Orders → Order and click 'Send Email' up in the top right), but this takes 15-20 seconds per order at best. If you have more than a few of these order emails to resend, this approach will quickly become tedious. Luckily, there's an alternative: it is possible to write a Magento script to do the job for you, which I'll explore in this blog post.
If, for example, we wanted to resend the order emails for the first 100 orders (e.g. order increments 100000001 through 100000101), we would run something like this:
<?php
define("MAGE_BASE_DIR", "/var/www/magento");
require_once MAGE_BASE_DIR . "/app/Mage.php";
Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID);
$orderIncrements = range(100000001, 100000101);
foreach ($orderIncrements as $orderIncrement) {
$order = Mage::getModel("sales/order")->loadByIncrementId($orderIncrement);
if ($order->getId()) {
try {
$order->sendNewOrderEmail();
echo "Order $orderIncrement successfully sentn";
} catch (Exception $e) {
echo $e->getMessage();
}
} else {
echo "Order $orderIncrement not foundn";
}
sleep(2);
}
The first few lines allow our script access to the Magento environment, which makes it possible to use Mage::getModel()
later on, and take action on the Magento orders:
define("MAGE_BASE_DIR", "/var/www/magento");
require_once MAGE_BASE_DIR . '/app/Mage.php';
Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID);
Note: You'll need to update MAGE_BASE_DIR
to point to your own Magento directory, or the script will fail at Mage::app()
.
Since we know that we just want the first 100 orders, ranging from 100000001 through 100000101, we write it this way. Alternatively, we could combine several sets of order increments like this:
$orderIncrements = array_merge(
range(100000001, 100000101),
range(200000001, 200000101),
range(300000001, 300000101)
);
Next, we simply iterate over the list of order increments and retrieve the corresponding order objects, one by one. We also want to print out an error message if the order cannot be found, so that we've got a record of exactly what happened after the script is finished.
foreach ($orderIncrements as $orderIncrement) {
$order = Mage::getModel('sales/order')->loadByIncrementId($orderIncrement);
if ($order->getId()) {
} else {
echo "Order $orderIncrement not found\n";
}
}
If this script was intended to be used more than once, or in an automated process, it would be more appropriate to log errors to file, but for this particular situation, simply echoing any errors is an acceptable option.
To determine how to send the order emails, we look at the core modules. In the emailAction
method of Mage_Adminhtml_Sales_OrderController
, which is called when you click the "Send Email" button, the $order->sendNewOrderEmail() method is called to handle emails. Chances are high that we can do the same thing (though in other situations you may need to do some extra initialisation to put your script into the correct state):
try {
$order->sendNewOrderEmail();
echo "Order $orderIncrement successfully sent\n";
} catch (Exception $e) {
echo $e->getMessage();
}
We also record any errors or successes, echoing either result, as we do when the order cannot be found.
Finally, we want to avoid filling up the mail queue too quickly, so we'll introduce a short (2-second) delay between each iteration of the script:
sleep(2);
The finished script can be run with php -f filename.php
, and might take something like 2-3 seconds per order to run (including the 2-second delay), and in the absence of any errors, it won't require any manual intervention. Quite an improvement.
And that's just one possibility. You could also adjust this script slightly to send other order-related emails. For example, with the following changes, a shipment will be generated for each of the selected orders and shipment notices will be sent to the respective customers:
<?php
define("MAGE_BASE_DIR", "/var/www/magento");
require_once MAGE_BASE_DIR . "/app/Mage.php";
Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID);
$orderIncrements = range(100000001, 100000101);
foreach ($orderIncrements as $orderIncrement) {
$order = Mage::getModel(
In this instance the shipment will be created to contain all of the products, so this particular script can only be run against an order once. Details of the shipment will show up in the Shipments tab of the order screen.
Finally, the next variation will generate invoices and send corresponding emails:
<?php
define("MAGE_BASE_DIR", "/var/www/magento");
require_once MAGE_BASE_DIR . "/app/Mage.php";
Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID);
$orderIncrements = range(100000001, 100000101);
foreach ($orderIncrements as $orderIncrement) {
$order = Mage::getModel("sales/order")->loadByIncrementId($orderIncrement);
if ($order->getId()) {
try {
$invoice = $order->prepareInvoice();
$invoice->register();
$invoice->setEmailSent(true);
$invoice->getOrder()->setIsInProcess(true);
$transactionSave = Mage::getModel("core/resource_transaction")
->addObject($invoice)
->addObject($invoice->getOrder())
->save();
$invoice->sendEmail(true, "");
echo "Invoice generation for order $orderIncrement was successfuln";
} catch (Exception $e) {
echo $e->getMessage();
}
} else {
echo "Order $orderIncrement not foundn";
}
sleep(2);
}
These examples demonstrate how you might programatically resend the main transactional emails related to an order, and of course it should be possible to take a similar approach for any other transactional emails that might need to be resent.
Magento Compatibility
Post originally written for Magento version: | 1.4.0.1 |
Tested with Magento versions: | 1.3.2.4, 1.4.0.1 |