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:
My code is divided into 2 parts.
- 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' ] );
}
}
- 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>';
}
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:
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
How to Read Gzip-Ed Response from Stackoverflow API in PHP
Concatenation with Addition in It Doesn't Work as Expected
Loop Code for Each File in a Directory
PHP Include Best Practices Question
Request Headers Bag Is Missing Authorization Header in Symfony 2
Turn Off Display Errors Using File "Php.Ini"
Parsing JSON Array with PHP Foreach
Comment Associative Array in PHP Documentor
Which Is the Best Way to Generate Excel Output in PHP
Parse JSON String Contents into PHP Array
Decrementing Alphabetical Values
Mysqli_Query() Expects at Least 2 Parameters, 1 Given In
Why Do I Have to Run "Composer Dump-Autoload" Command to Make Migrations Work in Laravel
Prevent Direct Url Access to PHP File
Passing PHP Objects to JavaScript
Structure of a Serialized PHP String