Magento CE 1.7.0.2
There is a newer version of this article available here.
Magento is a fantastic eCommerce platform. The fact that the Community Edition is totally free still makes me scratch my head. However, when it comes to the order fulfilment part of Magento, it almost always requires a custom solution.
You may find an order fulfilment provider who has written an extension that you can plug-in, but it’s unlikely to be supported, up-to-date or very well documented. I’ve recently had to roll my own fulfilment script and I thought I would share it with all of you 🙂
It’s not perfect (a work in progress), but it will hopefully give a good starting point for your own fulfilment solution.
The code is heavily commented throughout. The script is placed in the root directory of Magento and is called via crontab. I have put a sample in the code for that too.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | <?php /** * @author Jason * @website https://twincreations.co.uk * * This script integrates with our order fulfilment provider. * It prepares all 'new' orders and puts them into an XML object. * It then sends the object to a cURL service provided by the * integration system, and upon receiving a 'success' message, * it updates the order status' to 'processing'. * */ // call this script with cron like so... (every hour at 55 past, from 6 to 6, so 06:55, 07:55, 08:55 etc... up to 18:55) // 55 6-18 * * * /var/www/html/export.php?secure=password // HAXZ, AFAIK!! secure the script so that cron can call it safely if ($_GET['secure']!="password") { header("Location: http://www.google.com/search?q=computer+crime&oq=computer+crime"); die("Access denied"); } // include the core code we are going to use require_once('app/Mage.php'); umask (0); Mage::app('default'); // resources $clientName = 'domain.com'; $orderids = array(); // working code below this line ############################### // get pending (new) orders $orders = Mage::getModel('sales/order')->getCollection()->addFieldToFilter('state', 'new'); // create new SimpleXMLElement object, so we can send an XML object via curl later on $poArray = new SimpleXMLElement(""); // iterate through the orders foreach ($orders as $order) { // order id $orderid = $order->getEntityId(); // add child element to the SimpleXML object $pOrder = $poArray->addChild('PurchaseOrder'); // addresses $shippingAddress = $order->getShippingAddress(); $billingAddress = $order->getBillingAddress(); // shipping method $shippingMethod = ($shippingAddress->getCountry_id() == 'GB') ? 'SHIP_UK' : 'SHIP_OS'; // Add attributes to the SimpleXML element $pOrder->addChild('CreatedBy', $clientName); $pOrder->addChild('CreatedOn', $order->getCreatedAt()); $pOrder->addChild('AmendedBy', $clientName); $pOrder->addChild('AmendedOn', $order->getUpdatedAt()); $pOrder->addChild('Campaign', $order->getQuoteId()); $pOrder->addChild('ContactNumber', $order->getCustomerId()); $pOrder->addChild('Title', $order->getCustomerPrefix()); $pOrder->addChild('Forename', $order->getCustomerFirstname()); $pOrder->addChild('Surname', $order->getCustomerLastname()); $pOrder->addChild('Company', $order->getCompany()); $pOrder->addChild('Email', $order->getCustomerEmail()); $pOrder->addChild('Phone', $order->getPhone()); $pOrder->addChild('Currency', $order->getOrderCurrencyCode()); $pOrder->addChild('OrderNumber', $orderid); $pOrder->addChild('DeliveryAddressNumber', $order->getShippingAddressId()); $pOrder->addChild('TaxAmount', $order->getTaxAmount()); $pOrder->addChild('DiscountTotal', $order->getDiscountAmount()); $pOrder->addChild('DeliveryAmount', $order->getShippingInclTax()); $pOrder->addChild('TotalAmount', $order->getGrandTotal()); $pOrder->addChild('DeliveryTitle', $shippingAddress->getPrefix()); $pOrder->addChild('DeliveryForename', $shippingAddress->getFirstname()); $pOrder->addChild('DeliverySurname', $shippingAddress->getLastname()); $pOrder->addChild('DeliveryCompany', $shippingAddress->getCompany()); $pOrder->addChild('DeliveryEmail', $shippingAddress->getEmail()); $pOrder->addChild('DeliveryPhone', $shippingAddress->getTelephone()); $pOrder->addChild('DeliveryAddress', $shippingAddress->getStreet(1)); $pOrder->addChild('DeliveryAddress2', $shippingAddress->getStreet(2)); $pOrder->addChild('DeliveryAddress3', $shippingAddress->getStreet(3)); $pOrder->addChild('DeliveryTown', $shippingAddress->getCity()); $pOrder->addChild('DeliveryCounty', $shippingAddress->getRegion()); $pOrder->addChild('DeliveryPostcode', $shippingAddress->getPostcode()); $pOrder->addChild('DeliveryCountry', $shippingAddress->getCountry_id()); $pOrder->addChild('BillingAddress', $billingAddress->getStreet(1)); $pOrder->addChild('BillingAddress2', $billingAddress->getStreet(2)); $pOrder->addChild('BillingAddress3', $billingAddress->getStreet(3)); $pOrder->addChild('BillingTown', $billingAddress->getCity()); $pOrder->addChild('BillingCounty', $billingAddress->getRegion()); $pOrder->addChild('BillingPostcode', $billingAddress->getPostcode()); $pOrder->addChild('BillingCountry', $billingAddress->getCountry_id()); $pOrder->addChild('ShipMethod', $shippingMethod); // add XML children $pItems = $pOrder->addChild('PurchaseItems'); $pItems->addAttribute('PurchaseOrderNumber', $orderid); // get all order items $items = $order->getItemsCollection(); // loop through the order items foreach ($items AS $itemid => $item) { $pItem = $pItems->addChild('PurchaseItem'); $pItem->addChild('CreatedBy', $clientName); $pItem->addChild('CreatedOn', $item->getCreatedAt()); $pItem->addChild('PurchaseItemNumber', $item->getProductId()); $pItem->addChild('PurchaseOrderNumber', $orderid); $pItem->addChild('ProductCode', $item->getSku()); $pItem->addChild('LineNumber'); $pItem->addChild('ProductName', $item->getName()); $pItem->addChild('ProductPrice', $item->getPrice()); $pItem->addChild('ProductVat', $item->getTaxAmount()); $pItem->addChild('ProductDiscount', $item->getDiscount()); $pItem->addChild('Quantity', $item->getQtyOrdered()); } // add the id to the order ids array $orderids[] = $orderid; } // check if we have orders, if not, don't make the curl request if (empty($orderids)) { $content = "Success"; } else { // prepare the curl data $data = array(); $data['xml'] = $poArray->asXML(); $data['submit'] = 'submit'; $post_str = ''; foreach ($data as $key=>$val) { $post_str .= $key.'='.str_replace('&','and',str_replace('&','and',$val)).'&'; } // send the object via curl $post_str = substr($post_str, 0, -1); $handle = curl_init("https://provider.com/".$clientName); curl_setopt($handle, CURLOPT_RETURNTRANSFER, 1); curl_setopt($handle, CURLOPT_POST, 1); curl_setopt($handle, CURLOPT_POSTFIELDS, $post_str); curl_setopt($handle, CURLOPT_SSL_VERIFYPEER, false); $content = curl_exec($handle); curl_close($handle); } // wait a second for results from curl, // surprisingly, this is often needed to prevent wierd errors with curl (depending on the provider) sleep(1); // result if (strpos($content,'Success') !== false) { // if we have any orders to process... if (empty($orderids)) { echo "Success :: No Orders"; } else { // change order status to processing foreach ($orderids AS $id) { $order = Mage::getModel('sales/order')->load($id); $order->setState(Mage_Sales_Model_Order::STATE_PROCESSING, true)->save(); } echo "Success :: Processed Orders"; } } else { echo "Error: ".$content.""; // send an email if this fails! $to_add = "[email protected]"; $from_add = "Order Export "; $subject = "Server Error"; $message = "Dear User, rnThe order export failed on domain.com with: ".$content; $message .= ".rnCron tried to run export on "; $message .= date("D, F jS, Y")." at ".date("H:i:s"); $message .= ". Please investigate the cause of this error ASAP. rnEnd."; $headers = "From: $from_add rn"; $headers .= "Reply-To: $from_add rn"; $headers .= "Return-Path: $from_addrn"; $headers .= "X-Mailer: PHP rn"; // send the email if (mail($to_add,$subject,$message,$headers)) { echo "Reporting error…"; } else { echo "Email could not be sent!"; } } ?> |
Ta-da! Any code improvements gratefully accepted!
Hopefully this will reduce the development cycle for your own fulfillment solution. I have built and tested this in Magento CE 1.7.0.2 only, but it should be backwards compatible to 1.5 at least. Happy shipping!
Edit
In order to use the PHP parsing engine and all that it calls (SimpleXML, etc) we need to call our script differently on some systems. I like wget but you could also use cURL, Lynx or just parse the script with php by calling the path.
I’ll just demonstrate using wget with cron (my preferred method):
1 | 55 6-18 * * * /usr/bin/wget -q -O /tmp/order_imports.txt http://mydomain.com/export.php?secure=password |
-q tells wget to perform the operation quietly and -O /tmp/order_imports.txt writes the script output to a temp file.
That’s better!