With the help of these 2 posts (WooCommerce editable custom checkout field and displayed in formatted address)( Add additional fields to Admin User under Customer shipping section in Woocommerce), I added the custom field in the checkout, my account, single order, admin, and email. but there are 3 more area I would like to show this custom field and I couldn't find the hook needed to show them. I searched online but with no luck at all. I would love it if someone could guide me on this.
1- under shipping address table in thank you page
2- under shipping address table in order view on my account page
3- I would like to show the field in the address on my account. but its only visible when I click edit
this is the code I used to add the extra field and its working so far
// HOUSE NUMBER EXTRA FIELD
// display shipping House Number in checkout and my account edit shipping address
add_filter( 'woocommerce_shipping_fields', 'add_shipping_house_number_field' );
function add_shipping_house_number_field( $fields ) {
$fields['shipping_house_number'] = array(
'label' => __( 'House Number', 'woocommerce' ),
//'required' => true,
'class' => array( 'form-row-first' ),
'priority' => 61,
);
return $fields;
}
// Display editable custom fields on admin single order pages (backend new order) edit pages inside edit shipping section
add_filter( 'woocommerce_admin_shipping_fields' , 'add_order_admin_edit_shipping_house_number' );
function add_order_admin_edit_shipping_house_number( $fields ) {
// Include shipping house_number as editable field
$fields['house_number'] = array( 'label' => __( 'House Number', 'woocommerce' ), 'show' => '0' );
return $fields;
}
// Load Ajax custom field data as customer billing/shipping address fields in backend new order (data will be pulled from the database)
add_filter( 'woocommerce_ajax_get_customer_details' , 'add_custom_fields_to_ajax_customer_details', 10, 3 );
function add_custom_fields_to_ajax_customer_details( $data, $customer, $user_id ) {
$data['shipping'][house_number] = $customer->get_meta('shipping_house_number');
return $data;
}
// Display reordered editable custom fields on admin single User pages
add_filter( 'woocommerce_customer_meta_fields', 'house_number_customer_meta_fields', 10, 1 );
function house_number_customer_meta_fields( $fields ) {
$fields['shipping']['fields']['shipping_house_number'] = array(
'label' => __( 'House Number', 'woocommerce' ),
);
return $fields;
}
// Adding custom placeholder to woocommerce formatted address only on Backend
add_filter( 'woocommerce_localisation_address_formats', 'admin_localisation_address_formats', 50, 1 );
function admin_localisation_address_formats( $address_formats ){
// Only in backend (Admin)
if( is_admin() || ! is_wc_endpoint_url() ) {
foreach( $address_formats as $country_code => $address_format ) {
$address_formats[$country_code] .= "\n{house_number}";
}
}
return $address_formats;
}
// Custom placeholder replacement to woocommerce formatted address
add_filter( 'woocommerce_formatted_address_replacements', 'custom_formatted_address_replacements', 10, 2 );
function custom_formatted_address_replacements( $replacements, $args ) {
$replacements['{house_number}'] = ! empty($args['house_number']) ? $args['house_number'] : '';
return $replacements;
}
// Add the shipping house number value to be displayed on email notifications under shipping address
add_filter( 'woocommerce_order_formatted_shipping_address', 'add_shipping_house_number_to_formatted_shipping_address', 100, 2 );
function add_shipping_house_number_to_formatted_shipping_address( $shipping_address, $order ) {
global $pagenow, $post_type;
// Not on admin order edit pages (as it's already displayed).
if( ! ( $pagenow === 'post.php' && $post_type === 'shop_order' && isset($_GET['action']) && $_GET['action'] === 'edit' ) ) {
// Include shipping phone on formatted shipping address
$shipping_address['house_number'] = $order->get_meta('_shipping_house_number');
}
return $shipping_address;
}
For thank you page and my account view order you can use WC woocommerce_order_get_formatted_shipping_address filter hook.
function add_woocommerce_order_get_formatted_shipping_address( $address, $empty_content, $raw_address, $order ){
$address .= '<br/> this is custom text';
return $address;
}
add_filter( 'woocommerce_order_get_formatted_shipping_address', 'add_woocommerce_order_get_formatted_shipping_address', 10, 4 );
Thank you Page
My Account View Order.
My Account edit address
You need to override. woocommerce\templates\myaccount\my-address.php
to your theme or plugin.
I am trying to remove some default columns (amount & products) from the WooCommerce admin coupon list.
For that I make use of the following code:
add_filter( 'manage_posts_columns', 'custom_post_columns', 10, 2 );
function custom_post_columns( $columns, $post_type ) {
switch ( $post_type ) {
case 'shop_coupon':
unset(
$columns['amount'],
$columns['products']
);
break;
}
return $columns;
}
But it doesn't work and I'm not getting any errors. I think the code I'm using is just not being applied correctly.
You can use the manage_edit-{post type or taxonomy}_columns filter hook
So you get:
function filter_manage_edit_shop_coupon_columns( $columns ) {
// Remove
unset( $columns['products'] );
unset( $columns['amount'] );
return $columns;
}
add_filter( 'manage_edit-shop_coupon_columns', 'filter_manage_edit_shop_coupon_columns', 10, 1 );
I am having some really hard times trying to make it work.
I've had an idea in back of my head to:
be able to assign single user to a coupon code through an extra field in general coupon tab, which is listing unassigned users to coupon codes
I dont want to use third party extensions, custom-fields, etc. I was hoping I'll be able to do it through meta data but I failed. Not sure how to get it done properly.
add two extra columns on orders page and display both coupon code and user assigned to it.
After some time reading docs and xDebugging in phpstorm I have also failed to get it done.
function order_sellers_and_coupons_columns_values($column)
{
global $post, $the_order;
if ($column == 'order_coupon_code') {
$coupons = $the_order->get_used_coupons(); // not sure how to get coupon object by the coupon code
echo (count($coupons)) ? $coupons[0] : '';
}
}
// even though I see the order objects have an items and coupon lines property,
// which is an object, i can't get access to it
$the_order->items["coupon_lines"]
I am not asking for ready-to-go solution, but to show me the way how to get it done.
Thanks in advance for any kind of help.
In WooCommerce admin single coupon pages, we add an extra field for the seller (dealer):
// Add a custom field to Admin coupon settings pages
add_action( 'woocommerce_coupon_options', 'add_coupon_text_field', 10 );
function add_coupon_text_field() {
woocommerce_wp_text_input( array(
'id' => 'seller_id',
'label' => __( 'Assing a seller (dealer)', 'woocommerce' ),
'placeholder' => '',
'description' => __( 'Assign a seller / dealer to a coupon', 'woocommerce' ),
'desc_tip' => true,
) );
}
// Save the custom field value from Admin coupon settings pages
add_action( 'woocommerce_coupon_options_save', 'save_coupon_text_field', 10, 2 );
function save_coupon_text_field( $post_id, $coupon ) {
if( isset( $_POST['seller_id'] ) ) {
$coupon->update_meta_data( 'seller_id', sanitize_text_field( $_POST['seller_id'] ) );
$coupon->save();
}
}
Then using the following you will add to admin orders list the coupon code (when it's used in the order) with the seller / dealer name:
// Adding a new column to admin orders list
add_filter( 'manage_edit-shop_order_columns', 'custom_shop_order_column' );
function custom_shop_order_column($columns)
{
$reordered_columns = array();
// Inserting columns to a specific location
foreach( $columns as $key => $column){
$reordered_columns[$key] = $column;
if( $key == 'order_status' ){
// Inserting after "Status" column
$reordered_columns['coupons'] = __( 'Coupon','theme_domain');
}
}
return $reordered_columns;
}
// Adding used coupon codes
add_action( 'manage_shop_order_posts_custom_column' , 'custom_orders_list_column_content', 10, 2 );
function custom_orders_list_column_content( $column, $post_id )
{
global $the_order;
if ( $column == 'coupons' ) {
$coupons = (array) $the_order->get_used_coupons();
$dealers = [];
foreach( $coupons as $coupon_code ) {
$coupon = new WC_Coupon( $coupon_code );
$dealers[] = $coupon->get_meta('seller_id');
}
if( count($coupons) > 0 )
echo implode( ', ', $coupons );
if( count($dealers) > 0 )
echo '<br><small>(' . implode( ', ', $dealers ) . ')</small>';
}
}
All code goes in functions.php file of your active child theme (or active theme). Tested and works.
On Admin coupon single pages:
On Admin edit orders list:
I'd like to change the product meta label from "Weight" to "Square Feet" in both the back end and front end.
I've tried this and a few [hundred] variations without success:
add_filter( 'woocommerce_register_post_type_product', 'custom_product_labels' );
function custom_product_labels( $args ) {
//
// change labels in $args['labels'] array
//
$args['labels']['_weight'] = 'Square Feet';
return $args;
I've successfully edited the UNITs with this:
add_filter( 'woocommerce_product_settings', 'add_woocommerce_dimension_units' );
function add_woocommerce_dimension_units( $settings ) {
foreach ( $settings as &$setting ) {
if ( $setting['id'] == 'woocommerce_dimension_unit' ) {
$setting['options']['feet'] = __( 'ft' ); // foot
}
if ( $setting['id'] == 'woocommerce_weight_unit' ) {
$setting['options']['sq ft'] = __( 'sq ft' ); // square feet
}
}
return $settings;
}
But I still can't figure out how to hook into the measurement labels to edit them. It's important to note that I don't want to ADD a meta unit of "Square Feet" because we already have thousands of products filled in with the sq ft data in the Weight field.
My quick workaround was to find the actual code on these pages and edit them. But it's a poor solution.
woocommerce/includes/admin/meta-boxes/views/html-product-data-shipping.php
woocommerce/includes/wc-formatting-functions.php
woocommerce/includes/wc-template-functions.php
Edit: Here is a page showing the use.
https://homedesigningservice.com/product/cape-house-plan-10034-cp/
Thank you in advance for saving my melting brain. :-)
You may use this snippet
add_filter( 'gettext', 'theme_change_comment_field_names', 20, 3 );
function theme_change_comment_field_names( $translated_text, $text, $domain ) {
switch ( $translated_text ) {
case 'Weight' :
$translated_text = __( 'Square Feet', $domain );
break;
case 'weight' :
$translated_text = __( 'Square Feet', $domain );
break;
}
return $translated_text;
}
Woo has a filter for front end but no filter for backend label changing.. So use below code and it won't conflict with any other label... Lakshman's gettext will change weight anywhere in the site...
add_filter( 'woocommerce_display_product_attributes',
'prefix_change_weight_label_to_square_feet', 10, 2 );
function prefix_change_weight_label_to_square_feet( $product_attributes, $product ) {
// Change Weight to Square Feet
$product_attributes[ 'weight' ]['label'] = __('Square Feet');
return $product_attributes;
}
// edit WEIGHT label to SQUARE FEET
add_action( 'admin_footer', function(){
$currentPostType = get_post_type();
if( $currentPostType != 'product' ) return;
?>
<script>
(function($){
$(document).ready(function(){
if ( jQuery('label[for="_weight"]').length ) {
jQuery('label[for="_weight"]').text("Square Feet");
}
});
})(jQuery);
</script>
<?php
});
In the order email templates (for example email-order-items.php), WooCommerce uses the function wc_display_item_meta to display product details in the order table. The function code is present in the wc-template-functions.php file (line number 3011). I am copying the function code below for reference
function wc_display_item_meta( $item, $args = array() ) {
$strings = array();
$html = '';
$args = wp_parse_args( $args, array(
'before' => '<ul class="wc-item-meta"><li>',
'after' => '</li></ul>',
'separator' => '</li><li>',
'echo' => true,
'autop' => false,
) );
foreach ( $item->get_formatted_meta_data() as $meta_id => $meta ) {
$value = $args['autop'] ? wp_kses_post( $meta->display_value ) : wp_kses_post( make_clickable( trim( $meta->display_value ) ) );
$strings[] = '<strong class="wc-item-meta-label">' . wp_kses_post( $meta->display_key ) . ':</strong> ' . $value;
}
if ( $strings ) {
$html = $args['before'] . implode( $args['separator'], $strings ) . $args['after'];
}
$html = apply_filters( 'woocommerce_display_item_meta', $html, $item, $args );
if ( $args['echo'] ) {
echo $html; // WPCS: XSS ok.
} else {
return $html;
}
}
The problem is: it doesn't take any arguments that can help me filter out item data that I don't want to show in the order email. I don't want to change this function in the wc-template-functions.php as it's a core file. So, I want to know if there's a piece of code that I can add to functions.php that'll somehow modify this wc_display_item_meta function to filter out specific item meta.
Note: I know someone might suggest why not just remove that particular item data from the product details, but that data is essential to internal order processing. I just don't want it to show to the customers.
Update #1: What meta data I don't want to show in the order email? Below is a screenshot of an order email. I have highlighted three item data.."Qty Selector", "Qty" and "Total". I want all these three to not show in the order email.
Try the following without any guarantee (as I don't really have the real necessary keys):
add_filter( 'woocommerce_order_item_get_formatted_meta_data', 'unset_specific_order_item_meta_data', 10, 2);
function unset_specific_order_item_meta_data($formatted_meta, $item){
// Only on emails notifications
if( is_admin() || is_wc_endpoint_url() )
return $formatted_meta;
foreach( $formatted_meta as $key => $meta ){
if( in_array( $meta->key, array('Qty Selector', 'Qty', 'Total') ) )
unset($formatted_meta[$key]);
}
return $formatted_meta;
}
Code goes in function.php file of your active child theme (active theme). Tested with other meta data than yours and works. I hope it will work for you too.
Now, the hook used with this code is the right filter hook. It's located in the WC_Order_Item method get_formatted_meta_data() and allows to filter the order item meta data.
There is a bug with the accepted answer, and all of the other snippets that I've found around the internet, so I'm posting my own answer here in the hopes that stores around the world don't accidentally leak information.
The problem is that when you use the Order actions meta box to resend the email, the filter check fails because is_admin() === true.
The order actions is a meta box down the side of the Orders page:
So the first time, when the order is created, it filters the email like you want, but then if an admin resends the email to a customer then it will be broken and show all of the meta fields to the user in the resent email.
The code that fixes this scenario is this:
$is_resend = isset($_POST['wc_order_action']) ? wc_clean( wp_unslash( $_POST['wc_order_action'] ) ) === 'send_order_details' : false;
if ( !$is_resend && (is_admin() || is_wc_endpoint_url() ) ) {
return $formatted_meta;
}
So if you look at the linked snippet then you will see the meta box adds that field to the $_POST. It has to be cleaned up like that as well or it won't match.
The full example integrated into the accepted solution's answer is:
add_filter( 'woocommerce_order_item_get_formatted_meta_data', 'unset_specific_order_item_meta_data', 10, 2);
function unset_specific_order_item_meta_data($formatted_meta, $item){
// Only on emails notifications
$is_resend = isset($_POST['wc_order_action']) ? wc_clean( wp_unslash( $_POST['wc_order_action'] ) ) === 'send_order_details' : false;
if ( !$is_resend && (is_admin() || is_wc_endpoint_url() ) ) {
return $formatted_meta;
}
foreach( $formatted_meta as $key => $meta ){
if( in_array( $meta->key, array('Qty Selector', 'Qty', 'Total') ) )
unset($formatted_meta[$key]);
}
return $formatted_meta;
}
I hear, that you want to show the Order Item Meta Data in the admin backend only. That's actually a tricky one. I have played around for some hours but no solution I have found, guarentee that the Order Itema Meta Data doesn't show up in e-mails to the customer.
The thing is that there are several ways these e-mails are fired (eg. through the resend meta box (which #rtpHarry mentions) or by changing order status either at the order overview, the single order view or an automatic/programmatically order status change). That gives many cases where it's neccessary to unset the Order Item Meta Data - you need to find all cases except the admin backend.
Therefore, my suggestion is to first completely remove the Order Item Meta Data using the above mentioned woocommerce_order_item_get_formatted_meta_data filter and then add them again using an action like woocommerce_before_order_itemmeta which ONLY fires in the admin backend. Because the Order Item Meta Data is unset you cannot use the get_formatted_meta_data method to get the data. Instead you can use the function wc_get_order_item_meta.
Complete code (tested and works):
//Hide 'Qty Selector', 'Qty' and 'Total' completely
add_filter( 'woocommerce_order_item_get_formatted_meta_data', 'unset_specific_order_item_meta_data');
function unset_specific_order_item_meta_data($formatted_meta){
foreach( $formatted_meta as $key => $meta ){
if( in_array( $meta->key, array('Qty Selector', 'Qty', 'Total') ) )
unset($formatted_meta[$key]);
}
return $formatted_meta;
}
//Add 'Qty Selector', 'Qty' and 'Total' in the admin backend only
add_action('woocommerce_before_order_itemmeta', 'add_specific_order_item_meta_data_in_backend', 10, 2);
function add_specific_order_item_meta_data_in_backend( $item_id, $item ) {
//Only applies for line items
if( $item->get_type() !== 'line_item' ) return;
$qty_sel_lines = wc_get_order_item_meta($item_id, 'Qty Selector', false);
$qty_lines = wc_get_order_item_meta($item_id, 'Qty', false);
$total_lines = wc_get_order_item_meta($item_id, 'Total', false);
foreach ($qty_sel_lines as $qty_sel_line){
echo $qty_sel_line . '<br>';
}
foreach ($qty_lines as $qty_line){
echo $qty_line . '<br>';
}
foreach ($total_lines as $total_line){
echo $total_line. '<br>';
}
}
Note:
If you need to add the Order Item Meta Data to the admin e-mails, you need to do that seperately. I have not examined the options on that.
I kind of agreed with #pstidsen argument. So I was thinking about how to solve this without to re-add all the metadata, since it kind of disturbed me not to handle it in the same way as it was added before. I have additional filters to add css classes and so on to the metadata. So there would've been a need to take care of.
So here is my approach which gives you the opportunity to use it for emails, custom emails, pdf invoices or similar scenarios. I also uses a fallback to filter for our frontend or any situation we didn't consider.
Please keep the order of if else in mind. I checked the admin filter the last, to make sure any other filter gets fired before. The situation for an email at example is: It's sent from the admin interface, so the admin filter is true but also is the email filter.
Functionality for a different filter for admin emails is given as well.
/**
* This function filters all unwanted item metadata, if the specific filter are hooked in
* we also use a fallback filter, if none of the hooks are fired
*
* #params array() $metadata
*
*/
add_filter( 'woocommerce_order_item_get_formatted_meta_data', 'custom_filter_item_meta_data', 50, 1);
function custom_filter_item_meta_data( $metadata ){
if ( empty( $metadata ) ) return $metadata;
$filter_array = array();
if ( apply_filters( 'custom_filter_item_meta_email', false ) ){
// email filter goes here
$filter_array = array( 'whatever','you', 'wanna', 'filter, 'for', 'email' );
}elseif ( apply_filters( 'custom_filter_item_meta_admin_email', false ) ){
// admin email filter goes here
// pass
elseif ( apply_filters( 'custom_filter_item_meta_invoice', false ) ){
// invoice filter goes here
$filter_array = array( 'whatever','you', 'wanna', 'filter, 'for', 'invoices' );
}elseif ( apply_filters( 'custom_filter_item_meta_admin', false ) ){
// general admin filter goes here
$filter_array = array( 'whatever','you', 'wanna', 'filter, 'for', 'admin_backend' );
}else{
// fallback filter
$filter_array = array( 'whatever','you', 'wanna', 'filter, 'for', 'fallback' );
}
foreach ( $metadata as $key => $meta ){
if ( in_array( $meta->key, $filter_array ) ){
unset ( $metadata[ $key ] );
}
}
return $metadata;
}
/**
* Is used to enable our item meta filter for our admin backend
* Hooked:
* #admin_init
*/
add_action( 'admin_init', 'custom_init_item_meta_filter_admin', 50, 1 );
function custom_init_item_meta_filter_admin(){
add_filter( 'custom_filter_item_meta_admin', function(){ return true; });
}
/**
* Is used to enable our item meta filter for emails
* Hooked:
* #woocommerce_email_order_details
*/
add_action( 'woocommerce_email_order_details', 'custom_init_item_meta_filter_email' ), 10, 2);
function custom_init_item_meta_filter_email( $order, $sent_to_admin ){
if ( $sent_to_admin ){
add_filter('custom_filter_item_meta_admin_email', function(){ return true; } );
}else{
add_filter('custom_filter_item_meta_email', function(){ return true; } );
}
}
/**
* Is used to enable our item meta filter for invoices
* Hooked:
* #wpo_wcpdf_before_order_details
*/
add_filter( 'wpo_wcpdf_before_order_details', 'custom_init_item_meta_filter_invoice', 10, 1);
function custom_init_item_meta_filter_invoice(){
add_filter( 'custom_filter_item_meta_invoice', function(){ return true; });
}
I didn't test it in that "flattened" format. I used it within different classes of my oop coded plugin and edited it to post it here.
Structure of the html code of the metadata tags
If I want to delete meta_data 2 and meta_data 3:
add_filter( 'woocommerce_display_item_meta', 'filter_woocommerce_display_item_meta', 10, 3 );
function filter_woocommerce_display_item_meta( $html, $item, $args ) {
$arrayPortionsTags = explode("<li", $html);
unset($arrayPortionsTags[2],$arrayPortionsTags[3]);
$firstLi = array( '<li' );
$lastUl = array( '</ul>' );
array_splice( $arrayPortionsTags, 1, 0, $firstLi );
array_splice( $arrayPortionsTags, 3, 0, $lastUl );
$html= implode('',$arrayPortionsTags);
return $html;
};