Quite often we need to generate EDI purchase order files (850) and accept EDI ASN files (856) to exchange data between a web store and an external system when processing orders.
Working with EDI files may be quite involving especially if support for all possible scenarios and data elements is required. However, when working with a specific vendor and their needs it's possible to simplify things and make some assumptions or exclude data elements that are not used in specific cases.
The code to generate the EDI document will be invoked once an invoice is created. This means once the order is paid. We create a new module for this containing a new observer class called NeptuneWeb_ExportEdiOrder_Model_Observer with the following config.xml file:
<?xml version="1.0"?> <config> <modules> <NeptuneWeb_ExportEdiOrder> <version>0.1</version> </NeptuneWeb_ExportEdiOrder> </modules> <global> <models> <nwExportEdiOrder> <class>NeptuneWeb_ExportEdiOrder_Model</class> </nwExportEdiOrder> </models> <events> <sales_order_invoice_pay> <observers> <nwExportEdiOrder> <type>singleton</type> <class>nwExportEdiOrder/observer</class> <method>sales_order_invoice_pay</method> </nwExportEdiOrder> </observers> </sales_order_invoice_pay> </events> </global> </config>
Then in its Model/Observer.php file:
public function sales_order_invoice_pay($observer) {
function cleanForEdi($string) {
# replace `
$string = preg_replace('/`/', '', $string);
# replace the following characters * ~ >
return preg_replace('/[\*~>]/', '-', $string);
}
# gather necessary variables
$invoice = $observer->getEvent()->getInvoice();
$order = $invoice->getOrder();
$storeId = $order->getStoreId();
$orderItems = $order->getAllItems();
$orderId = $order->getIncrementId();
$customer = Mage::getModel('customer/customer')->load($order->getCustomerId());
$streetBA=$order->getBillingAddress()->getStreet();
$streetSA=$order->getShippingAddress()->getStreet();
$shippingMethod = $order->getShippingMethod();
# format is carrier_method
# eg: fedex_FEDEXGROUND
$data = explode('_', $shippingMethod);
$shippingCarrier = $data[0];
$shippingCarrierMethod = $data[1];
$ServiceLevelCode = "";
switch ($shippingCarrierMethod) {
case "FEDEXGROUND":
$ServiceLevelCode = "SG";
break;
case "FEDEX2DAY":
$ServiceLevelCode = "SE";
break;
}
$date = date("ymd");
$date2 = date("Ymd");
$time = date("Hi");
$InterchangeSenderID = "MYWEB1234";
$InterchangeSenderIDISA = "MYWEB1234 "; # must be 15 characters
$InterchangeReceiverID = "MYID12345678";
$InterchangeReceiverIDISA = "MYID12345678 "; # must be 15 characters
#$InterchangeControlNumber = $orderId; # need to be incremented, unique id - will use order id from magento
$InterchangeControlNumber = $order->getId(); # need to use id from the db because it must be numeric (and magento is creating numbers like 10000123-1)
# $InterchangeControlNumber must be 9 digits, need to pad with zeros
if (strlen($InterchangeControlNumber) < 9) {
$InterchangeControlNumber = str_pad($InterchangeControlNumber, 9, '0', STR_PAD_LEFT);
}
$PoNumber = $orderId; # order id from magento
$ProductionOrTest = "P";
# the entire ISA line must be 106 characters long
print "ISA*00* *00* *08*$InterchangeSenderIDISA*14*$InterchangeReceiverIDISA*$date*$time*:*00501*$InterchangeControlNumber*0*$ProductionOrTest*>~\n";
print "GS*PO*$InterchangeSenderID*$InterchangeReceiverID*$date2*$time*$InterchangeControlNumber*X*005010~\n";
$TransactionSetControlID = $InterchangeControlNumber;
$segmentCount = 1;
print "ST*850*$TransactionSetControlID~\n";
$segmentCount++;
$orderNumber = $PoNumber;
$orderDate = date("Ymd", strtotime($order->getCreatedAt()));
print "BEG*00*SA*$orderNumber**$orderDate~\n";
$segmentCount++;
# shipping method
print "TD5**2*FDEG**$shippingCarrierMethod*******$ServiceLevelCode~\n";
$segmentCount++;
$shipToName = cleanForEdi($order->getShippingAddress()->getFirstname() . " " . $order->getShippingAddress()->getLastname());
$customerId = $customer->getId(); # unique customer id
# get id for shipping address
$shippingAddressId = $order->getShippingAddress()->getId();
print "N1*ST*$shipToName*92*$shippingAddressId~\n";
$segmentCount++;
$address1 = cleanForEdi($streetSA[0]);
$address2 = cleanForEdi((count($streetSA)==2)?$streetSA[1]:'');
print "N3*$address1*$address2~\n";
$segmentCount++;
$city = cleanForEdi($order->getShippingAddress()->getCity());
$state = cleanForEdi($order->getShippingAddress()->getRegionCode());
$zip = cleanForEdi($order->getShippingAddress()->getPostcode());
$country = cleanForEdi($order->getShippingAddress()->getCountry());
print "N4*$city*$state*$zip*$country~\n";
$segmentCount++;
# phone must be 10 digits
$phone = cleanForEdi($order->getShippingAddress()->getTelephone());
$phone = preg_replace('/[^\d]/', '', $phone);
$phone = substr($phone, 0, 10);
print "PER*BD**TE*$phone~\n";
$segmentCount++;
foreach ($orderItems as $item) {
$qty = intval($item->getQtyOrdered());
$sku = cleanForEdi($item->getSku());
$unitPrice = sprintf("%.06f", ($item->getRowTotal() - $item->getDiscountAmount()) / $qty);
$total = sprintf("%.02f", $item->getRowTotal() - $item->getDiscountAmount());
print "PO1**$qty*EA*$unitPrice*LE*VN*$sku~\n";
$segmentCount++;
print "AMT*1*$total~\n";
$segmentCount++;
}
# line item for shipping
$totalShipping = sprintf("%.02f", $order->getShippingAmount() - $order->getShippingDiscountAmount());
print "PO1**1*EA*$totalShipping*LE*VN*SNHDL~\n";
$segmentCount++;
print "AMT*1*$totalShipping~\n";
$segmentCount++;
$NumberofDetailLines = count($orderItems) + 1;
print "CTT*$NumberofDetailLines~\n";
$segmentCount++;
$orderTotal = sprintf("%.02f", $order->getGrandTotal() - $order->getTaxAmount());
print "AMT*GV*$orderTotal~\n";
$segmentCount++;
print "SE*$segmentCount*$InterchangeControlNumber~\n";
print "GE*1*$InterchangeControlNumber~\n";
print "IEA*1*$InterchangeControlNumber~\n";
$string = ob_get_clean();
$ouputDirectory = "/tmp/edi-outgoing";
$outFile = $ouputDirectory . "/edi-". $orderId . ".txt";
$fp = fopen($outFile, "a");
fwrite($fp, $string);
fclose($fp);
}
}
In the above file we define a class and a method that will be invoked when invoices are paid. This will get all the order information and create an EDI purchase order document. The data being generated need to be customized with specific vendor information and additional error handling or validation can be added on top of the basic cleanForEdi() method.