Add New Fields
Sometimes the user interface isn’t enough. You need custom calculations. Here’s how.
Advanced Order Export for WooCommerce lets you add fields via PHP snippets. No clunky workarounds.
Before you start
Create the field in the UI first. Go to Setup Fields and add a custom field. Use a unique meta key like all_products or current_date.
First example: combine products into one cell
Want a single column showing “Rain x 4, Lemon x 2, Other product x Qty”?
Add a custom field with meta key “all_products“.
Then put following code to section “Misc Settings”:
|
1 2 3 4 5 6 7 8 |
add_filter('woe_get_order_value_all_products',function ($value, $order,$fieldname) { $lines = array(); foreach($order->get_items() as $item) { $lines[] = $item["name"]. " x " .$item["qty"]; } return join(", ", $lines); },10,3); |
Save the export. Run a test. Check your CSV.
Split Multiple Shipping Methods into Separate Columns
This code gives each method its own column:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
// export 2 shipping methods as separate columns // you should add keys - shipping_name_1 shipping_name_2 shipping_cost_1 shipping_cost_2 add_filter( "woe_fetch_order", function($row, $order ) { $i = 1; foreach ( $order->get_items('shipping') as $item_id=>$item ) { if( !isset( $row["shipping_name_".$i]) ) continue; $row["shipping_name_".$i] = $item->get_name(); $row["shipping_cost_".$i] = $item->get_total(); $i++; } return $row; },10,2); |
Add current date to every row
Create a field with meta key current_date. Add this snippet:
|
1 2 3 4 5 |
//export current date, add meta key "current_date" add_filter('woe_get_order_value_current_date',function ($value, $order,$fieldname) { return current_time("Y-m-d"); },10,3); |
Now every order shows today’s date. Great for daily reports.
Export shipping zone names
First, create a meta key called zone_name. Then use this:
|
1 2 3 4 5 6 7 8 9 10 11 |
//export Shipping Zone, add meta key "zone_name" add_filter('woe_get_order_value_zone_name',function ($value, $order,$fieldname) { $methods = $order->get_items( 'shipping' ); $method = reset( $methods ); // take first entry if ( ! empty( $method ) ) { $zone = WC_Shipping_Zones::get_zone_by('instance_id',$method['instance_id']); $value = $zone->get_zone_name(); } return $value; },10,3); |
Helpful for regional tax calculations.
Add Customer Country Based on IP Address
WooCommerce already captures the shipping and billing country. But what if customers check out as guests? Or their billing country doesn’t match their actual location?
This code grabs the country from the customer’s IP address. No extra input needed.
|
1 2 3 4 5 6 7 8 9 |
//export Geolocation Country, add meta key "geolocation_country" add_filter('woe_get_order_value_geolocation_country',function ($value, $order,$fieldname) { $user_ip_address = $order->get_customer_ip_address(); $geolocation_instance = new WC_Geolocation(); $user_geolocation = $geolocation_instance->geolocate_ip( $user_ip_address ); $value = $user_geolocation['country']; return $value; },10,3); |
Pull gift card data
Using Pimwick Gift Cards? Add two meta keys: card_number and amount_redeemed. Then paste:
|
1 2 3 4 5 6 7 8 9 |
// Pimwick Gift Card, add meta keys "card_number" and "amount_redeemed" add_filter( "woe_fetch_order", function($row, $order ){ foreach( $order->get_items( 'pw_gift_card' ) as $order_item_id => $line ) { $row["card_number"] = $line->get_card_number(); $row["amount_redeemed"] = $line->get_amount(); } return $row; },10,2); |
Export Which Supplier Sold Each Product
Each product has an assigned supplier. But standard WooCommerce exports don’t show that supplier name.
This code pulls the supplier name directly into your product export rows:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// export ATUM supplier, add meta key "atum_supplier" add_filter('woe_get_order_product_value_atum_supplier', function ($value, $order, $item, $product,$item_meta) { if($product AND class_exists("AtumIncHelpers") ) { $product = AtumIncHelpers::get_atum_product( $product->get_id() ); $supplier_id = $product->get_supplier_id(); if ( $supplier_id ) { $supplier_post = get_post( $supplier_id ); if ( $supplier_post ) $value = $supplier_post->post_title; } } return $value; }, 10, 5); |
Track Who Created Phone Orders
You take orders over the phone. Or your staff creates orders manually from the admin panel. But WooCommerce doesn’t track who made that order. It just shows “admin” or nothing at all.
This code captures the actual staff member’s name and role:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// export the name and role of the user that created the Phone Order // add meta keys "po_creator_name" , "po_creator_role" add_filter( "woe_fetch_order", function($row, $order){ $creator_id = $order->get_meta("_wpo_order_creator"); if( $creator_id AND ( isset($row['po_creator_name']) OR isset($row['po_creator_role']) ) ) { $user_info = get_userdata($creator_id); if($user_info AND isset($row['po_creator_name'])) $row['po_creator_name'] = $user_info->display_name; if($user_info AND isset($row['po_creator_role'])) $row['po_creator_role'] = implode(', ', $user_info->roles); } return $row; },10,2); |
Add order time
Want a separate column for the hour? Add field order_time via UI. Then this code:
|
1 2 3 4 5 6 7 8 9 10 |
// add order field "Order Time" add_filter('woe_get_order_fields', function ($fields) { $fields['order_time'] = array( 'label' => 'Order Time', 'colname' => 'Order Time', 'checked' => 1 ); return $fields; }); // calculate new field add_filter('woe_get_order_value_order_time', function ($value,$order, $field) { return $order->get_date_created()->date("H:i:s"); }, 10, 3); |
The field appears automatically in your export setup.
Product-level calculated field
Create qty_name as a product field. Then:
|
1 2 3 4 5 6 7 8 9 10 |
// add product field "Quantity x Name" add_filter('woe_get_order_product_fields', function($fields,$format) { $fields['qty_name'] = array( 'label' => 'Quantity x Name', 'colname' => 'Quantity x Name', 'checked' => 1 ); return $fields; },10, 2); add_filter('woe_get_order_product_value_qty_name', function($value,$order, $item, $product,$item_meta) { return $item['qty'] .' x '. $item['name']; }, 10, 5); |
Now each product row shows “2 x Blue Hoodie”.
Export Invoice Numbers from Germanized Pro
You use Germanized Pro for legal compliance. Your store generates invoices for every order. But standard exports ignore those invoice numbers completely.
This code pulls every invoice number tied to an order. Then puts them all in one column:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// add field "Invoice Numbers", for Germanized Pro add_filter('woe_get_order_fields', function ($fields) { $fields['german_invoice_numbers'] = array( 'label' => 'Invoice Numbers', 'colname' => 'Invoice Numbers', 'checked' => 1 ); return $fields; }); add_filter('woe_get_order_value_german_invoice_numbers', function ($value,$order, $field) { $invoices = wc_gzdp_get_invoices_by_order( $order ); if( $invoices ) { $numbers = array(); foreach ( $invoices as $id => $invoice ) { $numbers[] = $invoice->get_title(); } $value = join(", ", $numbers); } return $value; }, 10, 3); |
Find When an Order Was Actually Processed
WooCommerce has an “order date” and “paid date”. But no “processed date”. That’s the moment someone moved the order from “Pending Payment” to “Processing.”
This code finds that exact timestamp. It reads the order notes to locate that status change.
|
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 field "Date Processed", based on order's comments add_filter('woe_get_order_fields', function ($fields) { $fields['date_processed'] = array( 'label' => 'Date Processed', 'colname' =>'Date Processed', 'segment' => 'common', 'format'=>'date', 'checked' => 1 ); return $fields; }); add_filter('woe_get_order_value_date_processed', function ($value,$order,$fieldname) { $args = array( 'post_id' => $order->id, 'approve' => 'approve', 'type' => 'order_note', 'search' => 'Order status changed from Pending Payment to Processing.', ); // woocommerce hides such records by default remove_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ), 10 ); $notes = get_comments( $args ); add_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ), 10, 1 ); $date_processed = ''; if(!empty($notes)) { $date_processed = $notes[0]->comment_date; } return $date_processed; },10, 3); |
Split Every Tax Type into Separate Columns
WooCommerce lumps all taxes together. You see one “tax total” number. But your accountant needs CGST separate from SGST. Or VAT broken down by rate.
This code explodes every tax into its own columns. Shipping taxes. Product taxes. Percentages. Base amounts. Everything.
|
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 |
// Tax amounts, tax % and base amounts class WOE_add_product_shipping_taxes{ var $taxes = array("CGST","SGST","IGST"); //EDIT tax labels (as you see them inside the section "order items" )! function __construct() { add_filter('woe_get_order_fields', array($this,'add_order_fields'), 10, 1); add_filter('woe_get_order_product_fields', array($this,'add_product_fields'), 10, 2); add_filter('woe_fetch_order', array($this,'fill_shipping_and_total_taxes'), 10, 2); add_filter('woe_fetch_order_product',array($this,'fill_product_taxes'), 10, 5); } function add_order_fields($fields) { global $wpdb; foreach($this->taxes as $tax) { $fields['shipping_tax_amount_'.$tax] = array('label'=>"$tax Shipping Amount",'checked' => 1, 'segment'=>'cart','colname'=>"$tax Shipping Amount", 'format'=>'money'); $fields['shipping_tax_percentage_'.$tax] = array('label'=>"$tax Shipping Percentage",'checked' => 1, 'segment'=>'cart','colname'=>"$tax Shipping Percentage", 'format'=>'number'); $fields['total_tax_amount_'.$tax] = array('label'=>"$tax Tax Amount",'checked' => 1, 'segment'=>'cart','colname'=>"$tax Tax Amount", 'format'=>'money'); $fields['total_tax_percentage_'.$tax] = array('label'=>"$tax Total Percentage",'checked' => 1, 'segment'=>'cart','colname'=>"$tax Total Percentage", 'format'=>'number'); $fields['tax_plus_base_amount_'.$tax] = array('label'=>"$tax Tax Amount (+Base Amount)",'checked' => 1, 'segment'=>'cart','colname'=>"$tax Tax Amount(+Base Amount)", 'format'=>'money'); $fields['base_amount_'.$tax] = array('label'=>"$tax Base Amount",'checked' => 1, 'segment'=>'cart','colname'=>"$tax Base Amount", 'format'=>'money'); } return $fields; } function add_product_fields($fields,$format) { global $wpdb; foreach($this->taxes as $tax) { $fields['tax_amount_'.$tax] = array('label'=>"Product $tax Amount",'checked' => 1, 'segment'=>'cart','colname'=>"Product $tax Amount", 'format'=>'money'); $fields['tax_percentage_'.$tax] = array('label'=>"Product $tax Percentage",'checked' => 1, 'segment'=>'cart','colname'=>"Product $tax Percentage", 'format'=>'number'); } return $fields; } function fill_shipping_and_total_taxes($row, $order ) { $shipping_taxes = $shipping_tax_rates = array(); $total_taxes = $total_tax_rates = array(); $order_taxes = $order->get_taxes(); $base_amount = array(); foreach($order->get_items('shipping') as $shipping) { $tax_data = $shipping->get_taxes(); foreach($order_taxes as $tax_item) { $tax_item_id = $tax_item->get_rate_id(); if(!isset( $tax_data['total'][ $tax_item_id ] ) ) continue; $label = $tax_item->get_label() ; $shipping_taxes[$label] = isset( $tax_data['total'][ $tax_item_id ] ) ? $tax_data['total'][ $tax_item_id ] : 0; $shipping_tax_rates[$label] = $shipping->get_total()>0 ? round($shipping_taxes[$label]/$shipping->get_total() * 100) : 0; } } foreach($order->get_items( array('shipping','fee','line_item')) as $item) { $tax_data = $item->get_taxes(); foreach($order_taxes as $tax_item) { $tax_item_id = $tax_item->get_rate_id(); if(!isset( $tax_data['total'][ $tax_item_id ] ) ) continue; $label = $tax_item->get_label() ; if(!isset($base_amount[$label])) $base_amount[$label] = 0; $base_amount[$label] += $item->get_total(); } } foreach($order_taxes as $tax_item) { $label = $tax_item->get_label() ; $total_taxes[$label] = $tax_item->get_tax_total(); $total_tax_rates[$label] = $tax_item->get_rate_percent(); } foreach($this->taxes as $tax) { if( isset($row['shipping_tax_amount_'.$tax]) ) $row['shipping_tax_amount_'.$tax] = wc_round_tax_total($this->find_tax_by_label($shipping_taxes,$tax)); if( isset($row['shipping_tax_percentage_'.$tax]) ) $row['shipping_tax_percentage_'.$tax] = $this->find_tax_by_label($shipping_tax_rates,$tax); if( isset($row['total_tax_amount_'.$tax]) ) $row['total_tax_amount_'.$tax] = wc_round_tax_total($this->find_tax_by_label($total_taxes,$tax)); if( isset($row['total_tax_percentage_'.$tax]) ) $row['total_tax_percentage_'.$tax] = $this->find_tax_by_label($total_tax_rates,$tax); if( isset($row['tax_plus_base_amount_'.$tax]) ) $row['tax_plus_base_amount_'.$tax] = wc_round_tax_total($this->find_tax_by_label($total_taxes,$tax) + $this->find_tax_by_label($base_amount,$tax) ); if( isset($row['base_amount_'.$tax]) ) $row['base_amount_'.$tax] = wc_round_tax_total( $this->find_tax_by_label($base_amount,$tax) ); } return $row; } function fill_product_taxes($row, $order, $item, $product, $item_meta) { $product_taxes = $product_tax_rates = array(); $order_taxes = $order->get_taxes(); $tax_data = $item->get_taxes(); foreach($order_taxes as $tax_item) { $tax_item_id = $tax_item->get_rate_id(); if(!isset( $tax_data['total'][ $tax_item_id ] ) ) continue; $label = $tax_item->get_label() ; $product_taxes[$label] = isset( $tax_data['total'][ $tax_item_id ] ) ? $tax_data['total'][ $tax_item_id ] : 0; $product_tax_rates[$label] = $item->get_total()>0 ? round($product_taxes[$label]/$item->get_total() * 100) : 0; } foreach($this->taxes as $tax) { if( isset($row['tax_amount_'.$tax]) ) $row['tax_amount_'.$tax] = wc_round_tax_total( $this->find_tax_by_label($product_taxes,$tax)); if( isset($row['tax_percentage_'.$tax]) ) $row['tax_percentage_'.$tax] = $this->find_tax_by_label($product_tax_rates,$tax); } return $row; } function find_tax_by_label($taxes,$label) { $value = 0; //var_dump($taxes); die(); foreach($taxes as $key=>$v) { if( $key == $label) { $value = $v; break; } } return $value; } } new WOE_add_product_shipping_taxes(); |
Put Every Fee in Its Own Column
WooCommerce fees (like gift wrapping, payment charges, or rush processing) get lumped together. You see “Fee: $10.00” but no breakdown. If an order has multiple fees, they merge into one messy cell.
This code automatically detects every fee type. Then creates separate columns for each one. Tax amounts 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 |
//add fees as columns class WOE_add_fees_columns{ var $order_fees; var $fee_columns; function __construct() { add_filter('woe_get_order_fields', array($this,'add_order_fields'), 10, 1); add_filter('woe_settings_validate_defaults', array($this,'hook_new_fields'), 10, 1); add_filter('woe_order_export_started',array($this,'fetch_order_fees'), 10, 1); } //1 add to UI function add_order_fields($fields) { global $wpdb; $fees = $wpdb->get_results("SELECT DISTINCT order_item_name FROM {$wpdb->prefix}woocommerce_order_items WHERE order_item_type='fee' ORDER BY order_item_name"); foreach($fees as $fee) { $key = 'fee_'.md5($fee->order_item_name); $fields[ $key ] = array('label'=>$fee->order_item_name,'checked' => 1, 'segment'=>'cart','colname'=>$fee->order_item_name, 'format'=>'string'); $key = 'fee_tax_'.md5($fee->order_item_name); $fields[ $key ] = array('label'=>$fee->order_item_name. " Tax",'checked' => 1, 'segment'=>'cart','colname'=>$fee->order_item_name. " Tax", 'format'=>'string'); $key = 'fee_plus_tax_'.md5($fee->order_item_name); $fields[ $key ] = array('label'=>$fee->order_item_name. " + Tax",'checked' => 1, 'segment'=>'cart','colname'=>$fee->order_item_name. " + Tax", 'format'=>'string'); } return $fields; } // 2 set hooks for new fields function hook_new_fields($settings) { global $wpdb; $fees = $wpdb->get_results("SELECT DISTINCT order_item_name FROM {$wpdb->prefix}woocommerce_order_items WHERE order_item_type='fee' ORDER BY order_item_name"); foreach($fees as $fee) { $key = 'fee_'.md5($fee->order_item_name); add_filter('woe_get_order_value_'.$key, array($this,'get_fee_value'), 10, 3); $key = 'fee_tax_'.md5($fee->order_item_name); add_filter('woe_get_order_value_'.$key, array($this,'get_fee_value'), 10, 3); $key = 'fee_plus_tax_'.md5($fee->order_item_name); add_filter('woe_get_order_value_'.$key, array($this,'get_fee_value'), 10, 3); } return $settings; } function get_fee_value($value, $order, $field) { return isset($this->order_fees[$field]) ? $this->order_fees[$field] : 0; } //3 get data from order function fetch_order_fees($order_id) { //reset values $this->order_fees = array(); //read fees $order = new WC_Order($order_id); foreach($order->get_items('fee') as $item_id => $item) { $name = $item->get_name(); $key = 'fee_'.md5($name); $total = $item->get_total(); $total = floatval($total); $this->order_fees[ $key ] = $total; $key = 'fee_tax_'.md5($name); $total_tax = $item->get_total_tax(); $total_tax = floatval($total_tax); $this->order_fees[ $key ] = $total_tax; $total_totalTax = $total + $total_tax; $key = 'fee_plus_tax_'.md5($name); $this->order_fees[ $key ] = $total_totalTax; } return $order_id; } } new WOE_add_fees_columns(); |
Common mistake
Do not skip the UI step. The meta key must exist in Setup Fields first. The code only fills the value. It won’t create the column.
Where to put code snippets
Go to your theme’s functions.php. Or use a code snippet plugin. Never edit plugin core files. Updates will erase your work.
Test each snippet individually. One typo can break the whole export.
Advanced Order Export for WooCommerce handles basic fields out of the box. Use these snippets when you need custom logic. Start simple. Add complexity later.
