Format output
Your export works. But the output looks messy. Quotes everywhere. Wrong case. No spacing.
Let’s fix that.
Remove quotes from CSV
By default, CSV wraps everything in quotes. Some systems hate that.
Add this code to Misc Settings:
|
1 2 3 4 5 6 7 |
// CSV without quotes add_filter('woe_csv_custom_output_func', function ($custom_output,$handle,$data,$delimiter,$linebreak,$enclosure,$is_header) { fwrite($handle, join($delimiter, $data).$linebreak ); $custom_output = true; //stop fputcsv! return $custom_output; }, 10, 7); |
Clean CSV. No quotes. Works with old accounting software.
Force quotes around everything
Need quotes on every field? Swap to this:
|
1 2 3 4 5 6 7 8 |
// CSV force quotes add_filter('woe_csv_custom_output_func',function ($custom_output,$handle,$data,$delimiter,$linebreak,$enclosure,$is_header) { foreach($data as $k=>$v) $data[$k] = $enclosure . str_replace($enclosure, $enclosure . $enclosure, $v) . $enclosure; fwrite($handle, join($delimiter, $data). $linebreak ); return true; //stop default fputcsv! }, 10, 7); |
Great for databases that expect uniform formatting.
Fix umlauts and accents
German customers? French? “Müller” becomes “Muller”. “Café” becomes “Cafe”.
|
1 2 3 4 5 |
//CSV, replace umlauts with ASCII characters add_filter( "woe_csv_output_filter", function($row, $formatter){ return array_map( "remove_accents", $row ); },10,2); |
No more weird characters breaking your CSV.
Convert everything to UPPERCASE
|
1 2 3 4 5 6 7 8 |
//convert all values to UPPER case add_filter('woe_fetch_order', function($row,$order){ array_walk_recursive($row, function(&$item,$key) { $item = strtoupper($item); }); return $row; },10,2); |
Warehouse systems often demand uppercase. This delivers.
Add empty row between orders
Hard to read when orders run together. Add a blank row.
|
1 2 3 4 5 6 |
// add empty row after each order, Excel/CSV/TAB formats add_filter("woe_fetch_order_data", function($rows) { $rows[] = array_fill(0, count($rows[0]),""); return $rows; }); |
Each order now has a spacer row. Works for CSV, Excel, and TAB formats.
Add gap between orders in PDF
Same idea. For PDF reports.
|
1 2 3 4 5 6 7 8 9 10 11 |
// add gap after each order, PDF format add_filter('woe_pdf_before_print_row', function($style, $row, $pdf, $formatter) { static $last = null; $cur = isset($row[0]) ? $row[0] : null; if ($last !== null && $cur !== '' && $last !== $cur) { $pdf->Ln(5); } if ($cur !== '' && $cur !== null) $last = $cur; return $style; }, 10, 4); |
Adds a 5-point gap when the order number changes.
Add empty product row at top of each order
Some systems need a header row before products.
|
1 2 3 4 5 6 7 8 9 10 |
//output empty product as 1st row for order add_filter( "woe_fetch_order_products", function ($products, $order, $labels, $format, $static_vals) { $first_product = reset($products); $empty_product = array(); foreach($first_product as $k=>$data) $empty_product[$k] = ""; array_unshift($products,$empty_product); return $products; } , 10, 5); |
First row of each order is blank. Products start on row two.
Print date range above header in Excel
|
1 2 3 4 5 6 7 8 9 10 |
// print date range above header, Excel format add_action( 'woe_xls_print_header', function($objPHPExcel, $formater ) { $objPHPExcel->getActiveSheet()->insertNewRowBefore(1,2); //2 lines above header $formater->last_row += 2 ; //2 rows $objPHPExcel->getActiveSheet()->setCellValue( "A1", "From Date:" ); $objPHPExcel->getActiveSheet()->setCellValue( "B1", $formater->settings['global_job_settings']['from_date'] ); $objPHPExcel->getActiveSheet()->setCellValue( "C1", "To Date:" ); $objPHPExcel->getActiveSheet()->setCellValue( "D1", $formater->settings['global_job_settings']['to_date'] ); },10,2); |
Shows date range above your column headers. No confusion about which period you exported.
Format Excel column as numbers
Excel sometimes treats prices as text. Fix that.
|
1 2 3 4 5 6 |
// format Excel column as number add_action( 'woe_xls_print_footer', function ($objXls,$formatter) { $row = $formatter->last_row; $objXls->getActiveSheet()->getStyle( "C1:C" . $row)->getNumberFormat()->setFormatCode('0.00'); }, 10, 2); |
Change “C” to your price column. Shows two decimal places.
Sum an Excel column automatically
|
1 2 3 4 5 6 7 |
// sum Excel column add_action( 'woe_xls_print_footer', function ($objXls,$formatter) { $row = $formatter->last_row; // edit column names below! $formatter->objPHPExcel->getActiveSheet()->setCellValue( "A". ($row+1), "Orders Total:" ); $formatter->objPHPExcel->getActiveSheet()->setCellValue( "B". ($row+1), "=SUM(B2:B$row)"); } ,10, 2); |
Adds a total row at bottom. Column B sums automatically.
Fixed width columns for legacy systems
Some old systems need exact column widths. No delimiters.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// fixed width, you should use format TSV! add_filter('woe_tsv_custom_output_func', function ($custom_output,$handle,$data,$delimiter,$linebreak,$enclosure,$is_hader) { $default_len = 10; $len = array(20,20,20,20,20,20,20,20,20,20); //define width for each column $data = array_values($data); foreach( $data as $pos=>$v) { $l = isset($len[$pos]) ? $len[$pos] : $default_len; $v = substr($v, 0, $l);// truncate long values $data[$pos] = str_pad($v, $l, " ", STR_PAD_RIGHT); // or STR_PAD_LEFT ? edit it! } fwrite($handle, join("", $data). $linebreak); return true; //stop fputcsv! }, 10, 7); |
Each column has exact width. Long values get truncated. Short values get padded with spaces.
Pro tip: Edit the $len array. Match your actual column count.
Add customer section to JSON
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
//add section "customer" to json add_filter('woe_json_output_filter', function ($json,$data) { $order = new WC_Order(WC_Order_Export_Engine::$order_id); $data['customer'] = array(); $data['customer']['name'] = $order->get_shipping_first_name()." ".$order->get_shipping_last_name(); $data['customer']['email'] = $order->get_billing_email(); $data['customer']['phone'] = $order->get_billing_phone(); $data['customer']['address1'] = $order->get_shipping_address_1(); $data['customer']['address2'] = $order->get_shipping_address_2(); $data['customer']['city'] = $order->get_shipping_city(); $data['customer']['state'] = $order->get_shipping_state(); $data['customer']['country'] = $order->get_shipping_country(); $data['customer']['zip'] = $order->get_shipping_postcode(); return json_encode($data,JSON_PRETTY_PRINT); },10,2); |
Structures customer data as a nested object. Cleaner JSON.
Add shipping address as a nested XML section
By default, XML exports are flat. This creates a proper nested structure.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
// add nested section "CustomerShippingAddress" to XML add_filter( "woe_xml_output_filter", function($xml_string, $rec, $formatter){ $order = new WC_Order(WC_Order_Export_Engine::$order_id); //to XML $xml = new SimpleXMLElement($xml_string); $customer = $xml->AddChild("CustomerShippingAddress"); // add some nested fields $customer->AddChild("FirstName", $order->get_shipping_first_name() ); $customer->AddChild("LaststName", $order->get_shipping_last_name() ); $customer->AddChild("Address_1", $order->get_shipping_address_1() ); $customer->AddChild("Address_2", $order->get_shipping_address_2() ); $customer->AddChild("City", $order->get_shipping_city() ); $customer->AddChild("Zip", $order->get_shipping_postcode() ); $customer->AddChild("State", $order->get_shipping_state() ); $customer->AddChild("Country", $order->get_shipping_country() ); //convert back to string //format it! $dom = dom_import_simplexml( $xml ); $dom->ownerDocument->formatOutput = ($formatter->mode == 'preview' ); $output_flags = ! empty( $formatter->settings['self_closing_tags'] ) ? null : LIBXML_NOEMPTYTAG; $xml_string = $dom->ownerDocument->saveXML( $dom->ownerDocument->documentElement,$output_flags); return $xml_string; },10,3); |
What you get: Shipping address wrapped in <CustomerShippingAddress> tags. Clean and nested.
Pro tip: Edit the tag name. Change “CustomerShippingAddress” to whatever your system expects.
Add fees, shipping, and taxes as a JSON section
Standard JSON exports miss these. This adds them.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// add "other" items as separate section to json add_filter( "woe_json_output_filter", function($json, $record, $formatter){ $record['others'] = woe_get_order_others(WC_Order_Export_Engine::$order_id); // EDIT key return json_encode($record,JSON_PRETTY_PRINT); },10,3); function woe_get_order_others($order_id) { global $wpdb; $results = array(); $types = array( 'fee'=>'_fee_amount', 'shipping'=>'cost', 'tax'=>'tax_amount'); foreach($types as $type=>$key) { $items = $wpdb->get_results("SELECT items.order_item_name, itemmeta.meta_value FROM {$wpdb->prefix}woocommerce_order_items items INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta itemmeta ON items.order_item_id = itemmeta.order_item_id AND itemmeta.meta_key = '$key' WHERE items.order_id = $order_id AND items.order_item_type = '$type'" ); foreach($items as $item) $results[ $item->order_item_name ] = $item->meta_value; } return $results; } |
Color PDF rows by order
Makes PDF reports much easier to read.
|
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 |
//color PDF rows, you must include "Order Number" to export ! class Woe_Color_PDF{ var $order_number_position = 0; // EDIT it , as we need to know order number, positions start from 0 var $table_row_settings;// use UI to set background color for "odd" orders var $even_background_color = array(220, 220, 220); var $order_counter = 0; var $order_number_current = ""; function __construct() { //remember default row formatting add_filter("woe_formatter_pdf_properties", function($pdf_settings){ $this->table_row_settings = $pdf_settings['table_row']; return $pdf_settings; }); //count orders add_filter( 'woe_pdf_before_print_row', function($style, $row, $pdf,$formatter){ $order_number = $row[$this->order_number_position]; // new order not started if( $order_number != "" AND $order_number != $this->order_number_current) $this->order_counter++; // count orders $this->order_number_current = $order_number; $pdf_row_settings = $this->table_row_settings; if( $this->order_counter % 2 == 0 ) { // even order uses own color $pdf_row_settings['background_color'] = $this->even_background_color; } //done return $pdf_row_settings; },10,4); } } new Woe_Color_PDF(); |
Important: Change $order_number_position = 0 to match your Order Number column. First column is 0. Second is 1.
Odd orders use your default style. Even orders get light gray background.
Build completely custom XML from scratch
Don’t like the default XML structure? Build your own.
|
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 |
// custom XML add_filter("woe_xml_output_filter","woe_xml_make_order"); function woe_xml_make_order($xml) { // get order $order = new WC_Order(WC_Order_Export_Engine::$order_id); //make xml $xml = new SimpleXMLElement( "<Order/>" ); // top $xml->addChild("OrderNumber", $order->get_order_number() ); $xml->addChild("CustomerNumber", $order->get_customer_id() ); // addresses $addr = $xml->addChild("Addresses"); $b_addr = $addr->addChild("BillingAddress"); $b_addr->addChild("FirstName",$order->get_billing_first_name() ); $b_addr->addChild("LastName",$order->get_billing_last_name() ); $b_addr->addChild("Street", trim( $order->get_billing_address_1() . " " . $order->get_billing_address_2() ) ); $b_addr->addChild("Zipcode",$order->get_billing_postcode() ); $b_addr->addChild("City",$order->get_billing_city() ); $s_addr = $addr->addChild("ShippingAddress"); $s_addr->addChild("FirstName",$order->get_shipping_first_name() ); $s_addr->addChild("LastName",$order->get_shipping_last_name() ); $s_addr->addChild("Street", trim( $order->get_shipping_address_1() . " " . $order->get_shipping_address_2() ) ); $s_addr->addChild("Zipcode",$order->get_shipping_postcode() ); $s_addr->addChild("City",$order->get_shipping_city() ); // items $items = $xml->addChild("LineItems"); foreach ( $order->get_items('line_item') as $item_id=>$item ) { $product = $order->get_product_from_item( $item ); $item_meta = get_metadata( 'order_item', $item_id ); $itemXML = $items->addChild("LineItem"); $itemXML->addChild("Id",$product->get_sku() ); $itemXML->addChild("Name",$item['name']); $itemXML->addChild("Quantity",$item['qty']); } // shipment $ship = $xml->addChild("LineItemShipping"); $shipping_methods = $order->get_items( 'shipping' ); $shipping_method = reset($shipping_methods); // take first entry $shipping_method_id = !empty($shipping_method) ? $shipping_method['method_id'] : '' ; $ship->addChild("Id",$shipping_method_id); $ship->addChild("TotalPrice",$order->get_total_shipping() ); $ship->addChild("Name",$order->get_shipping_method() ); // payment $pay = $xml->addChild("LineItemPayment"); $pay->addChild("Id",$order->get_payment_method() ); $pay->addChild("TotalPrice",$order->get_total() ); $pay->addChild("Name",$order->get_payment_method_title() ); //bottom $xml->addChild("CreationDate", $order->get_date_created() ); $xml->addChild("ShippedOn", $order->get_date_completed() ); //format it! $dom = dom_import_simplexml( $xml ); $dom->ownerDocument->formatOutput = true; $xml = $dom->ownerDocument->saveXML( $dom->ownerDocument->documentElement ); return $xml; } |
Add dynamic date tags to PDF headers
Set PDF header text to “orders-{from_date}-to-{to_date}”. The code fills in the actual dates.
First, in Advanced Order Export for WooCommerce, open your PDF settings. Set “Page header text” to:
orders-de-debit-{from_date}-to-{to_date}
Then add this code:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
// open section PDF and set "Page header text" = "orders-de-debit-{from_date}-to-{to_date}" //use this code to fill added tags add_filter( 'woe_formatter_pdf_properties', function ( $props, $settings) { $date_range = WC_Order_Export_Data_Extractor::get_date_range($settings['global_job_settings'], false); $tags = array( '{from_date}'=> date("m/d/Y", strtotime($date_range['from_date'])), '{to_date}'=>date("m/d/Y", strtotime($date_range['to_date'])), ); $props['header']['title'] = strtr($props['header']['title'], $tags); return $props; }, 10, 2 ); |
What happens: {from_date} becomes “05/18/2026”. {to_date} becomes “05/25/2026”. No manual editing.
Pro tip: Change the date format. Swap "m/d/Y" for "Y-m-d" or "d-m-Y".
Transpose Excel output
Swap rows and columns. Perfect for pivot-style reports.
|
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 |
// Transpose after export - swap rows and columns in Excel add_action( 'woe_xls_print_footer', function ($objPHPExcel,$formatter) { $objWorksheet = $objPHPExcel->getActiveSheet(); //read existing cells $data_matrix = array(); $lastColumn = $objWorksheet->getHighestColumn(); foreach($objWorksheet->getRowIterator() as $rowIndex => $row) { $array = $objWorksheet->rangeToArray('A'.$rowIndex.':'.$lastColumn.$rowIndex) ; $data_matrix[] = array_pop( $array ); } // transpose $trans_matrix = array_map(null, ...$data_matrix); //add new sheet $sheet = $objPHPExcel->createSheet(); $sheet->setTitle("Transposed"); // add rows $pos = 1; foreach ($trans_matrix as $row_idx => $row) { $test_row = $row; unset($test_row[0]);// skip Field name // comment following line if you need empty rows! if(join("",$test_row) == "") continue; //skip row without data foreach($row as $col_idx=>$text) { $sheet->setCellValueByColumnAndRow($col_idx, $pos, $text); } $pos++; } //adjust 1st column $sheet->getStyle( "A1:A".$pos )->getFont()->setBold( true ); foreach(range('A',$sheet->getHighestColumn()) as $columnID) $sheet->getColumnDimension($columnID)->setAutoSize(true); //freeze $sheet->freezePane( 'B1' ); //done , we don't need original $objPHPExcel->removeSheetByIndex(0); }, 10, 2); |
Original sheet removed. New “Transposed” sheet appears. Headers become rows. Rows become headers.
Warning: This creates a new sheet. Your original formatting won’t carry over.
Where to put these snippets
Go to Misc Settings in your export profile. Paste code at the bottom.
Each snippet works alone. Pick what you need.
Advanced Order Export for WooCommerce handles the export. These snippets reshape the output.
Common mistake
Using too many snippets at once. They can conflict. Test one. Add another. Verify both work.
Also, some snippets assume specific column positions. Always test on 1-2 orders before running full export.
Pro tip
Combine date range header with column summing. Professional reports. No manual editing needed.
Test every format change. What looks good in preview might break your import system.