Add a New Custom Field to Woocommerce Checkout and Display on Admin Order Pages and Email Notifications

Add a new custom field to WooCommerce checkout and display on admin order pages and email notifications

You're using delivery_date, city_custom & Citymixed up, this gives you different values, so that is your problem

// Display a custom checkout select field after Billing form
add_action( 'woocommerce_after_checkout_billing_form', 'my_custom_checkout_field', 10, 1 );
function my_custom_checkout_field( $checkout ) {
echo '<div id="my_custom_checkout_field">
' . __('City') . '';

woocommerce_form_field( 'city_custom', array(
'type' => 'select',
'options' => array(
'Colombo 01' => __('Colombo 01', 'woocommerce' ),
'Colombo 02' => __('Colombo 02', 'woocommerce' ),
'Colombo 03' => __('Colombo 03', 'woocommerce' ),
'Colombo 04' => __('Colombo 04', 'woocommerce' ),
'Colombo 06' => __('Colombo 06', 'woocommerce' ),
'Colombo 07' => __('Colombo 07', 'woocommerce' ),
'Colombo 08' => __('Colombo 08', 'woocommerce' ),
'Colombo 09' => __('Colombo 09', 'woocommerce' ),
'Colombo 10' => __('Colombo 10', 'woocommerce' ),
'Colombo 11' => __('Colombo 11', 'woocommerce' ),
'Colombo 01' => __('Colombo 01', 'woocommerce' ),
'Dehiwela' => __('Dehiwela', 'woocommerce' ),
'Attidiya' => __('Attidiya', 'woocommerce' ),
'Mount Lavinia' => __('Mount Lavinia', 'woocommerce' ),
'Ratmalana' => __('Ratmalana', 'woocommerce' ),
'Maligawatte' => __('Maligawatte', 'woocommerce' ),
'Kotikawatta' => __('Kotikawatta', 'woocommerce' ),
'Rajagiriya' => __('Rajagiriya', 'woocommerce' ),
'Peliyagoda' => __('Peliyagoda', 'woocommerce' ),
'Kelaniya' => __('Kelaniya', 'woocommerce' ),
'Kiribathgoda' => __('Kiribathgoda', 'woocommerce' ),
'Wattala' => __('Wattala', 'woocommerce' )),
'class' => array('my-field-class form-row-wide'),
'placeholder' => __('Select City'),
), $checkout->get_value( 'city_custom' ));

echo '</div>';
}

// Save the dropdown custom field selected value as order custom meta data:
add_action( 'woocommerce_checkout_create_order', 'my_custom_checkout_field_update_order_meta', 10, 2 );
function my_custom_checkout_field_update_order_meta( $order, $data ) {
if ( isset($_POST['city_custom']) && ! empty($_POST['city_custom']) ) {
$order->update_meta_data( 'city', sanitize_text_field( $_POST['city_custom'] ) );
}
}

// Display the custom field value on admin order pages after billing adress:
add_action( 'woocommerce_admin_order_data_after_billing_address', 'my_custom_checkout_field_display_admin_order_meta', 10, 1 );
function my_custom_checkout_field_display_admin_order_meta( $order ) {
echo '<p><strong>'.__('City').':</strong> ' . $order->get_meta('city') . '</p>';
}

// Display the custom field value on email notifications:
add_action( 'woocommerce_email_after_order_table', 'custom_woocommerce_email_order_meta_fields', 10, 4 );
function custom_woocommerce_email_order_meta_fields( $order, $sent_to_admin, $plain_text, $email ) {
echo '<p><strong>'.__('City').':</strong> ' . $order->get_meta('city') . '</p>';
}

Save and display custom checkout field in WooCommerce

I have also revisited a bit your code. To save your custom checkout field value as custom order meta data and display it on orders and email notifications, use the following:

// Checkout: Display a custom checkout field (dropdown)
add_action( 'woocommerce_before_order_notes', 'wps_add_select_checkout_field' );
function wps_add_select_checkout_field( $checkout ) {
echo '<h2>'.__('Select a Good Cause').'</h2>';

$posts = get_posts( array(
'post_type' => 'good_cause',
'posts_per_page' => -1,
) );

$options = array(); // Initializing

foreach ( $posts as $value ) {
$options[$value->ID] = $value->post_title;
}

woocommerce_form_field( 'daypart', array(
'type' => 'select',
'class' => array( 'wps-drop' ),
'label' => __( 'Delivery options' ),
'options' => $options,
), $checkout->get_value( 'daypart' ));
}

// Save chosen custom field option as custom order meta data
add_action( 'woocommerce_checkout_create_order', 'action_checkout_create_order' );
function action_checkout_create_order( $order ) {
$key_field = 'daypart';

if( isset($_POST[$key_field]) && $_POST[$key_field] > 0 ) {
$post = get_post( intval($_POST[$key_field]) ); // Get the post object from the post ID

$order->update_meta_data( $key_field, $post->post_title); // Save the post title
$order->update_meta_data( $key_field.'_id', $post->ID); // Save post ID (optional if needed)
}
}

// Display on admin orders
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'display_chosen_delivery_on_admin_orders' );
function display_chosen_delivery_on_admin_orders( $order ){
if( $daypart = $order->get_meta( 'daypart' ) ) {
$daypart_id = $order->get_meta( 'daypart_id' );
$daypart_id = empty($daypart_id) ? '' : ' ('. $daypart_id .')';

// Display the delivery option (post title + post id)
echo '<p><strong>' . __("Delivery") . ':</strong> ' . $daypart . $daypart_id . '</p>';
}
}

// Display on customer orders and email notifications
add_filter( 'woocommerce_get_order_item_totals', 'display_delivery_on_order_item_totals', 10, 3 );
function display_delivery_on_order_item_totals( $total_rows, $order, $tax_display ){
if( $daypart = $order->get_meta( 'daypart' ) ) {
$new_total_rows = [];

// Loop through order total rows
foreach( $total_rows as $key => $values ) {
// Inserting before payment method
if( $key === 'payment_method' ) {
$new_total_rows[$field_id] = array(
'label' => __("Delivery", "woocommerce") . ':',
'value' => $daypart,
);
}
$new_total_rows[$key] = $values;
}
return $new_total_rows;
}
return $total_rows;
}

Code goes in functions.php file of the active child theme (or active theme). Tested and works.

Display custom field between billing address in WooCommerce admin order edit pages

The output is a string, which uses <br/> to separate the order billing address details.

So there are 2 possible views:

  • company_name<br/>Firstname Lastname<br/>street<br/>1<br/>1000 Location
  • Firstname Lastname<br/>street<br/>1<br/>1000 Location

There are several possibilities that you can apply to answer your question, but the easiest is to edit the string via the woocommerce_order_get_formatted_billing_address filter hook.

Note: we are going to check whether there is actually a billing company,
and adjust our result based on that.

Note: adjust $order->get_meta( '_your_meta_key' ); to your needs.

So you get:

function str_replace_first( $search, $replace, $subject ) {
$pos = strpos( $subject, $search );
if ( $pos !== false ) {
return substr_replace( $subject, $replace, $pos, strlen( $search ) );
}
return $subject;
}

/**
* Filter orders formatterd billing address.
*
* @since 3.8.0
* @param string $address Formatted billing address string.
* @param array $raw_address Raw billing address.
* @param WC_Order $order Order data. @since 3.9.0
*/
function filter_woocommerce_order_get_formatted_billing_address( $address, $raw_address, $order ) {
// Get meta
$value = $order->get_meta( '_your_meta_key' );

// When empty, return
if ( empty ( $value ) ) return $address;

// Get billing company
$billing_company = $order->get_billing_company();

// Not empty
if ( ! empty ( $billing_company ) ) {
$address = str_replace_first( '<br/>', '<br/>' . $value . '<br/>', $address );
} else {
$address = $value . '<br/>' . $address;
}

return $address;
}
add_filter( 'woocommerce_order_get_formatted_billing_address', 'filter_woocommerce_order_get_formatted_billing_address', 10, 3 );

Result:

  • company_name<br/>meta_value<br/>Firstname Lastname<br/>street<br/>1<br/>1000 Location
  • meta_value<br/>Firstname Lastname<br/>street<br/>1<br/>1000 Location

Used in this answer: https://stackoverflow.com/a/2606638/11987538

Add and display custom cart item data in WooCommerce orders and emails

There are some mistakes in your last function… Instead replace it with the following:

/**
* Set custom field as visible order item meta data (displayed on orders and email notifications)
*/
add_action( 'woocommerce_checkout_create_order_line_item', 'hpplrs_add_custom_data_to_order', 10, 4 );
function hpplrs_add_custom_data_to_order( $item, $cart_item_key, $values, $order ) {
if( isset($values['title_field']) && ! empty($values['title_field']) ) {
$item->add_meta_data( __( 'Size', 'hpplrs' ), $values['title_field'] );
}
}

Tested and works. Now your custom field will be displayed on orders and email notifications.

Displaying a second custom field on WooCommerce email notifications

You're missing the correct meta_key

Use it like this instead:

function custom_woocommerce_email_order_meta_fields( $fields, $sent_to_admin, $order ) {
// Get meta
$shipping_phone = $order->get_meta( '_shipping_phone', true );

// NOT empty
if( ! empty( $shipping_phone ) ) {
$fields['_shipping_phone'] = array(
'label' => __( 'Shipping Phone' ),
'value' => $shipping_phone,
);
}

// Get (other) meta
$shipping_email = $order->get_meta( '_shipping_email', true );

// NOT empty
if ( ! empty( $shipping_email ) ) {
$fields['_shipping_email'] = array(
'label' => __( 'Shipping Email' ),
'value' => $shipping_email,
);
}

return $fields;
}
add_filter( 'woocommerce_email_order_meta_fields', 'custom_woocommerce_email_order_meta_fields', 10, 3 );

Display a WooCommerce custom checkout field value on email notification

Updated - There are some small mistakes like:

  • Since Woocommerce 3, to get the order ID from the Order object use: $order->get_id()
  • On your last function, there is a missing parameter variable from the hooked function, So you can't get the correct WC_Order Object $order variable required in your code.
  • The correct post meta key is

Optional advices:

  • It's better to use woocommerce_checkout_create_order action hook introduce in Woocommerce 3, instead of old woocommerce_checkout_update_order_meta action hook.
  • You can use WC_Data get_meta() method with the WC_Order object instead of get_post_meta()

Here is your complete revisited code:

// Display a custom checkout select field after Order notes
add_action( 'woocommerce_after_order_notes', 'my_custom_checkout_field', 10, 1 );
function my_custom_checkout_field( $checkout ) {
echo '<div id="my_custom_checkout_field">
<h2>' . __('Bezorg moment') . '</h2>';

woocommerce_form_field( 'delivery_date', array(
'type' => 'select',
'options' => array(
'17:30-18:00' => __('17:30 - 18:00', 'woocommerce' ),
'18:00-18:30' => __('18:00 - 18:30', 'woocommerce' )),
'class' => array('my-field-class form-row-wide'),
'label' => __('Bezorgtijd'),
'placeholder' => __('Zo snel mogelijk'),
), $checkout->get_value( 'delivery_date' ));

echo '</div>';
}

// Save the dropdown custom field selected value as order custom meta data:
add_action( 'woocommerce_checkout_create_order', 'my_custom_checkout_field_update_order_meta', 10, 2 );
function my_custom_checkout_field_update_order_meta( $order, $data ) {
if ( isset($_POST['delivery_date']) && ! empty($_POST['delivery_date']) ) {
$order->update_meta_data( 'Bezorg moment', sanitize_text_field( $_POST['delivery_date'] ) );
}
}

// Display the custom field value on admin order pages after billing adress:
add_action( 'woocommerce_admin_order_data_after_billing_address', 'my_custom_checkout_field_display_admin_order_meta', 10, 1 );
function my_custom_checkout_field_display_admin_order_meta( $order ) {
echo '<p><strong>'.__('Bezorg moment').':</strong> ' . $order->get_meta('Bezorg moment') . '</p>';
}

// Display the custom field value on email notifications:
add_action( 'woocommerce_email_after_order_table', 'custom_woocommerce_email_order_meta_fields', 10, 4 );
function custom_woocommerce_email_order_meta_fields( $order, $sent_to_admin, $plain_text, $email ) {
echo '<p><strong>'.__('Bezorg moment').':</strong> ' . $order->get_meta('Bezorg moment') . '</p>';
}

Code goes in function.php file of your active child theme (or active theme). Tested and works.


If you want to target "New Order" email notification only use this instead:

// Display the custom field value on "New Order" notification:
add_action( 'woocommerce_email_after_order_table', 'custom_woocommerce_email_order_meta_fields', 10, 4 );
function custom_woocommerce_email_order_meta_fields( $order, $sent_to_admin, $plain_text, $email ) {
if( 'new_order' === $email->id )
echo '<p><strong>'.__('delivery_date').':</strong> ' . $order->get_meta('Bezorg moment') . '</p>';
}

Sample Image



Related Topics



Leave a reply



Submit