Previous / Related Question: Display ALL available shipping methods for each specific order on admin edit order pages in Woocommerce
Currently in my WooCommerce based site, I am wanting to display the available shipping methods and prices on the order edit page.
It does not display the data as I want. For example, the output of my code so far results in:
Method 1
Method 2
Method 3
Price 1
Price 2
Price 3
When alternatively, I would like for it to display like this:
Method 1 - $Price 1
Method 2 - $Price 2
Method 3 - $Price 3
I understand why it is displaying this way, but I was curious how I could iterate the loops at the same time and format them, rather than one after the other.
This is my code so far:
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'action_woocommerce_admin_order_data_after_shipping_address', 10, 1 );
function action_woocommerce_admin_order_data_after_shipping_address( $order ){
// Get meta
$rate_labels = $order->get_meta( '_available_shipping_methods' );
$rate_costs = $order->get_meta( '_available_shipping_method_cost' );
$methods = array ( $rate_labels, $rate_costs );
// True
if ( $rate_labels ) {
// Loop
echo '<p><strong>Shipping Methods: </strong>';
foreach( $rate_labels as $rate_label ) {
// Output
echo '<p>' . $rate_label . '</p>';
}
foreach( $rate_costs as $rate_cost ) {
// Output
echo '<p> $' . $rate_cost . '</p>';
}
}
}
The following slight different code will display the label and the cost of all available shipping methods (in one array | one foreach loop):
add_action( 'woocommerce_checkout_create_order', 'action_wc_checkout_create_order' );
function action_wc_checkout_create_order( $order ) {
$shipping_data = array(); // Initializing
// Get shipping packages keys from cart
$packages_keys = (array) array_keys(WC()->cart->get_shipping_packages());
// Loop through shipping packages keys (when cart is split into many shipping packages)
foreach( $packages_keys as $key ){
// Get available shipping rates from WC_Session
$shipping_rates = WC()->session->get('shipping_for_package_'.$key)['rates'];
// Loop through shipping rates
foreach( $shipping_rates as $rate_key => $rate ){
// Set all related shipping rate data in the array
$shipping_data[] = array(
'id' => $rate_key,
'method_id' => $rate->method_id,
'instance_id' => (int) $rate->instance_id,
'label' => $rate->label,
'cost' => (float) $rate->cost,
'taxes' => (array) $rate->taxes,
'package_key' => (int) $key,
);
}
}
// Save shipping data as order custom field
if( ! empty($shipping_data) ) {
$order->update_meta_data( '_shipping_data', $shipping_data );
}
}
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'available_shipping_rates_after_shipping_address' );
function available_shipping_rates_after_shipping_address( $order ) {
// Get shipping rates custom meta data
$shipping_data = $order->get_meta( '_shipping_data' );
if ( ! empty($shipping_data) ) {
echo '<p><strong>Shipping Methods: </strong><br>';
// Loop through shipping rates data
foreach( $shipping_data as $rate ) {
// Calculate cost with taxes
$rate_cost = $rate['cost'] + array_sum($rate['taxes']);
// Output
echo $rate['label'] . ( $rate_cost > 0 ? ': '. wc_price($rate_cost) : '' ) . '<br>';
}
echo '</p>';
}
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
In case anyone happens to have the same question as I did, here is how I did it:
function action_woocommerce_admin_order_data_after_shipping_address( $order ) {
// Get meta
$rate_labels = $order->get_meta( '_available_shipping_methods' );
$rate_costs = $order->get_meta( '_available_shipping_method_cost' );
$methods = array ( $rate_labels, $rate_costs );
// True
if ( $rate_labels ) {
// Loop
echo '<p><strong>Shipping Methods: </strong>';
foreach(array_combine($rate_labels, $rate_costs) as $rate_label => $rate_cost) {
echo '<p>' . $rate_label . ' - $' . $rate_cost . '</p>';
}
}
}
Related
I am using the following code to show custom product meta in the order items table on the WooCommerce admin order details page:
add_action('woocommerce_admin_order_item_headers', 'tempiconsegna_admin_order_item_headers');
function tempiconsegna_admin_order_item_headers() {
$column_name = 'Tempi Consegna';
echo '<th>' . $column_name . '</th>';
}
add_action('woocommerce_admin_order_item_values', 'tempiconsegna_admin_order_item_values', 10, 3);
function tempiconsegna_admin_order_item_values($_product, $item, $item_id = null) {
$value = get_post_meta($_product->post->ID, 'prefix-tempiconsegna', 1);
echo '<td>' . $value . '</td>';
}
It displays "prefix-tempiconsegna" which are custom metas like:
Available in 3 days
Available now
etc..
My problem is that if I change the availability in the product, it changes also in previous orders.
How do i make this displaying the value at the moment of the order without changing when I update the availability of the product?
Your current code contains 2 errors:
Attempt to read property "post" on null
Attempt to read property "ID" on null
To answer your question: that's because you're using get_post_meta() and the productID, so if you adjust the data for the product it will also change the data where it is displayed, in your case the current and previous orders.
To prevent this, you have to add the data per order line item, this can be done via:
function action_woocommerce_checkout_create_order_line_item( $item, $cart_item_key, $values, $order ) {
// The WC_Product instance Object
$product = $item->get_product();
// Get value
$value = $product->get_meta( 'prefix-tempiconsegna' );
// NOT empty
if ( ! empty ( $value ) ) {
$item->update_meta_data( 'prefix-tempiconsegna', $value );
}
}
add_action( 'woocommerce_checkout_create_order_line_item', 'action_woocommerce_checkout_create_order_line_item', 10, 4 );
Then to display this in WooCommerce admin order details page, use:
// Add header
function action_woocommerce_admin_order_item_headers( $order ) {
// Set the column name
$column_name = __( 'Tempi Consegna', 'woocommerce' );
// Display the column name
echo '<th class="my-class">' . $column_name . '</th>';
}
add_action( 'woocommerce_admin_order_item_headers', 'action_woocommerce_admin_order_item_headers', 10, 1 );
//Add content
function action_woocommerce_admin_order_item_values( $product, $item, $item_id ) {
// Only for "line_item" items type, to avoid errors
if ( ! $item->is_type('line_item') ) return;
// Get value
$value = $item->get_meta( 'prefix-tempiconsegna' );
// NOT empty
if ( ! empty ( $value ) ) {
echo '<td>' . $value . '</td>';
} else {
echo '<td>Meta not found!</td>';
}
}
add_action( 'woocommerce_admin_order_item_values', 'action_woocommerce_admin_order_item_values', 10, 3 );
Inspired by this answer code, I've write the code below which works to add an error notice in WooCommerce checkout under a condition (they add a non-shippable product, (id'd by shipping class) to the cart and are outside of my local delivery zones (i.e. their postal code falls into the rest of the world zone) and only pickup is available (identified by only shipping method available).
add_action('woocommerce_after_checkout_validation', 'get_zone_info', 10 );
function get_zone_info( ) {
// Get cart shipping packages
$shipping_packages = WC()->cart->get_shipping_packages();
// Get the WC_Shipping_Zones instance object for the first package
$shipping_zone = wc_get_shipping_zone( reset( $shipping_packages ) );
$zone_id = $shipping_zone->get_id(); // Get the zone ID
$zone_name = $shipping_zone->get_zone_name(); // Get the zone name
$shipping_class_target = 87;
$in_cart = 0;
foreach ( WC()->cart->get_cart_contents() as $key => $values ) {
if ( $values[ 'data' ]->get_shipping_class_id() == $shipping_class_target ) {
$in_cart = 1;
break;
}
}
if ( $zone_name=='Locations not covered by your other zones' && $in_cart==1 || $zone_id==0 && $in_cart==1 ) {
//wc_print_notice( __( '<p>Count_ships: ' .$counter_ship. ' Cart id: ' . $in_cart . ' | Zone id: ' . $zone_id . ' | Zone name: ' . $zone_name . '</p>', 'woocommerce' ), 'success' );
wc_print_notice( __( '<b>ONLY PICKUP IS AVAILABLE</b><br>To have courier delivery, please goto your cart and remove products which cannot be shipped', 'woocommerce' ), 'success' );
} else {
//donothing
}
}
I have two Issues:
1- The notice check doesn't retrigger each time the address changes
2- I use the Multi-Step Checkout Pro for WooCommerce by Silkypress and want this notice to come up during the ORDER section...however the above notice doesn't work at all when I activate this plugin.
I've contacted them for support, but appreciate any help.
Alternatively, you can think differently, automatically disabling all shipping methods except local pickup (if there is at least one product in the cart with the shipping class id equal to the one specified).
Based on:
Hide shipping methods for specific shipping class in WooCommerce
Hide shipping methods for specific shipping classes in WooCommerce
Then you can use a custom function to check if there are products in the cart with a specific shipping class id:
// check if the products in the cart have a specific shipping class id
function check_product_in_cart_according_shipping_class_id() {
$shipping_class_target = 87;
$in_cart = false;
// check if in the cart there is at least one product with the shipping class id equal to "87"
foreach ( WC()->cart->get_cart() as $cart_item ) {
$product = $cart_item['data'];
$shipping_class = $product->get_shipping_class_id();
if ( $shipping_class == $shipping_class_target ) {
$in_cart = true;
return $in_cart;
}
}
return $in_cart;
}
And with another custom function you get the list of products that cannot be shipped (and therefore those that have the shipping class id equal to 87):
// gets products that have a specific shipping class
function get_product_in_cart_according_shipping_class_id() {
$shipping_class_target = 87;
$product_list = '<ul>';
// check if in the cart there is at least one product with the shipping class id equal to "87"
foreach ( WC()->cart->get_cart() as $cart_item ) {
$product = $cart_item['data'];
$shipping_class = $product->get_shipping_class_id();
if ( $shipping_class == $shipping_class_target ) {
$sku = $product->get_sku();
$name = $product->get_name();
$qty = $cart_item['quantity'];
$product_list .= '<li>' . $qty . ' x ' . $name . ' (' . $sku . ')</li>';
}
}
$product_list .= '</ul>';
return $product_list;
}
Then disable all shipping methods except local pickup:
// disable all shipping methods except local pickup based on product shipping class
add_filter( 'woocommerce_package_rates', 'disable_shipping_methods_based_on_product_shipping_class', 10, 2 );
function disable_shipping_methods_based_on_product_shipping_class( $rates, $package ) {
$in_cart = check_product_in_cart_according_shipping_class_id();
// if it is present, all shipping methods are disabled except local pickup
if ( $in_cart ) {
foreach( $rates as $rate_key => $rate ) {
if ( $rate->method_id != 'local_pickup' ) {
unset($rates[$rate_key]);
}
}
}
return $rates;
}
Finally, it adds the custom notice on the cart and checkout page.I created two functions because on the cart page the notice needs to update when a product is added or removed and using the woocommerce_before_calculate_totals hook would cause multiple notices in the checkout.
In the cart:
// add custom notice in cart
add_action( 'woocommerce_before_calculate_totals', 'add_custom_notice_in_cart' );
function add_custom_notice_in_cart() {
// only on the cart page
if ( ! is_cart() ) {
return;
}
$in_cart = check_product_in_cart_according_shipping_class_id();
if ( $in_cart ) {
$products = get_product_in_cart_according_shipping_class_id();
wc_clear_notices();
wc_add_notice( sprintf( __( 'Only <strong>Local Pickup</strong> shipping method is available. These products cannot be shipped:<br>%s', 'woocommerce' ), $products ), 'notice' );
}
}
In the checkout:
// add custom notice in checkout
add_action( 'woocommerce_checkout_before_customer_details', 'add_custom_notice_in_checkout' );
function add_custom_notice_in_checkout() {
$in_cart = check_product_in_cart_according_shipping_class_id();
if ( $in_cart ) {
$products = get_product_in_cart_according_shipping_class_id();
wc_clear_notices();
wc_add_notice( sprintf( __( 'Only <strong>Local Pickup</strong> shipping method is available. These products cannot be shipped:<br>%s', 'woocommerce' ), $products ), 'notice' );
}
}
The code has been tested and works. Add it to your active theme's functions.php.
On the checkout page of my WooCommerce based site, users will have a list of shipping methods to choose from depending on what they are purchasing
This is the list I want to capture:
Things like
“Free Shipping” for orders over a certain price
“Freight Shipping” for certain items, and so on.
My goal, is to display ALL available methods for each specific order, and display it on the “Edit Order / Order Details” page in the Admin view.
A small feature that would help us be able to quickly identify what option people are choosing more often, depending on the choices that they have available.
This is what I have so far:
add_action( 'woocommerce_checkout_update_order_meta', 'save_available_shipping_methods' );
function save_available_shipping_methods( $order_id ) {
$shippingmethods = WC()->cart->get_shipping_methods();
update_post_meta( $order_id, '_shipping_methods', $shippingmethods );
}
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'get_available_shipping_methods', 10, 1 );
function get_available_shipping_methods($order){
$order = wc_get_order( $order_id );
if ( $order ) {
echo '<p><strong>'.__('Available Shipping Methods: ').'</strong> ' . get_post_meta($order->get_shipping_methods(), '_shipping_field_value', true ) . '</p>';
}
}
I'll attach a picture as well to maybe make things a little easier to follow.
This is where I would like to put it. The current hook places the field underneath the shipping address
In the woocommerce_checkout_update_order_meta hook, the available shipping packages are stored via update_post_meta
Comments with explanation added in the code
function action_woocommerce_checkout_update_order_meta( $order_id ) {
// Get shipping packages
$packages = WC()->shipping()->get_packages();
// Set array
$rate_labels = array();
// Loop trough packages
foreach ( $packages as $key => $package ) {
// Loop through package rates
foreach( $package['rates'] as $rate_id => $rate ) {
// Push to array
$rate_labels[] = $rate->get_label();
}
}
// NOT empty
if ( ! empty ( $rate_labels ) ) {
// Update post meta
update_post_meta( $order_id, '_available_shipping_methods', $rate_labels );
}
}
add_action( 'woocommerce_checkout_update_order_meta', 'action_woocommerce_checkout_update_order_meta', 10, 1 );
// Display on the order edit page (backend)
function action_woocommerce_admin_order_data_after_shipping_address( $order ) {
// Get meta
$rate_labels = $order->get_meta( '_available_shipping_methods' );
// True
if ( $rate_labels ) {
// Loop trough rate labels
foreach( $rate_labels as $rate_label ) {
// Output
echo '<p>' . $rate_label . '</p>';
}
}
}
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'action_woocommerce_admin_order_data_after_shipping_address', 10, 1 );
In case anyone is wondering, this is the final result. It will loop through each order to find the available shipping methods at the time, along with how much it was quoted for.
// Capture the available shipping methods, and costs:
function action_woocommerce_checkout_update_order_meta( $order_id ) {
// Get shipping packages
$packages = WC()->shipping()->get_packages();
// Set array
$rate_labels = array();
$rate_costs = array();
// Loop trough packages
foreach ( $packages as $key => $package ) {
// Loop through package rates
foreach( $package['rates'] as $rate_id => $rate ) {
// Push to array
$rate_labels[] = $rate->get_label();
$rate_costs[] = $rate->get_cost();
}
}
// NOT empty
if ( ! empty ( $rate_labels ) ) {
// Update post meta
update_post_meta( $order_id, '_available_shipping_methods', $rate_labels );
update_post_meta( $order_id, '_available_shipping_method_cost', $rate_costs );
}
}
add_action( 'woocommerce_checkout_update_order_meta', 'action_woocommerce_checkout_update_order_meta', 10, 1 );
// Make it display on the edit order page:
function action_woocommerce_admin_order_data_after_shipping_address( $order ) {
// Get meta
$rate_labels = $order->get_meta( '_available_shipping_methods' );
$rate_costs = $order->get_meta( '_available_shipping_method_cost' );
$methods = array ( $rate_labels, $rate_costs );
// True
if ( $rate_labels ) {
// Loop
echo '<p><strong>Shipping Methods: </strong>';
foreach(array_combine($rate_labels, $rate_costs) as $rate_label => $rate_cost) {
echo '<p>' . $rate_label . ' - $' . $rate_cost . '</p>';
}
}
}
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'action_woocommerce_admin_order_data_after_shipping_address', 10, 1 );
I use code that displays a custom field on the product edit page. This text box shows the cooking time on the single product page.
Here is the code:
// Backend: Display additional product fields
add_action( 'woocommerce_product_options_general_product_data', 'add_time_field_general_product_data' );
function add_time_field_general_product_data() {
// Custom Time Field
woocommerce_wp_text_input( array(
'id' => '_custom_time',
'label' => __( 'Time for cooking', 'woocommerce' ),
));
}
// Backend: Save the data value from the custom fields
add_action( 'woocommerce_admin_process_product_object', 'save_time_custom_fields_values' );
function save_time_custom_fields_values( $product ) {
// Save Custom Time Field
if( isset( $_POST['_custom_time'] ) ) {
$product->update_meta_data( '_custom_time', sanitize_text_field( $_POST['_custom_time'] ) );
}
}
// Display custom fields values under item name in checkout
add_filter( 'woocommerce_checkout_cart_item_quantity', 'custom_time_field_checkout_item_name', 10, 3 );
function custom_time_field_checkout_item_name( $item_qty, $cart_item, $cart_item_key ) {
if( $value4 = $cart_item['data']->get_meta('_custom_time') ) {
$item_qty .= '<br /><div class="my-custom-style"><strong>' . __("Time for cooking", "woocommerce") . ':</strong> ' . $value4 . ' min.</div>';
}
return $item_qty;
}
// Display custom fields values on orders and email notifications
add_filter( 'woocommerce_order_item_name', 'custom_time_field_order_item_name', 10, 2 );
function custom_time_field_order_item_name( $item_name, $item ) {
$product = $item->get_product();
if( $value4 = $product->get_meta('_custom_time') ) {
$item_name .= '<br /><span class="my-custom-style"><strong>' . __("Time for cooking", "woocommerce") . ':</strong> ' . $value4 . ' min.</span>';
}
return $item_name;
}
How can get the following functionality based on this code?
For example, a customer adds several dishes to a cart and checkout order. He sees how much time will be cooking this or that dish.
But when the customer chooses the delivery by courier (Flat Rate), in the same block, the delivery time is shown.
Flat Rate = $10
Delivery time = (the longest cooking time is selected from the order) min. + 45 min.
As I understand it, need to get the data of the custom field '_custom_time' when placing the order. Then, somehow need to get the highest value of this field and add 45 minutes.
I ask for your help! I hope that the answer to this question will be useful to many developers.
Try the following codethat will display a delivery time (calculated from the highest item cooking time value + 45 minutes) when a "flat rate" shipping method is selected on checkout page:
add_action( 'woocommerce_after_shipping_rate', 'action_after_shipping_rate_callback', 10, 2 );
function action_after_shipping_rate_callback( $method, $index ) {
$chosen_shipping_id = WC()->session->get( 'chosen_shipping_methods' )[$index];
if( is_checkout() && $method->method_id === 'flat_rate' && $method->id === $chosen_shipping_id ) {
$extra_time = 45; // Additional time to be added
$data_array = []; // Initializing
// Loop through car items
foreach ( WC()->cart->get_cart() as $cart_item ) {
if( $cooking_time = $cart_item['data']->get_meta('_custom_time') ) {
$data_array[] = (int) $cooking_time;
}
}
if ( sizeof($data_array) ) {
$max_time = (int) max($data_array);
$delivery_time = $max_time + $extra_time;
echo '<br><small style="margin-left:2em;border:solid 1px #ccc;padding:2px 5px;"><strong>' . __("Delivery time", "woocommerce") . '</strong>: ' . $delivery_time . ' min.</small>';
}
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
To enable that in cart page too, remove is_checkout() && from the IF statement.
I have implemented a custom HTML Form and asking for some data which my customers will pass to place order successfully. Without these details my order has no importance.
For HTML form, I am referencing some custom PHP script which is below and which processes POST data from the Form and creates Cart with these data programmatically. Thanks #LoicTheAztec to help me achieve this.
The script.php file code:
<?php
require_once("../wp-load.php");
$customer_name = $_POST["customer_name"];
$customer_email = $_POST["customer_email"];
$customer_sex = $_POST["customer_sex"];
$customer_age = $_POST["customer_age"];
$product_id = $_POST["product_id"];
$custom_data = array(); // Initializing
if( isset($_POST['customer_name']) && ! empty($_POST['customer_name']) )
$custom_data['custom_data']['name'] = $_POST['customer_name'];
if( isset($_POST['customer_email']) && ! empty($_POST['customer_email']) )
$custom_data['custom_data']['email'] = $_POST['customer_email'];
if( isset($_POST['customer_sex']) && ! empty($_POST['customer_sex']) )
$custom_data['custom_data']['sex'] = $_POST['customer_sex'];
if( isset($_POST['customer_age']) && ! empty($_POST['customer_age']) )
$custom_data['custom_data']['age'] = $_POST['customer_age'];
global $woocommerce;
if (WC()->cart->add_to_cart( $product_id, '1', '0', array(), $custom_data )) {
var_dump($product_id);
} else {
var_dump($customer_name);
}
header("Location: ./checkout");
?>
As you see we have programmatically created a cart item using WC_Cart add_to_cart() method. So, all custom data is being saved to Cart as custom cart item data.
And now, we have also placed a code block into functions.php to print these data on Checkout page.
add_action( 'woocommerce_after_order_notes', 'my_custom_checkout_field' );
function my_custom_checkout_field( $checkout ) {
//var_dump($checkout);
global $woocommerce;
echo '<div id="my_custom_checkout_field"><h2>' . __('Child Info') . '</h2>';
foreach ( $woocommerce->cart->get_cart() as $cart_item ) {
if( isset($cart_item['custom_data']) ) {
$custom_data = $cart_item['custom_data'];
echo("<div>Name: <strong>" . $custom_data['name'] . "</strong></div>");
echo("<div>Email: <strong>" . $custom_data['email'] . "</strong></div>");
echo("<div>Gender: <strong>" . $custom_data['sex'] . "</strong></div>");
echo("<div>Age: <strong>" . $custom_data['age'] . "</strong></div>");
}
}
echo '</div>';
}
Now, I am trying to add these data printed on Checkout page to the Order page as well. As my order can't be completed without these data, user need to fill up these data and when he creates order, these data needs to be passed to the Order Summary page as well. And admin also needs to be able to see these data so he can process the order.
I hope this description clears everything and thanks again #LoicTheAztec to make me able to do this. Thank you very much.
Update 2 - Two steps
1) Saving data:
We will save this custom customer data as "Hidden" order "item" meta data and then as order meta data too as this is related to subscriptions with a unique order item:
// Utility function: array of custom customer key/label pairs
function custom_data_keys_labels(){
return array(
'name' => __('Customer name'), 'email' => __('Customer email'),
'sex' => __('Customer gender'), 'age' => __('Customer age'),
);
}
// Add/save custom field value as custom HIDDEN order item meta data
add_action( 'woocommerce_checkout_create_order_line_item', 'custom_field_update_order_item_meta', 20, 4 );
function custom_field_update_order_item_meta( $item, $cart_item_key, $values, $order ) {
if ( ! isset( $values['custom_data'] ) )
return;
$custom_data = $values['custom_data'];
$meta_data = array();
$labels_keys = custom_data_keys_labels();
foreach( $labels_keys as $key => $label ){
if ( isset( $custom_data[$key] ) )
$meta_data[$key] = $custom_data[$key];
}
if ( sizeof( $meta_data ) > 0 )
$item->update_meta_data( __('_customer_data'), $meta_data );
return $cart_item_data;
}
// Add/save custom fields values as custom order meta data
add_action( 'woocommerce_checkout_create_order', 'my_custom_checkout_field_update_order_meta', 20, 2 );
function my_custom_checkout_field_update_order_meta( $order, $data ) {
$order_items = $order->get_items(); // Order itesm
$item = reset($order_items); // Keep only the first order item
$item_data = $item->get_meta( '_customer_data' ); // Get custom customer data
if( is_array($item_data) && sizeof($item_data) > 0 ){
foreach( $item_data as $key => $value ) {
if ( isset( $item_data[$key] ) )
$order->update_meta_data( '_customer_' . $key, $value );
}
// Mark as data saved
$order->update_meta_data( '_customer_data_set', true );
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
2) Displaying saved custom data:
The code below also use our utility function custom_data_keys_labels()…
// Order pages (frontend and admin) display
add_filter( 'woocommerce_order_details_after_order_table' , 'display_admin_order_meta_cutom_data', 20, 1 ); // Front
add_action( 'woocommerce_admin_order_data_after_billing_address', 'display_admin_order_meta_cutom_data', 20, 1 ); // Admin
function display_admin_order_meta_cutom_data( $order ){
$labels_keys = custom_data_keys_labels();
if( $order->get_meta( '_customer_data_set' ) ){
if( is_admin() ){ // Admin
echo '<p>';
foreach( $labels_keys as $key => $label ){
if ( $order->get_meta( '_customer_' . $key ) )
echo '<strong>' . $label . ':</strong> ' . $order->get_meta( '_customer_' . $key ) . '<br>';
}
echo '</p>';
}
else { // Front end: order view and Order received (thankyou)
echo '<table class="woocommerce-table"><tbody>';
foreach( $labels_keys as $key => $label ){
if ( $order->get_meta( '_customer_' . $key ) )
echo '<tr><th>' . $label . ':</th><td>' . $order->get_meta( '_customer_' . $key ) . '</td></tr>';
}
echo '</tbody></table>';
}
}
}
// Email notifications display
add_filter( 'woocommerce_email_order_meta_fields' , 'display_email_cutom_data', 20, 3 );
function display_email_cutom_data ( $fields, $sent_to_admin, $order ) {
$labels_keys = custom_data_keys_labels();
if( $order->get_meta( '_customer_data_set' ) ){
foreach( $labels_keys as $key => $label ){
$fields['customer_' . $key] = array(
'label' => $label,
'value' => $order->get_meta( '_customer_' . $key ),
);
}
}
return $fields;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.