Order Export & Shipping Fulfillment Script For Magento, Version III

back to tech articles
Magento CE 1.7.0.2

I deserve a hot chocolate for this, or at least an apple danish 🙂 After some further refinements and tweaks, here is version 3 of the order fulfilment script I first posted in January of this year.

This script will collect all orders in the Magento database with a status of either Pending (card payments) or Processing (PayPal) and pass them to an order fulfilment provider or warehouse to be picked, packed and shipped.

I use a separate script for updating the orders when they are despatched. I will post that soon and put the link here. In the meantime, this script does the following:

  • Collects all orders with a status of Pending or Processing.
  • Collects the items in each order, including quantity.
  • Collects customer information, such as name, email, phone numbers.
  • Collects billing and shipping addresses.
  • Collects shipping method details.
  • Simple password protection prevents misuse.
  • Updates the order status to Processing once it has been received by the provider.
  • Called via cron.
  • The code is very well commented throughout.

This information is then used to build an XML object which it sends to the order fulfilment provider (your warehouse) for processing. This is done via cURL and uses the PHP extension SimpleXML (standard in PHP 5.3.3 AFAIK). It’s not perfect, but it saves us hours every day and it works a treat.

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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
<?php
/**
 * @author      Jason <jason@twincreations.co.uk>
 * @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/exportorders.php?pword=53cur3

// secure the script so that cron can call it safely
if ($_GET['pword']!="53cur3") {
  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
define('MAGENTO_ROOT', getcwd());
$mageFilename = MAGENTO_ROOT . '/app/Mage.php';

require_once($mageFilename);
umask(0);
Mage::app();

// resources
$clientName = 'twincreate';
$orderIds = array();
$theDate = date('D, F jS, Y')." at ".date('H:i:s');
$orderStatuses = array('pending','processing');
$orderDespatchMsg = 'Automatically sent order for fulfilment.';

// working code below this line
###############################

// get pending orders
$orders = Mage::getModel('sales/order')->getCollection()->addFieldToFilter('status', array('in' => $orderStatuses));

// create new SimpleXMLElement object, so we can send an XML object via curl later on
$poArray = new SimpleXMLElement("<ArrayOfPurchaseOrder></ArrayOfPurchaseOrder>");

// function to remove accents
function stripAccents($stripAccents){
  return strtr($stripAccents,'àáâãäçèéêëìíîïñòóôõöùúûüýÿÀÁÂÃÄÇÈÉÊËÌÍÎÏÑÒÓÔÕÖÙÚÛÜÝ','aaaaaceeeeiiiinooooouuuuyyAAAAACEEEEIIIINOOOOOUUUUY');
}

##########
# ITERATE
##########

foreach ($orders as $order) {
  // order details
  $orderId = $order->getEntityId();
  $orderState = $order->getState();
  $orderPayMethod = $order->getPayment()->getMethodInstance()->getTitle();
  $orderComments = $order->getAllStatusHistory();
  $processCurrentLoop = 'yes';
 
  // addresses
  $shippingAddress = $order->getShippingAddress();
  $billingAddress = $order->getBillingAddress();
 
  // shipping method
  if (strpos($order->getShippingDescription(),'UK Next Day') !== false) {
    $shippingMethod = 'DPD_UK';
  } else {
    $shippingMethod = ($shippingAddress->getCountry_id() == 'GB') ? 'STANDARD_UK' : 'STANDARD_OS';
  }
 
  // telephone number
  $orderPhoneNumber = (strlen($shippingAddress->getTelephone()) > 1) ? $shippingAddress->getTelephone() : $order->getPhone();
 
  // we don't want to re-send orders for fulfilment ;)
  foreach ($orderComments as $comment) {
    $body = $comment->getData('comment');
    if (strpos(strtolower($body),strtolower($orderDespatchMsg)) !== false) {
      // exit the foreach loop(s) (for this iteration)
      $processCurrentLoop = 'no';
    }
  }
 
  // don't proess this loop if it has been processed
  if ($processCurrentLoop == 'yes') {
 
    ##################
   # BUILD XML OBJECT
   ##################
 
    // add child element to the SimpleXML object
    $pOrder = $poArray->addChild('PurchaseOrder');
 
    $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('UserID', 'twincreate');
    $pOrder->addChild('Password', 'password');
    $pOrder->addChild('ContactNumber', $order->getCustomerId());
    $pOrder->addChild('Title', $order->getCustomerPrefix());
    $pOrder->addChild('Forename', stripAccents($order->getCustomerFirstname()));
    $pOrder->addChild('Surname', stripAccents($order->getCustomerLastname()));
    $pOrder->addChild('Company', $order->getCompany());
    $pOrder->addChild('Email', $order->getCustomerEmail());
    $pOrder->addChild('Phone', $order->getPhone());
    $pOrder->addChild('Currency', $order->getOrderCurrencyCode());
    $pOrder->addChild('ContactHistoryNumber');
    $pOrder->addChild('CurrentState', 'Confirmed');
    $pOrder->addChild('PurchaseOrderNumber', $orderId);
    $pOrder->addChild('DeliveryDate');
    $pOrder->addChild('BatchNumber');
    $pOrder->addChild('TransactionNumber', $orderId);
    $pOrder->addChild('DeliveryAddressNumber', $order->getShippingAddressId());
    $pOrder->addChild('BillingAddressNumber');
    $pOrder->addChild('IsGift');
    $pOrder->addChild('TaxAmount', $order->getTaxAmount());
    $pOrder->addChild('DiscountTotal', $order->getDiscountAmount());
    $pOrder->addChild('DeliveryAmount', $order->getShippingInclTax());
    $pOrder->addChild('DonationAmount', '0');
    $pOrder->addChild('TotalAmount', $order->getGrandTotal());
    $pOrder->addChild('MarketingInfo');
    $pOrder->addChild('ThankingCode');
    $pOrder->addChild('EmailCert');
    $pOrder->addChild('IsTest');
    $pOrder->addChild('DeliveryTitle', $shippingAddress->getPrefix());
    $pOrder->addChild('DeliveryForename', stripAccents($shippingAddress->getFirstname()));
    $pOrder->addChild('DeliverySurname', stripAccents($shippingAddress->getLastname()));
    $pOrder->addChild('DeliveryCompany', $shippingAddress->getCompany());
    $pOrder->addChild('DeliveryEmail', $shippingAddress->getEmail());
    $pOrder->addChild('DeliveryPhone', $orderPhoneNumber);
    $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('DeliveryMessage');
    $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('ContactMethod', 'WEBS');
    $pOrder->addChild('ShipMethod', $shippingMethod);
 
    $pItems = $pOrder->addChild('PurchaseItems');
    $pItems->addAttribute('PurchaseOrderNumber', $orderId);
 
    // get all 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;
  }
}

##############
# CURL REQUEST
##############

// check if we have orders, if not, don't make the curl request
if (empty($orderIds)) {
  exit("Success :: No Orders (".$theDate.").");
} else {
  // prepare the curl data
  $data = array();
  $data['OrderXML'] = $poArray->asXML();
  $data['submit'] = 'submit';
  $post_str = '';

  foreach ($data as $key=>$val) {
    $post_str .= $key.'='.str_replace('&','and',str_replace('&amp;','and',$val)).'&';
  }

  // send the object via curl
  $post_str = substr($post_str, 0, -1);
  $handle = curl_init("https://fulfilment.co.uk/".$clientName."/XMLOrder.js?client=".$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);
}

// be nice - wait a second for results from curl
sleep(1);

#########
# RESULT
#########

if (strpos(strtolower($content),'success') !== false) {
  // change order status to processing
  foreach ($orderIds AS $id) {
    $order = Mage::getModel('sales/order')->load($id);
    $order->setIsInProcess(true);
    $order->addStatusHistoryComment($orderDespatchMsg, false);
    // update order status
    if ($order->getState() != Mage_Sales_Model_Order::STATE_PROCESSING){
      $order->setState(Mage_Sales_Model_Order::STATE_PROCESSING, true);
    }
    $order->save();
    echo "Success :: Order No. ".$id." Submitted ".$theDate.".
"
;
  }
  exit("Success :: Orders Processed (".$theDate.").");
} else {
  echo "Error: ".$content."<p></p>";
 
  // send an email if this fails!
  $to_add = "jason@twincreations.co.uk";
  $from_add = "Support <support@twincreations.co.uk>";
  $subject = "Server Error";
 
  $message = "Dear User, \r\nThe order export failed while sending to the fulfilment provider on twincreations.co.uk with message: ".$content;
  $message .= ".\r\nCron 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. \r\nEnd.";
 
  $headers = "From: $from_add \r\n";
  $headers .= "Reply-To: $from_add \r\n";
  $headers .= "Return-Path: $from_add\r\n";
  $headers .= "X-Mailer: PHP \r\n";
 
  // send the email
  if (mail($to_add,$subject,$message,$headers)) {
    echo "Reporting error...";
  } else {
    echo "Email could not be sent!";
  }
  exit();
}
?>

The code is very procedural and could be tidied up a lot (remove comments, etc). Don’t forget, wherever you put this script, add it to your disallow list in the robots file!!

Happy shipping, until version IV! Comments or improvements gratefully accepted.

Leave a Reply

Your email address will not be published. Required fields are marked *