Search This Blog

May 22, 2011

Creating Batch Shipment in Magento via API

How the process goes?

I successfully created a daily order importation script from Magento database to the fulfillment center last week as part of client requirements. The script dumped a csv file containing daily orders and passed it in fulfillment in a daily basis. Since it is a one way process, client also want me to create a script which will update the order status based on the fulfillment confirmation file. The fulfillment is sending back an confirmation file which formated in comma separated value (csv) that contains completed orders. Base on that file, the script will update the order status in Magento automatically(of course by setting up a cron job).

Preparing the script

The whole process need two files:
1. Confirmation file (csv or a text file)
2. php script

Confirmation file – it comma separated format, where order and tracking numbers is include.
100000222,8.8200,2.0,2010-09-22 14:40:02.000,01805213907832456279,upsprt
The first line is your csv header, and next lines onward are their values.
php script - a script which will do the process. The script use Magento api.

Step 1 - create a database connection

function sql_connect($server_host, $mysql_user, $mysql_password,$own_db){
$link = mysql_connect($server_host, $mysql_user, $mysql_password);
if (!$link) {
  die('Could not connect: ' . mysql_error());
 $db_selected = mysql_select_db($own_db, $link);
 if (!$db_selected) {
   die ('Can\'t use ' . $own_db . ' : ' . mysql_error());
  echo 'Connect Successfully'." <br>"; 

Step 2 - Call the sql_connect function and replace the credentials variable with the correct one.

* prepare credentials - 
* then connect through sql_connect function

$server_host  = '';

$sql_username = '';
$sql_password = '';
$own_db    = '';

sql_connect($server_host, $sql_username,$sql_password,$own_db);

Step 3 - include the mage.php in order to use Magento api

* include the mage.php file and the app() class
* set default value for email and comment

require_once("../store/app/Mage.php");  // please change with correct path...


$comment     = null;
$email      = false;
$includeComment = false;

Step 4 - make sure you have standard naming for your csv file.



Notice that the name is change only in date. In that case you can apply the following code. The code is setup to get yesterdays order.
* get the date of the yesterday orders
* this is the basis of the order file name
* shipment.$last_order is the standard name of csv file.

$last_order = date('Ymd', strtotime('yesterday'));

$source = "shipment".$last_order.".csv";

Step 5 - do a looping for each order in the csv and create shipment for each order.

* get information from the csv file
* declare variables 
* look for the order no., tracking no., and method

$row = 1;
$row_info  = array();

if (($handle = fopen($source, "r")) !== FALSE) {
    while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
        $num = count($data);
       if ($row > 1){
          for ($c=0; $c < $num; $c++) {
      switch ($c) {
         case 0:
             $row_info['invoice_no'] = $data[$c]; 
             $order_id = trim($row_info['invoice_no']);
         case 1:
             $row_info['ship_charge'] = $data[$c]; 
         case 2:
             $row_info['ship_weight'] = $data[$c]; 
         case 3:
             $row_info['ship_date'] = $data[$c]; 
          case 4:
             $row_info['tracking_no'] = $data[$c]; 
             $tracking_no = $row_info['tracking_no'];
         case 5:
             $row_info['ship_method'] = $data[$c]; 
                if (preg_match("/ups/", $row_info['ship_method']))
              $row_ship_method = "United Parcel Service";
              $row_carrier_code = "ups";
            if (preg_match("/USP/", $row_info['ship_method']))
              $row_ship_method = "United States Postal Service";
              $row_carrier_code = "usps";
      } //end of switch
          }  //end of for
          * query the customer email based on the order number
          * found on the csv file.
           $sql = "SELECT  sfo.customer_email FROM sales_flat_order as sfo WHERE sfo.increment_id = $order_id "; 
      $result = mysql_query($sql);
      $sfo_row = mysql_fetch_assoc($result);
      $email = $sfo_row['customer_email'];
      * magento api coding start
      * load the sales order model by $order_id defined in the csv
      * then evaluate if it can ship the order.
      $order = Mage::getModel('sales/order')->loadByIncrementId($order_id);
      $convertor = Mage::getModel('sales/convert_order');
      $shipment = $convertor->toShipment($order);
      * load all the items in specified order.
      * then ship it. 
      foreach ($order->getAllItems() as $orderItem) {
          if (!$orderItem->getQtyToShip()) {
          if ($orderItem->getIsVirtual()) {
          $item = $convertor->itemToShipmentItem($orderItem);
          $qty = $orderItem->getQtyToShip();
      } //end of foreach
      * prepare for adding the carrier code, title and number.
      * save the shipment and send an email confirmation.
      $data = array();
      $data['carrier_code'] = $row_carrier_code;
      $data['title']     = $row_ship_method;
      $data['number']    = $tracking_no;
      $track = Mage::getModel('sales/order_shipment_track')->addData($data);
      Mage::register('current_shipment', $shipment);
      $shipment->addComment($comment, $email && $includeComment);
      $transactionSave = Mage::getModel('core/resource_transaction')
      $send = $shipment->sendEmail($email, ($includeComment ? $comment : ''));
         echo "Sending of confirmation email failed.!";
         echo "Shipment for Order Number:" . $order_id . "is successfully created.";
         echo "can't create order";
     } //end of second if
  }  // end of while 
  echo "Shipping CSV Information not found!"; 
* send an email notification if shipment file was not found
* send this to
   $headers  = "From: \r\n";
   $headers .= "Reply-To: \r\n";
    $headers .= "MIME-Version: 1.0\r\n";
   $headers .= "Content-Type: text/html; charset=ISO-8859-1\r\n";
   $email_to ='' ; 
   $email_subject = "Shipping CSV Information Failure";
   $email_message  = $source .' Not Found!!! <br> <br><br>  CSV Information Failed!!! Please check the file in FTP directory <br><br> Directory : own/shipping <br><br><br><br>';
   $email_message .= 'Possible: Failure of uploading the file in specified directory. <br><br><br><br>Thank you!';

   $ok = mail($email_to, $email_subject, $email_message, $headers);

You can download the full script here. If there's any problem, please feel free to comment below


Ted Kendall said...

This is exactly what I was looking for...but unfortunately, it didn't work when I tried to implement it using the sample data created here.  Paths are correct, but I am still getting "Can't Create Order" even after "Connect Successfully" returns from the MySQL database connection.  Thank you for posting though!

Janzell Jurilla said...

Hi Ted,

Sorry for the late reply, The script is working for me well. Can't create order exist if there no match order number between your csv and magento database. Another reason maybe is that your order is already shipped. In that sense, canShip function didn't met the condition. 

Send me your files in my email -  - so I can look at it. 


Sean said...

PLEASE don't ever let this blog close down - or make sure this information is archived some where. This was 100% invaluable for the project I was working on. I had the csv going already, but i couldn't for the life of me figure out how to actually get the code to create the shipment. Filling in some of my code gaps with what you had here did the trick.

Many many many thanks for logging your code and opening it up for public consumption.

Janzell Jurilla said...

Thanks Sean. It's honor for me to help others.

- Janz

Sean said...

Just a FYI - tried running this again after it successfully worked, and received an error similar to: "Uncaught exception 'Mage_Core_Exception' with message 'Mage registry key "current_shipment" already exists'"

I added Mage::unregister('current_shipment'); just before the endwhile; and all seems to work again.
I'm not 100% on the registry so I dont advise doing this willy nilly unless you're at your wits end...but yea, just a FYI.

Janzell Jurilla said...

Hi Sean, Thanks for noticing that. Its seems that Magento can't handle the same registry. I will hunt some alternative for registry and I'll let you know. If you have something alternative out there you can freely post it here.

Many thanks.


asmed said...

I know this is a year old but I have the same probably that Sean was having. Is his solution the best or is there an alternative that was found?

Abaij said...

Thank you for posting this article. It helps me a lot in handling the order processing, but I have the same problem with Sean. The error shows up everytime there are more than 1 shipping code. Please post any solution you found here. Thank you.