In woocommerce, I have successfully managed to add a custom field to a Woocommerce simple product and display that value on the additional info tab on a product page.
Now I am totally lost with the final piece of my jigsaw trying to insert a total row in cart and checkout pages with for the total calculated volume.
The custom field is to store the volume in m3 of a furniture item. When a client adds an item to the basket I would like to calculate the total m3 of the shipment by adding the values of all the custom fields together and displaying the total to the customer on the page. Can anyone point me in the right direction of how to add these custom fields and display them please.
So far my code looks like this
// Display Fields using WooCommerce Action Hook
add_action( 'woocommerce_product_options_general_product_data', 'woocom_general_product_data_custom_field' );
function woocom_general_product_data_custom_field() {
// Create a custom text field
// Number Field
woocommerce_wp_text_input(
array(
'id' => '_item_volume',
'label' => __( 'Item Shipping Volume', 'woocommerce' ),
'placeholder' => '',
'description' => __( 'Enter the volume her in m3.', 'woocommerce' ),
'type' => 'number',
'custom_attributes' => array(
'step' => 'any',
'min' => '0.001'
)
)
);
}
// Hook to save the data value from the custom fields
add_action( 'woocommerce_process_product_meta', 'woocom_save_general_proddata_custom_field' );
/** Hook callback function to save custom fields information */
function woocom_save_general_proddata_custom_field( $post_id ) {
// Save Number Field
$number_field = $_POST['_item_volume'];
if( ! empty( $number_field ) ) {
update_post_meta( $post_id, '_item_volume', esc_attr( $number_field ) );
}
}
add_action( 'woocommerce_product_additional_information', 'custom_data_in_product_add_info_tab', 20, 1 );
function custom_data_in_product_add_info_tab( $product ) {
//Product ID - WooCommerce compatibility
$product_id = method_exists( $product, 'get_id' ) ? $product->get_id() : $product->id;
// Get your custom fields data
$custom_field1 = get_post_meta( $product_id, '_item_volume', true );
// Set your custom fields labels (or names)
$label1 = __( 'Shipping Volume m3', 'woocommerce');
// The Output
echo '<h3>'. __('Item Shipping Volume', 'woocommerce') .'</h3>
<table class="custom-fields-data">
<tbody>
<tr class="custom-field1">
<th>'. $label1 .'</th>
<td>'. $custom_field1 .'</td>
</tr>
</tbody>
</table>';
}
The following will display the total cart volume in cart and checkout page:
add_action( 'woocommerce_cart_totals_before_shipping', 'display_cart_volume_total', 20 );
add_action( 'woocommerce_review_order_before_shipping', 'display_cart_volume_total', 20 );
function display_cart_volume_total() {
$total_volume = 0;
// Loop through cart items and calculate total volume
foreach( WC()->cart->get_cart() as $cart_item ){
$product_volume = (float) get_post_meta( $cart_item['product_id'], '_item_volume', true );
$total_volume += $product_volume * $cart_item['quantity'];
}
if( $total_volume > 0 ){
// The Output
echo ' <tr class="cart-total-volume">
<th>' . __( "Total Shipping Volume", "woocommerce" ) . '</th>
<td data-title="total-volume">' . number_format($total_volume, 2) . ' m3</td>
</tr>';
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
Related
please I have this code which I used to add max price to my woocommerce store.
as show here
But I'm seeking for a way to make the max-price appear on the cart, checkout, and order notice pages together with the regular price.
Here is the code I used.
// Add a custom field for price range to product in backend
add_action( 'woocommerce_product_options_pricing', 'add_field_product_options_pricing' );
function add_field_product_options_pricing() {
global $post;
echo '<div class="options_group show_if_simple">';
woocommerce_wp_text_input( array(
'id' => '_max_price_for_range',
'label' => __('Max price for range', 'woocommerce').' ('.get_woocommerce_currency_symbol().')',
'placeholder' => __('Set the max price for range', 'woocommerce'),
'description' => __('Set the max price for range, to activate it…', 'woocommerce'),
'desc_tip' => 'true',
));
echo '</div>';
}
// Save product custom field to database when submitted in Backend
add_action( 'woocommerce_process_product_meta', 'save_product_options_custom_fields', 30, 1 );
function save_product_options_custom_fields( $post_id ){
// Saving custom field value
if( isset( $_POST['_max_price_for_range'] ) ){
update_post_meta( $post_id, '_max_price_for_range', sanitize_text_field( $_POST['_max_price_for_range'] ) );
}
}
// Frontend: display a price range when the max price is set for the product
add_filter( 'woocommerce_get_price_html', 'custom_range_price_format', 10, 2 );
function custom_range_price_format( $price, $product ) {
// Only for simple product type
if( $product->is_type('simple') ){
// Get the max price for range
$max_price = get_post_meta( $product->get_id(), '_max_price_for_range', true );
if( empty($max_price) )
return $price; // exit
$active_price = wc_get_price_to_display( $product, array( 'price' => $product->get_price() ) );
$price = sprintf( '%s – %s', wc_price($active_price), wc_price($max_price) );
}
return $price;
}
I'm really new to PHP coding so I wasn't able to pick the right hooks to add the max price to the pages.
Thanks in anticipation of your help.
In Woocommerce I would like to show a minimum price for a simple product and variable products in front of the catalog.
Based on "Set a min unit displayed price for variable products in Woocommerce" answer code, where I have made light changes to the last function as follow:
// Frontend: Display the min price with "From" prefix label for variable products
add_filter( 'woocommerce_variable_price_html', 'custom_min_unit_variable_price_html', 30, 2 );
function custom_min_unit_variable_price_html( $price, $product ) {
$min_unit_price = get_post_meta( $product->get_id(), '_min_unit_price', true );
if( $min_unit_price > 0 ){
$min_price_html = wc_price( wc_get_price_to_display( $product, array( 'price' => $min_unit_price ) ) );
$price = sprintf( __( '%1$s <span class="amount"><div style="font-size:11px;color: #666;text-align: center;">Bulk purchasing</div></span>', 'woocommerce' ), $min_price_html );
}
return $price;
}
I would like to adapt this solution to simple products as well. Any help is appreciated.
From product additional price setting field (see the screenshots below), a minimal unit price replace the displayed product price in Woocommerce shop, archives and single product pages.
Based on "Set a min unit displayed price for variable products in Woocommerce" answer code, the following will work for simple and variable products to be used with Woocommerce 3 and above.
The code (completely revisited and enhanced):
// Backend: Add and display a custom field for simple and variable products
add_action( 'woocommerce_product_options_general_product_data', 'add_custom_price_field_to_general_product_data' );
function add_custom_price_field_to_general_product_data() {
global $product_object;
echo '<div class="options_group hide_if_external">';
woocommerce_wp_text_input(array(
'id' => '_min_unit_price',
'label' => __('Min Unit price', 'woocommerce') . ' (' . get_woocommerce_currency_symbol() . ')',
'description' => __('Enter the minimum unit price here.', 'woocommerce'),
'desc_tip' => 'true',
'value' => str_replace('.', ',', $product_object->get_meta('_min_unit_price') ),
'data_type' => 'price'
));
echo '</div>';
}
// Backend: Save the custom field value for simple and variable products
add_action( 'woocommerce_admin_process_product_object', 'save_product_custom_price_field', 10, 1 );
function save_product_custom_price_field( $product ) {
if ( isset($_POST['_min_unit_price']) ) {
$product->update_meta_data( '_min_unit_price', wc_clean( wp_unslash( str_replace( ',', '.', $_POST['_min_unit_price'] ) ) ) );
}
}
// Frontend variable products: Display the min price with "From" prefix label
add_filter( 'woocommerce_variable_price_html', 'custom_min_unit_variable_price_html', 10, 2 );
function custom_min_unit_variable_price_html( $price, $product ) {
if( $min_unit_price = $product->get_meta('_min_unit_price') ){
$price = wc_price( wc_get_price_to_display( $product, array( 'price' => $min_unit_price ) ) );
$price .= ' <span class="prefix" style="font-size:11px;color: #666;text-align: center;">'.__('Bulk purchasing').'</span>';
}
return $price;
}
// Frontend simple products: Display the min price with "From" prefix label
add_filter( 'woocommerce_get_price_html', 'custom_min_unit_product_price_html', 10, 2 );
function custom_min_unit_product_price_html( $price, $product ) {
if( $product->is_type('simple') && $min_unit_price = $product->get_meta('_min_unit_price') ){
$price = wc_price( wc_get_price_to_display( $product, array( 'price' => $min_unit_price ) ) );
$price .= ' <span class="prefix" style="font-size:11px;color: #666;text-align: center;">'.__('Bulk purchasing').'</span>';
}
return $price;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
In backend, for simple and variables products:
In frontend, for simple and variables products (in shop, archives and single product pages)
In Woocommerce, I'm attempting to add a piece of custom meta to my products and I would like to pass it through to orders.
We have a substantial amount of products and they are accountable to different cost centers so I need a select box inside the product admin that we can choice the cost centers that passes a value into an order this does not need to be viewed by the customer but needs to be viewable by admin in the orders and also in the order exports each month for accounting.
This is what I have so far, this will display the select box in product edit pages (admin):
// Display Fields
add_action( 'woocommerce_product_options_general_product_data', 'woo_add_custom_general_fields' );
function woo_add_custom_general_fields() {
global $woocommerce, $post;
echo '<div class="options_group">';
woocommerce_wp_select(
array(
'id' => '_select',
'label' => __( 'Cost Centre', 'woocommerce' ),
'options' => array(
'one' => __( 'MFEG', 'woocommerce' ),
'two' => __( 'YDIT', 'woocommerce' ),
)
)
);
echo '</div>';
}
// Save Fields
add_action( 'woocommerce_process_product_meta', 'woo_add_custom_general_fields_save' );
function woo_add_custom_general_fields_save( $post_id ){
// Select
$woocommerce_select = $_POST['_select'];
if( !empty( $woocommerce_select ) )
update_post_meta( $post_id, '_select', esc_attr( $woocommerce_select ) );
}
But it is not passing the value in to an order.
How can I pass this custom field value to the order?
I have revisited a bit your code. The following will save your product custom field "Cost centre" as hidden order item meta data, visible only in Admin Order edit pages on each item:
// Admin products: Display custom Field
add_action( 'woocommerce_product_options_general_product_data', 'product_options_general_product_data_add_field' );
function product_options_general_product_data_add_field() {
global $post;
echo '<div class="options_group">';
woocommerce_wp_select( array(
'id' => '_cost_centre',
'label' => __( 'Cost Centre', 'woocommerce' ),
'options' => array(
'MFEG' => __( 'MFEG', 'woocommerce' ), // Default displayed option value
'YDIT' => __( 'YDIT', 'woocommerce' ),
)
) );
echo '</div>';
}
// Admin products: Save custom Field
add_action( 'woocommerce_process_product_meta', 'product_options_general_product_data_save_field' );
function product_options_general_product_data_save_field( $post_id ){
if( isset( $_POST['_cost_centre'] ) )
update_post_meta( $post_id, '_cost_centre', esc_attr( $_POST['_cost_centre'] ) );
}
// Order items: Save product "Cost centre" as hidden order item meta data
add_action('woocommerce_checkout_create_order_line_item', 'save_file_type_as_order_item_meta', 20, 4);
function save_file_type_as_order_item_meta($item, $cart_item_key, $values, $order) {
if ( $cost_centre = $values['data']->get_meta('_cost_centre') ) {
$item->update_meta_data( '_cost_centre', $cost_centre ); // Save as order item (visble on admin only)
}
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
Export: (In StackOverFlow, the rule is one question for one answer).
The WordPress/Woocommerce basic order export don't allow to export order items
You will need to use a third party plugin and depending on the chosen plugin, you will have to add your order item custom field _cost_centre for your export based on the plugin possibilities.
In woocommerce I am using Advanced Custom Fields and trying to get a custom field value in each product as price instead of the default product price. this custom field is called 'custom_price'.
How can I change this hard coded value to use that instead?
Here is my code:
add_action( 'woocommerce_before_calculate_totals', 'add_custom_price'
);
function add_custom_price( $cart_object ) {
$custom_price = 10;
foreach ( $cart_object->cart_contents as $key => $value ) {
$value['data']->set_price($custom_price);
}
}
Update 3: Here is the complete solution with all custom fields and the cart item price change.
You will need to add some jQuery code to make your product price calculation, display calculated price on product page and set this calculated price on a hidden field.
Once product will be added to cart, the code will catch the calculated price and will set it in the corresponding cart item…
The code:
// The product custom field - Frontend
add_action( 'woocommerce_before_add_to_cart_button', 'custom_discount_price_product_field' );
function custom_discount_price_product_field() {
global $product;
$curs = get_woocommerce_currency_symbol(); // Currency symbol
// Get the discounted value (from product backend)
$discount = (float) get_post_meta( $product->get_id(), '_price_discount', true );
// jQuery will get the discount here for calculations
echo '<input type="hidden" name="price_discount" value="'.$discount.'">';
echo '<div>';
woocommerce_form_field( 'select_price', array(
'type' => 'select',
'class' => array('my-field-class form-row-wide'),
'label' => __('Discount'),
'options' => array(
'' => __( 'Select your discount', 'woocommerce' ),
'5' => $curs . '5',
'10' => $curs . '10',
'15' => $curs . '15',
'20' => $curs . '20',
),
), '' );
// This field will be used to send the calculated price
// jQuery will set the calculated price on this field
echo '<input type="hidden" name="custom_price" value="52">'; // 52 is a fake value for testing purpose
echo '</div><br>';
// BELOW your jquery code to calculate price and update "custom_price" hidden field
?>
<script type="text/javascript">
jQuery( function($){
// Here
});
</script>
<?php
}
// Add a custom field to product in backend
add_action( 'woocommerce_product_options_pricing', 'add_field_product_options_pricing' );
function add_field_product_options_pricing() {
global $post;
echo '<div class="options_group">';
woocommerce_wp_text_input( array(
'id' => '_price_discount',
'label' => __('Discount price', 'woocommerce') . ' (%)',
'placeholder' => __('Set the Discount price…', 'woocommerce'),
'description' => __('Enter the custom value here.', 'woocommerce'),
'desc_tip' => 'true',
));
echo '</div>';
}
// Save product custom field to database when submitted in Backend
add_action( 'woocommerce_process_product_meta', 'save_product_options_custom_fields', 30, 1 );
function save_product_options_custom_fields( $post_id ){
// Saving custom field value
if( isset( $_POST['_price_discount'] ) ){
update_post_meta( $post_id, '_price_discount', sanitize_text_field( $_POST['_price_discount'] ) );
}
}
// Add custom calculated price conditionally as custom data to cart items
add_filter( 'woocommerce_add_cart_item_data', 'add_custom_price_to_cart_item_data', 20, 2 );
function add_custom_price_to_cart_item_data( $cart_item_data, $product_id ){
if( ! isset($_POST['custom_price']) )
return $cart_item_data;
$cart_item_data['custom_price'] = (float) sanitize_text_field( $_POST['custom_price'] );
$cart_item_data['unique_key'] = md5( microtime() . rand() ); // Make each item unique
return $cart_item_data;
}
// Set conditionally a custom item price
add_action('woocommerce_before_calculate_totals', 'set_cutom_cart_item_price', 20, 1);
function set_cutom_cart_item_price( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
foreach ( $cart->get_cart() as $cart_item ) {
if ( isset( $cart_item['custom_price'] ) )
$cart_item['data']->set_price( $cart_item['custom_price'] );
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works (but you will need to make your own calculations with jquery)
I've created a custom field for warranty in my products pages, via function.php.
add_action( 'woocommerce_product_options_general_product_data', 'test_custom_fields' );
function test_custom_fields() {
// Print a custom text field
woocommerce_wp_text_input( array(
'id' => '_warranty',
'label' => 'i.e. 15 years',
'description' => '',
'desc_tip' => 'true',
'placeholder' => 'i.e. 15 years'
) );
}
add_action( 'woocommerce_process_product_meta', 'test_save_custom_fields' );
function test_save_custom_fields( $post_id ) {
if ( ! empty( $_POST['_warranty'] ) ) {
update_post_meta( $post_id, '_warranty', esc_attr( $_POST['_warranty'] ) );
}
}
I would like to "duplicate" this custom field with key and value, in an self-generated custom field on the admin order page depending on products in cart/order (without plugin).
So, with this custom field on order page, I will finally be able to display "warranty" in my pdf invoice with WooCommerce PDF Invoice plugin.
Another explanation :
As admin, I fill _warranty value in "product1" page
When "product1" is in an order, on the admin order view, I would like to see a custom field containing "_warranty + value" from product1 page.
So, as admin, I could set {{_warranty}} in WooCommerce PDF Invoice plugin to display "Warranty : 15 years"
Many thanks for your help.
I have just tested the following case: show product meta in order items table in Order Details
But this does not give me a custom field, so I couldn't get my {{_warranty}} value width it.
What I am doing wrong?
How can I achieve this?
Thanks.
First: "Duplicating this custom field with key and value, in an self-generated custom field on the admin order page" is not the good approach.
To achieve what you are expecting, you have missed just some little things. You need to:
Store custom field in Cart (when product is added to cart)
Render this on cart and checkout pages
Add the information in the order as meta data (to make it part of the order)
With point 3 you will be able to get this on WooCommerce PDF Invoice plugin to display "Warranty : 15 years".
So the code you need is:
// create the custom field on product admin tab
add_action( 'woocommerce_product_options_general_product_data', 'create_warranty_custom_field' );
function create_warranty_custom_field() {
// Create a custom text field
woocommerce_wp_text_input( array(
'id' => '_warranty',
'type' => 'text',
'label' => __('Warranty', 'woocommerce' ),
'description' => '',
'desc_tip' => 'true',
'placeholder' => __('i.e. 15 years', 'woocommerce' ),
) );
}
// save the data value from this custom field on product admin tab
add_action( 'woocommerce_process_product_meta', 'save_warranty_custom_field' );
function save_warranty_custom_field( $post_id ) {
$wc_text_field = $_POST['_warranty'];
if ( !empty($wc_text_field) ) {
update_post_meta( $post_id, '_warranty', esc_attr( $wc_text_field ) );
}
}
// Store custom field in Cart
add_filter( 'woocommerce_add_cart_item_data', 'store_warranty_custom_field', 10, 2 );
function store_warranty_custom_field( $cart_item_data, $product_id ) {
$warranty_item = get_post_meta( $product_id , '_warranty', true );
if( !empty($warranty_item) ) {
$cart_item_data[ '_warranty' ] = $warranty_item;
// below statement make sure every add to cart action as unique line item
$cart_item_data['unique_key'] = md5( microtime().rand() );
WC()->session->set( 'days_manufacture', $warranty_item );
}
return $cart_item_data;
}
// Render meta on cart and checkout
add_filter( 'woocommerce_get_item_data', 'rendering_meta_field_on_cart_and_checkout', 10, 2 );
function rendering_meta_field_on_cart_and_checkout( $cart_data, $cart_item ) {
$custom_items = array();
// Woo 2.4.2 updates
if( !empty( $cart_data ) ) {
$custom_items = $cart_data;
}
if( isset( $cart_item['_warranty'] ) ) {
$custom_items[] = array( "name" => __( "Warranty", "woocommerce" ), "value" => $cart_item['_warranty'] );
}
return $custom_items;
}
// Add the information in the order as meta data
add_action('woocommerce_add_order_item_meta','add_waranty_to_order_item_meta', 1, 3 );
function add_waranty_to_order_item_meta( $item_id, $values, $cart_item_key ) {
// Retrieving the product id for the order $item_id
$product_id = wc_get_order_item_meta( $item_id, '_product_id', true );
// Getting the warranty value for this product Id
$warranty = get_post_meta( $product_id, '_warranty', true );
// Add the meta data to the order
wc_add_order_item_meta($item_id, 'Warranty', $warranty, true);
}
Naturally, this goes in function.php file of your active child theme (or theme) or also in any plugin file.
This code is tested and works.
References:
WooCommerce : Add custom Metabox to admin order page
Admin product pages custom field displayed in Cart and checkout
There is an error at the end of the code, you call a variable $prod_id at first, and then $product_id. Correct and working code is:
// Add the information in the order as meta data
add_action('woocommerce_add_order_item_meta','add_waranty_to_order_item_meta', 1, 3 );
function add_waranty_to_order_item_meta( $item_id, $values, $cart_item_key ) {
// Retrieving the product id for the order $item_id
$prod_id = wc_get_order_item_meta( $item_id, '_product_id', true );
// Getting the warranty value for this product Id
$warranty = get_post_meta( $prod_id, '_warranty', true );
// Add the meta data to the order
wc_add_order_item_meta($item_id, 'Warranty', $warranty, true);
}