Woocommerce:Add Custom Metabox to Admin Order Page

WooCommerce : Add custom Metabox to admin order page

UPDATE 2017/11/02 (Works perfectly in Woocommerce 3+)

Firstly, I have gotten everything working as expected, except getting the value for my_field_name in the back end "Custom fields" Metabox within Order pages.

Then after a real nightmare, I have found a pretty nice working solution, better than before. In the back end you have now a Custom metabox with the custom field my_field_name displaying the right value, like in this screenshot:

screenshot


My code is divided into 2 parts.

  1. The backend Metabox in Order pages, with an editable field showing the correct value coming from a custom field on the product pages (in the front end):
// Adding Meta container admin shop_order pages
add_action( 'add_meta_boxes', 'mv_add_meta_boxes' );
if ( ! function_exists( 'mv_add_meta_boxes' ) )
{
function mv_add_meta_boxes()
{
add_meta_box( 'mv_other_fields', __('My Field','woocommerce'), 'mv_add_other_fields_for_packaging', 'shop_order', 'side', 'core' );
}
}

// Adding Meta field in the meta container admin shop_order pages
if ( ! function_exists( 'mv_add_other_fields_for_packaging' ) )
{
function mv_add_other_fields_for_packaging()
{
global $post;

$meta_field_data = get_post_meta( $post->ID, '_my_field_slug', true ) ? get_post_meta( $post->ID, '_my_field_slug', true ) : '';

echo '<input type="hidden" name="mv_other_meta_field_nonce" value="' . wp_create_nonce() . '">
<p style="border-bottom:solid 1px #eee;padding-bottom:13px;">
<input type="text" style="width:250px;" name="my_field_name" placeholder="' . $meta_field_data . '" value="' . $meta_field_data . '"></p>';

}
}

// Save the data of the Meta field
add_action( 'save_post', 'mv_save_wc_order_other_fields', 10, 1 );
if ( ! function_exists( 'mv_save_wc_order_other_fields' ) )
{

function mv_save_wc_order_other_fields( $post_id ) {

// We need to verify this with the proper authorization (security stuff).

// Check if our nonce is set.
if ( ! isset( $_POST[ 'mv_other_meta_field_nonce' ] ) ) {
return $post_id;
}
$nonce = $_REQUEST[ 'mv_other_meta_field_nonce' ];

//Verify that the nonce is valid.
if ( ! wp_verify_nonce( $nonce ) ) {
return $post_id;
}

// If this is an autosave, our form has not been submitted, so we don't want to do anything.
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return $post_id;
}

// Check the user's permissions.
if ( 'page' == $_POST[ 'post_type' ] ) {

if ( ! current_user_can( 'edit_page', $post_id ) ) {
return $post_id;
}
} else {

if ( ! current_user_can( 'edit_post', $post_id ) ) {
return $post_id;
}
}
// --- Its safe for us to save the data ! --- //

// Sanitize user input and update the meta field in the database.
update_post_meta( $post_id, '_my_field_slug', $_POST[ 'my_field_name' ] );
}
}



  1. Front end / Back end:

• The product page custom field (front end).

• Displaying this data on cart, checkout pages and thank you order (front end).

• Displaying data on the order page (back end)

// Add the field to the product
add_action('woocommerce_before_add_to_cart_button', 'my_custom_product_field');
function my_custom_product_field() {
echo '<div id="my_custom_field">
<label>' . __( 'My Field') . ' </label>
<input type="text" name="my_field_name" value="">
</div><br>';
}

// Store custom field
add_filter( 'woocommerce_add_cart_item_data', 'save_my_custom_product_field', 10, 2 );
function save_my_custom_product_field( $cart_item_data, $product_id ) {
if( isset( $_REQUEST['my_field_name'] ) ) {
$cart_item_data[ 'my_field_name' ] = $_REQUEST['my_field_name'];
// below statement make sure every add to cart action as unique line item
$cart_item_data['unique_key'] = md5( microtime().rand() );
WC()->session->set( 'my_order_data', $_REQUEST['my_field_name'] );
}
return $cart_item_data;
}

// Add a hidden field with the correct value to the checkout
add_action( 'woocommerce_after_order_notes', 'my_custom_checkout_field' );
function my_custom_checkout_field( $checkout ) {
$value = WC()->session->get( 'my_order_data' );
echo '<div id="my_custom_checkout_field">
<input type="hidden" class="input-hidden" name="my_field_name" id="my_field_name" value="' . $value . '">
</div>';
}

// Save the order meta with hidden field value
add_action( 'woocommerce_checkout_update_order_meta', 'my_custom_checkout_field_update_order_meta' );
function my_custom_checkout_field_update_order_meta( $order_id ) {
if ( ! empty( $_POST['my_field_name'] ) ) {
update_post_meta( $order_id, '_my_field_slug', $_POST['my_field_name'] );
}
}

// Display field value on the order edit page (not in custom fields metabox)
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){
$my_custom_field = get_post_meta( $order->id, '_my_field_slug', true );
if ( ! empty( $my_custom_field ) ) {
echo '<p><strong>'. __("My Field", "woocommerce").':</strong> ' . get_post_meta( $order->id, '_my_field_slug', true ) . '</p>';
}
}

// Render meta on cart and checkout
add_filter( 'woocommerce_get_item_data', 'render_meta_on_cart_and_checkout', 10, 2 );
function render_meta_on_cart_and_checkout( $cart_data, $cart_item = null ) {
$custom_items = array();
if( !empty( $cart_data ) ) $custom_items = $cart_data;

if( isset( $cart_item['my_field_name'] ) )
$custom_items[] = array( "name" => 'My Field', "value" => $cart_item['my_field_name'] );

return $custom_items;
}

// Add the information as meta data so that it can be seen as part of the order
add_action('woocommerce_add_order_item_meta','add_values_to_order_item_meta', 10, 3 );
function add_values_to_order_item_meta( $item_id, $cart_item, $cart_item_key ) {
// lets add the meta data to the order (with a label as key slug)
if( ! empty( $cart_item['my_field_name'] ) )
wc_add_order_item_meta($item_id, __('My field label name'), $cart_item['my_field_name'], true);
}

Everything is working as expected now.

Add a custom meta box on order edit pages and display it on the customer order pages

You can do it in multiple ways. I have corrected your code and added a custom hooked function at the end that will display this custom field value in my Account Order view pages:

// Add meta box
add_action( 'add_meta_boxes', 'tcg_tracking_box' );
function tcg_tracking_box() {
add_meta_box(
'tcg-tracking-modal',
'The Courier Guy Tracking',
'tcg_meta_box_callback',
'shop_order',
'side',
'core'
);
}

// Callback
function tcg_meta_box_callback( $post )
{
$value = get_post_meta( $post->ID, '_tracking_box', true );
$text = ! empty( $value ) ? esc_attr( $value ) : '';
echo '<input type="text" name="tracking_box" id="tcg_tracking_box" value="' . $text . '" />';
echo '<input type="hidden" name="tracking_box_nonce" value="' . wp_create_nonce() . '">';
}

// Saving
add_action( 'save_post', 'tcg_save_meta_box_data' );
function tcg_save_meta_box_data( $post_id ) {

// Only for shop order
if ( 'shop_order' != $_POST[ 'post_type' ] )
return $post_id;

// Check if our nonce is set (and our cutom field)
if ( ! isset( $_POST[ 'tracking_box_nonce' ] ) && isset( $_POST['tracking_box'] ) )
return $post_id;

$nonce = $_POST[ 'tracking_box_nonce' ];

// Verify that the nonce is valid.
if ( ! wp_verify_nonce( $nonce ) )
return $post_id;

// Checking that is not an autosave
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
return $post_id;

// Check the user’s permissions (for 'shop_manager' and 'administrator' user roles)
if ( ! current_user_can( 'edit_shop_order', $post_id ) && ! current_user_can( 'edit_shop_orders', $post_id ) )
return $post_id;

// Saving the data
update_post_meta( $post_id, '_tracking_box', sanitize_text_field( $_POST[ 'tracking_box' ] ) );
}

// Display To My Account view Order
add_action( 'woocommerce_order_details_after_order_table', 'tcg_display_tracking_box_in_order_view', 10, 1 );
function tcg_display_tracking_box_in_order_view( $order )
{
$tracking_box = get_post_meta( $order->get_id(), '_tracking_box', true );
// Output Tracking box
if( ! empty( $tracking_box ) && is_account_page() )
echo '<p>Tracking box: '. $tracking_box .'</p>';
}

Code goes in function.php file of your active child theme (or theme) or also in any plugin file.

This code is tested on woocommerce version 3+ and works

Metabox with multiple custom fields for WooCommerce admin order pages

Updated... You just need to have multiple different slugs in the same functions. For your desired 3 custom fields use the following:

// Add columns to admin orders table.
add_filter( 'manage_edit-shop_order_columns', 'set_custom_edit_shop_order_columns' );
function set_custom_edit_shop_order_columns($columns) {
$columns['courier_reference'] = __( 'Courier reference', 'woocommerce' );
$columns['tracking_number'] = __( 'Tracking number', 'woocommerce' );
$columns['shipping_date'] = __( 'Shipping date', 'woocommerce' );
return $columns;
}

// Add the data to the custom columns for the order post type:
add_action( 'manage_shop_order_posts_custom_column' , 'custom_shop_order_column', 10, 2 );
function custom_shop_order_column( $column, $post_id ) {
switch ( $column ) {

case 'courier_reference' :
echo esc_html( get_post_meta( $post_id, 'courier_reference', true ) );
break;

case 'tracking_number' :
echo esc_html( get_post_meta( $post_id, 'tracking_number', true ) );
break;

case 'shipping_date' :
echo esc_html( get_post_meta( $post_id, 'shipping_date', true ) );
break;

}
}

// Add a metabox.
add_action( 'add_meta_boxes', 'add_shop_order_meta_box' );
function add_shop_order_meta_box() {

add_meta_box(
'custom_meta_box',
__( 'Tracking information', 'woocommerce' ),
'shop_order_content_callback',
'shop_order'
);

}

// For displaying metabox content
function shop_order_content_callback( $post ) {

// Textarea Field
$courier_reference = get_post_meta( $post->ID, 'courier_reference', true );

echo '<p>' . __( 'Courier reference', 'woocommerce' ) . '<br>
<textarea style="width:100%" id="courier_reference" name="courier_reference">' . esc_attr( $courier_reference ) . '</textarea></p>';

// Text field
$tracking_number = get_post_meta( $post->ID, 'tracking_number', true );
echo '<p>' . __( 'Tracking number', 'woocommerce' ) . '<br>
<input type="text" style="width:100%" id="tracking_number" name="tracking_number" value="' . esc_attr( $tracking_number ) . '"></p>';

// Date picker field
$shipping_date = get_post_meta( $post->ID, 'shipping_date', true );

echo '<p>' . __( 'shipping_date', 'woocommerce' ) . '<br>
<input type="date" style="width:100%" id="shipping_date" name="shipping_date" value="' . esc_attr( $shipping_date ) . '"></p>';
}

// For saving the metabox data.
add_action( 'save_post_shop_order', 'save_shop_order_meta_box_data' );
function save_shop_order_meta_box_data( $post_id ) {

// If this is an autosave, our form has not been submitted, so we don't want to do anything.
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}

// Check the user's permissions.
if ( ! current_user_can( 'edit_shop_order', $post_id ) ) {
return;
}

// Make sure that 'shipping_date' is set.
if ( isset( $_POST['courier_reference'] ) ) {

// Update the meta field in the database.
update_post_meta( $post_id, 'courier_reference', sanitize_textarea_field( $_POST['courier_reference'] ) );
}

// Make sure that 'tracking_number' it is set.
if ( isset( $_POST['tracking_number'] ) ) {

// Update the meta field in the database.
update_post_meta( $post_id, 'tracking_number', sanitize_text_field( $_POST['tracking_number'] ) );
}

// Make sure that 'shipping_date' is set.
if ( isset( $_POST['shipping_date'] ) ) {

// Update the meta field in the database.
update_post_meta( $post_id, 'shipping_date', sanitize_text_field( $_POST['shipping_date'] ) );
}
}

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

How to add custom fields for WooCommerce order page (admin)?

Yes, just add_meta_box() to the post type shop_order and proceed as normal.

add_action( 'add_meta_boxes', 'add_meta_boxes' );

function add_meta_boxes()
{
add_meta_box(
'woocommerce-order-my-custom',
__( 'Order Custom' ),
'order_my_custom',
'shop_order',
'side',
'default'
);
}
function order_my_custom()
{
echo '<h1>Sample meta box</h1>';
}

Sample Image

Display product custom field as order item meta on WooCommerce admin order pages

You need to target order "line" items only and admin to avoid errors, as follow:

add_action( 'woocommerce_before_order_itemmeta', 'unit_before_order_itemmeta', 10, 3 );
function unit_before_order_itemmeta( $item_id, $item, $product ){
// Only "line" items and backend order pages
if( ! ( is_admin() && $item->is_type('line_item') ) ) return;

$unit = $product->get_meta('unite');
if( ! empty($unit) ) {
echo '<p>'.$unit.'</p>';
}
}

It should better work now without errors.

Get the custom field value in Woocommerce Edit Order pages custom metabox

Your main problem is located in your <imput> text field attribute name="Total USD Currency". It should be instead name="total-usd". Also the html id attributes should be unique (you can't set the same ID in your and in your html tags).

I have changed your code:

// Adding the metabox (on the right side)
add_action( 'add_meta_boxes', 'cpmb_add_meta_box');
function cpmb_add_meta_box() {

add_meta_box(
'woocommerce-order-my-custom',
__('USD Currency display'),
'cpmb_display_meta_box',
'shop_order',
'side',
'core'
);
}
// The metabox content
function cpmb_display_meta_box( $post ) {
// Get
$total_usd = get_post_meta( $post->ID, 'total-usd', true );

echo '<input type="hidden" name="cpmb_total_usd_nonce" value="' . wp_create_nonce() . '">
<label class="total-usd" for="total-usd">
<strong>Total USD Currency</strong></label><br />
<input type="text" id="total-usd" name="total-usd" value="' . $total_usd . '" placeholder="'. __("Enter Total here").'" />';
}

// Save/Update the meta data
add_action( 'save_post', 'cpmb_save_meta_box_data' );
function cpmb_save_meta_box_data( $post_id ) {

// Only for shop order
if ( 'shop_order' != $_POST[ 'post_type' ] )
return $post_id;

## Security verifications. ##

// Check if our nonce is set (and our cutom field)
if ( ! isset( $_POST[ 'cpmb_total_usd_nonce' ] ) && isset( $_POST['total-usd'] ) )
return $post_id;

$nonce = $_POST[ 'cpmb_total_usd_nonce' ];

// Verify that the nonce is valid.
if ( ! wp_verify_nonce( $nonce ) )
return $post_id;

// Checking that is not an autosave
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
return $post_id;

// Check the user’s permissions (for 'shop_manager' and 'administrator' user roles)
if ( ! current_user_can( 'edit_shop_order', $post_id ) && ! current_user_can( 'edit_shop_orders', $post_id ) )
return $post_id;

## SETTING AND UPDATING DATA (SECURITY PASSED) ##

update_post_meta( $post_id, 'total-usd', sanitize_text_field( $_POST[ 'total-usd' ] ) );
}

Code goes in function.php file of your active child theme (or theme) or also in any plugin file.

This code is tested on WooCommerce versions 3+ and works.

You will get this:

Sample Image

Metabox with multi checkbox in WooCommerce admin single orders

There was another issue with the function woocommerce_wp_multi_checkbox() that I have updated again (when used in a custom metabox).

I have also revisited all your code, specially the last function that saves the multi-checkboxes selected values.

The complete code:

// WooCommerce admin custom multi checkbox field function
function woocommerce_wp_multi_checkbox( $field ) {
global $thepostid, $post;

if( ! $thepostid ) {
$thepostid = $post->ID;
}

$field['value'] = get_post_meta( $thepostid, $field['id'], true );

$thepostid = empty( $thepostid ) ? $post->ID : $thepostid;
$field['class'] = isset( $field['class'] ) ? $field['class'] : 'select short';
$field['style'] = isset( $field['style'] ) ? $field['style'] : '';
$field['wrapper_class'] = isset( $field['wrapper_class'] ) ? $field['wrapper_class'] : '';
$field['value'] = isset( $field['value'] ) ? $field['value'] : array();
$field['name'] = isset( $field['name'] ) ? $field['name'] : $field['id'];
$field['desc_tip'] = isset( $field['desc_tip'] ) ? $field['desc_tip'] : false;

echo '<fieldset class="form-field ' . esc_attr( $field['id'] ) . '_field ' . esc_attr( $field['wrapper_class'] ) . '">
<legend>' . wp_kses_post( $field['label'] ) . '</legend>';

if ( ! empty( $field['description'] ) && false !== $field['desc_tip'] ) {
echo wc_help_tip( $field['description'] );
}

echo '<ul class="wc-radios">';

foreach ( $field['options'] as $key => $value ) {

echo '<li><label><input
name="' . esc_attr( $field['name'] ) . '"
value="' . esc_attr( $key ) . '"
type="checkbox"
class="' . esc_attr( $field['class'] ) . '"
style="' . esc_attr( $field['style'] ) . '"
' . ( is_array( $field['value'] ) && in_array( $key, $field['value'] ) ? 'checked="checked"' : '' ) . ' /> ' . esc_html( $value ) . '</label>
</li>';
}
echo '</ul>';

if ( ! empty( $field['description'] ) && false === $field['desc_tip'] ) {
echo '<span class="description">' . wp_kses_post( $field['description'] ) . '</span>';
}

echo '</fieldset>';
}

// Adding a custom Metabox on WooCommerce single orders
add_action( 'add_meta_boxes', 'add_custom_shop_order_metabox' );
function add_custom_shop_order_metabox(){
add_meta_box(
'custom_shop_order_metabox',
__('Employee Extra Actions', 'woocommerce'),
'content_custom_shop_order_metabox',
'shop_order',
'side',
'core'
);
}

// Custom Metabox content on WooCommerce single orders
function content_custom_shop_order_metabox() {
global $thepostid, $post;

echo '<div class="options_group">';

woocommerce_wp_multi_checkbox( array(
'id' => 'employee_actions12',
'name' => 'employee_actions12[]',
'label' => __('Levels', 'woocommerce'),
'options' => array(
'tee' => __( 'MBO', 'woocommerce' ),
'saa' => __( 'HBO', 'woocommerce' ),
'tee1' => __( 'WO', 'woocommerce' ),
),
) );

echo '</div>';
}

// Save WooCommerce single orders Custom Metabox field values
add_action( 'save_post_shop_order', 'save_custom_shop_order_metabox_field_values' );
function save_custom_shop_order_metabox_field_values( $post_id ){
if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
|| ! current_user_can( 'edit_shop_order', $post_id ) ) {
return;
}

if( isset( $_POST['employee_actions12'] ) ){
update_post_meta( $post_id, 'employee_actions12', wc_clean($_POST['employee_actions12']) );
}
}

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



Related Topics



Leave a reply



Submit