Order Export & Shipping Fulfillment Script For Magento, Version II

back to tech articles
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&amp;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('&amp;','and',str_replace('&amp;','and',$val)).'&amp;';
    }

    // 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!