I am using Update fee dynamically based on radio buttons in Woocommerce checkout answer code solution that worked very well for me to add checkbox fields with a different price for each one, and the price changes are reflected in the checkout.
But I need some help: When I select a type of packaging with additional tax, it appears in the backend in the order area, but only shows the price, and I would like to show the title as well.
The checkbox options have:
'options' => array (
'bag' => __ ('In a bag' .wc_price (3.00), $ domain),
'box' => __ ('In a gift box' .wc_price (9.00), $ domain),
),
How to make it show the name on the order?
Also if it's possible to change the checkboxes to select field instead?
I have made some changes to the original code that will:
Display a custom select field (instead of radio buttons input fields)
Display a custom error notice if customer has not selected a packing option
Display the selected packing type everywhere (on orders and email notifications)
The code:
// Add a custom select fields for packing option fee
add_action( 'woocommerce_review_order_after_shipping', 'checkout_shipping_form_packing_addition', 20 );
function checkout_shipping_form_packing_addition( ) {
$domain = 'woocommerce';
echo '<tr class="packing-select"><th>' . __('Packing options', $domain) . '</th><td>';
$chosen = WC()->session->get('chosen_packing');
// Add a custom checkbox field
woocommerce_form_field( 'chosen_packing', array(
'type' => 'select',
'class' => array( 'form-row-wide packing' ),
'options' => array(
'' => __("Choose a packing option ...", $domain),
'bag' => sprintf( __("In a bag (%s)", $domain), strip_tags( wc_price(3.00) ) ),
'box' => sprintf( __("In a gift box (%s)", $domain), strip_tags( wc_price(9.00) ) ),
),
'required' => true,
), $chosen );
echo '</td></tr>';
}
// jQuery - Ajax script
add_action( 'wp_footer', 'checkout_shipping_packing_script' );
function checkout_shipping_packing_script() {
// Only checkout page
if ( is_checkout() && ! is_wc_endpoint_url() ) :
WC()->session->__unset('chosen_packing');
?>
<script type="text/javascript">
jQuery( function($){
$('form.checkout').on('change', 'select#chosen_packing', function(){
var p = $(this).val();
console.log(p);
$.ajax({
type: 'POST',
url: wc_checkout_params.ajax_url,
data: {
'action': 'woo_get_ajax_data',
'packing': p,
},
success: function (result) {
$('body').trigger('update_checkout');
console.log('response: '+result); // just for testing | TO BE REMOVED
},
error: function(error){
console.log(error); // just for testing | TO BE REMOVED
}
});
});
});
</script>
<?php
endif;
}
// Php Ajax (Receiving request and saving to WC session)
add_action( 'wp_ajax_woo_get_ajax_data', 'woo_get_ajax_data' );
add_action( 'wp_ajax_nopriv_woo_get_ajax_data', 'woo_get_ajax_data' );
function woo_get_ajax_data() {
if ( isset($_POST['packing']) ){
$packing = sanitize_key( $_POST['packing'] );
WC()->session->set('chosen_packing', $packing );
echo json_encode( $packing );
}
die(); // Alway at the end (to avoid server error 500)
}
// Add a custom dynamic packaging fee
add_action( 'woocommerce_cart_calculate_fees', 'add_packaging_fee', 20, 1 );
function add_packaging_fee( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
$domain = "woocommerce";
$packing_fee = WC()->session->get( 'chosen_packing' ); // Dynamic packing fee
if ( $packing_fee === 'bag' ) {
$label = __("Bag packing fee", $domain);
$cost = 3.00;
} elseif ( $packing_fee === 'box' ) {
$label = __("Gift box packing fee", $domain);
$cost = 9.00;
}
if ( isset($cost) )
$cart->add_fee( $label, $cost );
}
// Field validation, as this packing field is required
add_action('woocommerce_checkout_process', 'packing_field_checkout_process');
function packing_field_checkout_process() {
// Check if set, if its not set add an error.
if ( isset($_POST['chosen_packing']) && empty($_POST['chosen_packing']) )
wc_add_notice( __( "Please choose a packing option...", "woocommerce" ), 'error' );
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
The error message when customer hasn't chosen a packing option:
Related
Inspired from Shipping carrier custom fields validation in Woocommerce checkout page answer code, I use following code which displays a select field with shipping companies (this field is displayed only when I choose a specific shipping method):
add_action( 'woocommerce_after_shipping_rate', 'carrier_custom_fields', 20, 2 );
function carrier_custom_fields( $method, $index ) {
if( ! is_checkout()) return; // Only on the checkout page
$customer_carrier_method = 'flat_rate:14';
if( $method->id != $customer_carrier_method ) return; // Mostrar solo para "flat_rate:14"
$chosen_method_id = WC()->session->chosen_shipping_methods[ $index ];
// If the chosen shipping method is 'flat_rate: 14', we will show
if($chosen_method_id == $customer_carrier_method ):
echo '<div class="custom-carrier2">';
woocommerce_form_field( 'carrier_name1', array(
'type' => 'select',
'class' => array('carrier_name2-class form-row-wide'),
'label' => __('<strong>Shipping Company</strong>'),
'required' => 'true',
'options' => array(
'1' => '', // no data means that the field is not selected
'Shipping Company 1' => 'Shipping Company 1',
'Shipping Company 2' => 'Shipping Company 2',
'Shipping Company 3' => 'Shipping Company 3',
'Shipping Company 4' => 'Shipping Company 4'
)
), WC()->checkout->get_value( 'carrier_name1' ) );
echo '</div>';
endif;
}
// Validate the custom selection field
add_action('woocommerce_checkout_process', 'carrier_checkout_process');
function carrier_checkout_process() {
if( isset( $_POST['carrier_name1'] ) && empty( $_POST['carrier_name1'] ) )
wc_add_notice( ( "<strong>Shipping Company</strong> it is a required field." ), "error" );
}
// Save custom fields to sort metadata
add_action( 'woocommerce_checkout_update_order_meta', 'carrier_update_order_meta', 30, 1 );
function carrier_update_order_meta( $order_id ) {
if( isset( $_POST['carrier_name1'] ))
update_post_meta( $order_id, 'carrier_name1', sanitize_text_field( $_POST['carrier_name1'] ) );
}
The problem is that it's only displayed on the checkout page and I would like it to show it in cart page, keeping the selected vale on cart page to checkout page.
I think I have found something that says that this transfer of the selected data between the cart and the payment page is done through Ajax, but I am not skilled with Ajax and I don't know how to make that work.
To make that work on cart and checkout pages, you will need some additional code using jQuery, Ajax and WC Session variable:
Final update - To make the code more dynamic, we start with a custom function that will handle all required settings:
// Custom function that handle your settings
function carrier_settings(){
return array(
'targeted_methods' => array('flat_rate:14'), // Your targeted shipping method(s) in this array
'field_id' => 'carrier_name', // Field Id
'field_type' => 'select', // Field type
'field_label' => '', // Leave empty value if the first option has a text (see below).
'label_name' => __("Carrier company","woocommerce"), // for validation and as meta key for orders
'field_options' => array(
// The option displayed at first ( or keep an empty value '',)
__("Choose a carrier company", "woocommerce"),
// The carrier companies below (one by line)
'Company name 1',
'Company name 2',
'Company name 3',
'Company name 4',
),
);
}
Then we can load that settings on any function where it's needed.
Now the Select field with carrier companies displayed for a specific shipping method on cart and checkout pages:
// Display the custom checkout field
add_action( 'woocommerce_after_shipping_rate', 'carrier_company_custom_select_field', 20, 2 );
function carrier_company_custom_select_field( $method, $index ) {
extract( carrier_settings() ); // Load settings and convert them in variables
$chosen = WC()->session->get('chosen_shipping_methods'); // The chosen methods
$value = WC()->session->get($field_id);
$value = WC()->session->__isset($field_id) ? $value : WC()->checkout->get_value('_'.$field_id);
$options = array(); // Initializing
if( ! empty($chosen) && $method->id === $chosen[$index] && in_array($method->id, $targeted_methods) ) {
echo '<div class="custom-carrier">';
// Loop through field otions to add the correct keys
foreach( $field_options as $key => $option_value ) {
$option_key = $key == 0 ? '' : $key;
$options[$option_key] = $option_value;
}
woocommerce_form_field( $field_id, array(
'type' => $field_type,
'label' => '', // Not required if the first option has a text.
'class' => array('form-row-wide ' . $field_id . '-' . $field_type ),
'required' => true,
'options' => $options,
), $value );
echo '</div>';
}
}
The Ajax part: The jQuery sender + PHP WordPress admin Ajax receiver code for the selected carrier company:
// jQuery code (client side) - Ajax sender
add_action( 'wp_footer', 'carrier_company_script_js' );
function carrier_company_script_js() {
// Only cart & checkout pages
if( is_cart() || ( is_checkout() && ! is_wc_endpoint_url() ) ):
// Load settings and convert them in variables
extract( carrier_settings() );
$js_variable = is_cart() ? 'wc_cart_params' : 'wc_checkout_params';
// jQuery Ajax code
?>
<script type="text/javascript">
jQuery( function($){
if (typeof <?php echo $js_variable; ?> === 'undefined')
return false;
$(document.body).on( 'change', 'select#<?php echo $field_id; ?>', function(){
var value = $(this).val();
$.ajax({
type: 'POST',
url: <?php echo $js_variable; ?>.ajax_url,
data: {
'action': 'carrier_name',
'value': value
},
success: function (result) {
console.log(result); // Only for testing (to be removed)
}
});
});
});
</script>
<?php
endif;
}
// The Wordpress Ajax PHP receiver
add_action( 'wp_ajax_carrier_name', 'set_carrier_company_name' );
add_action( 'wp_ajax_nopriv_carrier_name', 'set_carrier_company_name' );
function set_carrier_company_name() {
if ( isset($_POST['value']) ){
// Load settings and convert them in variables
extract( carrier_settings() );
if( empty($_POST['value']) ) {
$value = 0;
$label = 'Empty';
} else {
$value = $label = esc_attr( $_POST['value'] );
}
// Update session variable
WC()->session->set( $field_id, $value );
// Send back the data to javascript (json encoded)
echo $label . ' | ' . $field_options[$value];
die();
}
}
Then on checkout page, the field validation and saving the chosen carrier company to the order:
// Conditional function for validation
function has_carrier_field(){
$settings = carrier_settings();
return array_intersect(WC()->session->get( 'chosen_shipping_methods' ), $settings['targeted_methods']);
}
// Validate the custom selection field
add_action('woocommerce_checkout_process', 'carrier_company_checkout_validation');
function carrier_company_checkout_validation() {
// Load settings and convert them in variables
extract( carrier_settings() );
if( has_carrier_field() && isset( $_POST[$field_id] ) && empty( $_POST[$field_id] ) )
wc_add_notice(
sprintf( __("Please select a %s as it is a required field.","woocommerce"),
'<strong>' . $label_name . '</strong>'
), "error" );
}
// Save custom field as order meta data
add_action( 'woocommerce_checkout_create_order', 'save_carrier_company_as_order_meta', 30, 1 );
function save_carrier_company_as_order_meta( $order ) {
// Load settings and convert them in variables
extract( carrier_settings() );
if( has_carrier_field() && isset( $_POST[$field_id] ) && ! empty( $_POST[$field_id] ) ) {
$order->update_meta_data( '_'.$field_id, $field_options[esc_attr($_POST[$field_id])] );
WC()->session->__unset( $field_id ); // remove session variable
}
}
Display the selected carrier on admin order pages, on customer orders and email notifications:
// Display custom field in admin order pages
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'admin_order_display_carrier_company', 30, 1 );
function admin_order_display_carrier_company( $order ) {
// Load settings and convert them in variables
extract( carrier_settings() );
$carrier = $order->get_meta( '_'.$field_id ); // Get carrier company
if( ! empty($carrier) ) {
// Display
echo '<p><strong>' . $label_name . '</strong>: ' . $carrier . '</p>';
}
}
// Display carrier company after shipping line everywhere (orders and emails)
add_filter( 'woocommerce_get_order_item_totals', 'display_carrier_company_on_order_item_totals', 1000, 3 );
function display_carrier_company_on_order_item_totals( $total_rows, $order, $tax_display ){
// Load settings and convert them in variables
extract( carrier_settings() );
$carrier = $order->get_meta( '_'.$field_id ); // Get carrier company
if( ! empty($carrier) ) {
$new_total_rows = [];
// Loop through order total rows
foreach( $total_rows as $key => $values ) {
$new_total_rows[$key] = $values;
// Inserting the carrier company under shipping method
if( $key === 'shipping' ) {
$new_total_rows[$field_id] = array(
'label' => $label_name,
'value' => $carrier,
);
}
}
return $new_total_rows;
}
return $total_rows;
}
All code goes on functions.php file of your active child theme (or theme). Tested and works.
Other related threads:
Enable delivery time options for a specific state in Woocommerce checkout
Update cart shipping data with AJAX in WooCommerce
On cart page (for chosen specific shipping method):
On checkout page page (for chosen specific shipping method):
On customer orders (email notifications and admin order pages too):
I have a Checkout page using Woocommerce where I have Discount added using the following code since I have a checkbox where user could check and the discount will apply. Here is the products page, please add any of them and proceed to Checkout page where you will get that Discount Checbox with label "Senior, Military, etc.". You can try entering "SAVE15" for the coupon code to apply.
// Add a custom checkbox fields after billing fields
function action_woocommerce_after_checkout_billing_form( $checkout ) {
// Add a custom checkbox field
echo '<h3 class="discount-type">' . __('DISCOUNT TYPE') . '</h3>';
woocommerce_form_field( 'discount30', array(
'type' => 'checkbox',
'label' => __( ' Senior, Military, Police, Firefighter, EMT Discount, Teacher',
'woocommerce' ),
'class' => array( 'form-row-wide' ),
), '' );
}
add_action( 'woocommerce_after_checkout_billing_form',
'action_woocommerce_after_checkout_billing_form', 10, 1 );
// Remove "(optional)" label on "discount30" field
function filter_woocommerce_form_field( $field, $key, $args, $value ) {
// Only on checkout page
if ( $key === 'discount30' && is_checkout() ) {
$optional = ' <span class="optional">(' . esc_html__( 'optional', 'woocommerce' ) .
')</span>';
$field = str_replace( $optional, '', $field );
}
return $field;
}
add_filter( 'woocommerce_form_field' , 'filter_woocommerce_form_field', 10, 4 );
// jQuery - Ajax script
function action_wp_footer() {
// Only on Checkout
if ( is_checkout() && ! is_wc_endpoint_url() ) :
if ( WC()->session->__isset('enable_fee') )
WC()->session->__unset('enable_fee')
?>
<script type="text/javascript">
jQuery( function($){
if ( typeof wc_checkout_params === 'undefined' )
return false;
$( 'form.checkout' ).on( 'change', 'input[name=discount30]', function(e) {
var fee = $(this).prop('checked') === true ? '1' : '';
$.ajax({
type: 'POST',
url: wc_checkout_params.ajax_url,
data: {
'action': 'enable_fee',
'enable_fee': fee,
},
success: function (result) {
$('body').trigger('update_checkout');
},
});
});
});
</script>
<?php
endif;
}
add_action( 'wp_footer', 'action_wp_footer' );
// Get Ajax request and saving to WC session
function get_enable_fee() {
if ( isset($_POST['enable_fee']) ) {
WC()->session->set( 'enable_fee', ( $_POST['enable_fee'] ? true : false ) );
}
die();
}
add_action( 'wp_ajax_enable_fee', 'get_enable_fee' );
add_action( 'wp_ajax_nopriv_enable_fee', 'get_enable_fee' );
// Add a custom 3 Cents Discount
function action_woocommerce_cart_calculate_fees( $cart ) {
// Only on checkout
if ( ( is_admin() && ! defined( 'DOING_AJAX' ) ) || ! is_checkout() )
return;
// Get number of items in the cart.
$items_in_cart = $cart->get_cart_contents_count();
// Calculate
$discount = 0.03 * $items_in_cart;
// Apply discount
if ( WC()->session->get('enable_fee') ) {
$cart->add_fee( __( 'Discount', 'woocommerce' ), -$discount );
}
}
add_action( 'woocommerce_cart_calculate_fees', 'action_woocommerce_cart_calculate_fees', 10, 1
);
Now, I'm using Coupon Code as well but don't want it to get mixed up so, how can I make a condition that if someone enter the Coupon Code, the discount that ABOVE code applies should be removed?
I'm creating a plugin that will calculate custom shipping variants with API. I have a jQuery script that calculates postal and fias codes based on an entered address.
$("#billing_address_1").suggestions({
serviceUrl: "https://suggestions.dadata.ru/suggestions/api/4_1/rs",
token: php_vars.dadata_suggest_token,
type: "ADDRESS",
count: 5,
onSelect: function (suggestion) {
$("#billing_city").val(suggestion.data.city);
$("#billing_state").val(suggestion.data.region);
$("#billing_postcode").val(suggestion.data.postal_code);
if (suggestion.data.settlement_fias_id)
$("#billing_fias_code").val(suggestion.data.settlement_fias_id);
else if (suggestion.data.city_fias_id)
$("#billing_fias_code").val(suggestion.data.city_fias_id);
else
$("#billing_fias_code").val('');
}
});
To store the fias code, I created custom field.
add_filter( 'woocommerce_checkout_fields' , array( $this, 'custom_checkout_fields' ));
add_action( 'woocommerce_checkout_update_order_meta', array( $this, 'shipping_apartment_update_order_meta') );
function custom_checkout_fields( $fields ) {
$fields['shipping']['shipping_fias_code'] = array(
'type' => 'text',
'label' => __('FIAS', 'woocommerce'),
'placeholder' => _x('Код', 'placeholder', 'woocommerce'),
'required' => false,
'class' => array('form-row-wide'),
'clear' => true
);
$fields['billing']['billing_fias_code'] = array(
'type' => 'text',
'label' => __('FIAS', 'woocommerce'),
'placeholder' => _x('Код', 'placeholder', 'woocommerce'),
'required' => false,
'class' => array('form-row-wide'),
'clear' => true
);
return $fields;
}
function shipping_apartment_update_order_meta( $order_id ) {
if ( ! empty( $_POST['shipping_fias_code'] ) ) {
update_post_meta( $order_id, 'shipping_fias_code', sanitize_text_field( $_POST['shipping_fias_code'] ) );
}
if ( ! empty( $_POST['billing_fias_code'] ) ) {
update_post_meta( $order_id, 'billing_fias_code', sanitize_text_field( $_POST['billing_fias_code'] ) );
}
}
The calculate_shipping() method in WC_Shipping_Method in woocommerce calculates shipping options using the $ package variable, which has an 'destination' array field with information about the shipping address. The postal code is passed into this array by default. But I also need to pass my custom field inside $package.
As I understand it, the field that I created will save the information added there via the jQuery script, only after the form is posted. But other fields included in $package['destination'] are saved immediately after adding information to them.
How do I add data from a custom field in the checkout form to the $package['destination'] variable?
I can't test (or modify) your jQuery code, so you will have to handle it yourself, maybe making some changes to it. I have revisited completely all your code (except your jQuery code) and everything works as you expect.
So $package['destination'] will have an additional entry for 'fias_code'.
The commented code:
// Add billing and shipping fields
add_filter( 'woocommerce_billing_fields' , 'custom_billing_fields' );
add_filter( 'woocommerce_shipping_fields' , 'custom_shipping_fields' );
function custom_shipping_fields( $fields ) {
$fields['shipping_fias_code'] = array(
'type' => 'text',
'label' => __('FIAS', 'woocommerce'),
'placeholder' => _x('Код', 'placeholder', 'woocommerce'),
'required' => false,
'class' => array('form-row-wide'),
'clear' => true
);
return $fields;
}
function custom_billing_fields( $fields ) {
$fields['billing_fias_code'] = array(
'type' => 'text',
'label' => __('FIAS', 'woocommerce'),
'placeholder' => _x('Код', 'placeholder', 'woocommerce'),
'required' => false,
'class' => array('form-row-wide'),
'clear' => true
);
return $fields;
}
// Ajax sender
add_action( 'wp_footer', 'checkout_send_fias_code_via_ajax_js' );
function checkout_send_fias_code_via_ajax_js() {
if ( is_checkout() && ! is_wc_endpoint_url() ) :
?><script type="text/javascript">
jQuery( function($){
if (typeof wc_checkout_params === 'undefined')
return false;
// Function that send the Ajax request
function sendAjaxRequest( value, fieldset = 'billing' ) {
$.ajax({
type: 'POST',
url: wc_checkout_params.ajax_url,
data: {
'action': 'fias_code',
'fias_code': value,
'fieldset' : fieldset
},
success: function (result) {
$(document.body).trigger('update_checkout'); // Update checkout processes
console.log( result ); // For testing (output data sent)
}
});
}
// Billing fias code change & input events
$(document.body).on( 'change input', 'input[name=billing_fias_code]', function() {
sendAjaxRequest( $(this).val() );
});
// Shipping fias code change & input events
$(document.body).on( 'change input', 'input[name=shipping_fias_code]', function() {
sendAjaxRequest( $(this).val(), 'shipping' );
});
});
</script>
<?php
endif;
}
// The Wordpress Ajax PHP receiver (set data to a WC_Session variable)
add_action( 'wp_ajax_fias_code', 'set_fias_code_to_wc_session' );
add_action( 'wp_ajax_nopriv_fias_code', 'set_fias_code_to_wc_session' );
function set_fias_code_to_wc_session() {
$field_key = 'fias_code';
if ( isset($_POST[$field_key]) && isset($_POST['fieldset']) ){
// Get data from custom session variable
$values = (array) WC()->session->get($field_key);
// Initializing when empty
if( ! empty($values) ) {
$values = array(
'billing' => WC()->customer->get_meta('billing_'.$field_key),
'shipping' => WC()->customer->get_meta('shipping_'.$field_key)
);
}
// Sanitizing data sent
$fieldset = esc_attr($_POST['fieldset']);
$fias_code = sanitize_text_field($_POST[$field_key]);
// Set / udpate custom WC_Session variable
$values[$fieldset] = $fias_code;
WC()->session->set($field_key, wc_clean($values));
// Send back to javascript the data received as an array (json encoded)
echo json_encode(array($fieldset.'_'.$field_key => $fias_code));
wp_die(); // always use die() or wp_die() at the end to avoird errors
}
}
// Update checkout fields 'fias_code' values from custom WC_session variable
add_filter('woocommerce_checkout_get_value', 'update_fias_code_checkout_fields_values', 10, 2 );
function update_fias_code_checkout_fields_values( $value, $input ) {
$field_key = 'fias_code';
// Get data from custom session variable
$values = (array) WC()->session->get($field_key);
if ( ! empty($values) ) {
if ( 'billing_'.$field_key === $input ) {
$value = $values['billing'];
}
if ( 'shipping_'.$field_key === $input ) {
$value = $values['shipping'];
}
}
return $value;
}
// Add 'fias_code' data to destination shipping packages
add_filter( 'woocommerce_cart_shipping_packages', 'add_fias_code_to_destination_shipping_package' );
function add_fias_code_to_destination_shipping_package( $packages ) {
$customer = WC()->customer; // The WC_Customer Object
// Get 'fias_code' data from customer meta data
$main_key = 'fias_code';
$meta_value = $customer->get_meta('shipping_'.$main_key);
$meta_value = empty($meta_value) ? $customer->get_meta('billing_'.$main_key) : $meta_value;
// Get data from custom session variable
$values = (array) WC()->session->get($main_key);
if ( ! empty($values) ) {
$session_value = $values['shipping'];
if ( $session_value === $meta_value ) {
$session_value = $values['billing'];
if ( $session_value !== $meta_value ) {
$meta_value = $values['billing'];
}
} else {
$meta_value = $session_value;
}
}
// Loop through shipping packages
foreach ( $packages as $key => $package ) {
// Set to destination package the "fias_code"
$packages[$key]['destination'][$main_key] = $meta_value;
}
return $packages;
}
// Remove custom WC_Session variable once order has been created (before thankyou)
add_action( 'woocommerce_checkout_order_created', 'remove_fias_code_custom_wc_session_variable' );
function remove_fias_code_custom_wc_session_variable() {
// Remove the custom WC_Session variable
WC()->session->__unset('fias_code');
}
This code goes in functions.php file of the active child theme (or active theme). Tested and works.
Important notes:
Displaying and save fias_code checkout fields:
I am using woocommerce_billing_fields and woocommerce_shipping_fields filter hooks instead of woocommerce_checkout_fields as that way the data is saved itself as order meta data and user meta data. So your last function is not required anymore.
The order meta keys start with an underscore like all billing and shipping order metadata.
The fields will be displayed on My account edit addresses… So if you want to avoid that you will need to add a condition to both related hooks.
Regarding my jQuery code:
You should better copy it to an external file and register/enqueue it in a clean WordPress way, restricting output to checkout page only. Then you will remove the related action hook and the hooked function…
Regarding action and filter hooks: You will have to change all add_action() and add_filter() for your plugin like:
add_filter( 'woocommerce_billing_fields' , array($this, 'custom_billing_fields') );
add_filter( 'woocommerce_shipping_fields' , array($this, 'custom_shipping_fields') );
add_action( 'wp_footer', array($this, 'checkout_send_fias_code_via_ajax_js') );
add_action( 'wp_ajax_fias_code', array($this, 'set_fias_code_to_wc_session') );
add_action( 'wp_ajax_nopriv_fias_code', array($this, 'set_fias_code_to_wc_session') );
add_filter('woocommerce_checkout_get_value', array($this, 'update_fias_code_checkout_fields_values'), 10, 2 );
add_filter( 'woocommerce_cart_shipping_packages', array($this, 'add_fias_code_to_destination_shipping_package') );
add_action( 'woocommerce_checkout_order_created', array($this, 'remove_fias_code_custom_wc_session_variable') );
In WooCommerce, I use a code that shows the steak weight selection form, saves the selection data and displays this data in the cart, on the checkout page, when editing the order and in email notifications.
// Display Custom Checkbox Field
add_action('woocommerce_product_options_general_product_data', 'steak_custom_field_add');
function steak_custom_field_add(){
global $post;
// Checkbox
woocommerce_wp_checkbox(
array(
'id' => '_steak_checkbox',
'label' => __('Steak Weight', 'woocommerce' ),
'description' => __( 'If necessary, enable steak weight selection', 'woocommerce' )
)
);
}
// Save Custom Checkbox Field
add_action('woocommerce_process_product_meta', 'steak_custom_field_save');
function steak_custom_field_save($post_id){
// Custom Product Checkbox Field
$steak_checkbox = isset( $_POST['_steak_checkbox'] ) ? 'yes' : 'no';
update_post_meta($post_id, '_steak_checkbox', esc_attr( $steak_checkbox ));
}
// Display Custom Select Box
add_action( 'woocommerce_before_add_to_cart_button', 'display_steak_custom_field', 0 );
function display_steak_custom_field() {
global $product;
// If is single product page and have the "steak_checkbox" enabled we display the field
if ( $product->get_meta( '_steak_checkbox' ) === 'yes' ) {
echo '<div class="steak_select_box">';
$select = woocommerce_form_field( 'steak_custom_options', array(
'type' => 'select',
'class' => array('my-steak-select-box form-row-wide'),
'label' => __('Steak Weight'),
'required' => false,
'return' => false,
'options' => array(
'' => 'Select...',
'300g' => '300g',
'400g' => '400g',
'500g' => '500g',
'600g' => '600g',
'700g' => '700g',
'800g' => '800g',
'900g' => '900g',
'1000g' => '1000g'
)
), '' );
echo $select;
echo '</div>';
}
}
// Add as custom cart item data
add_filter( 'woocommerce_add_cart_item_data', 'add_custom_steak_cart_item_data', 10, 21 );
function add_custom_steak_cart_item_data($cart_item_data, $product_id, $variation_id ){
if( isset( $_POST['steak_custom_options'] ) ) {
$cart_item_data['steak_option'] = wc_clean( $_POST['steak_custom_options'] );
}
return $cart_item_data;
}
// Add custom fields values under cart item name in cart
add_filter( 'woocommerce_cart_item_name', 'steak_custom_field_add_cart', 10, 21 );
function steak_custom_field_add_cart( $item_name, $cart_item, $cart_item_key ) {
if( ! is_cart() )
return $item_name;
if( isset($cart_item['steak_option']) ) {
$item_name .= '<div class="my-steak-class"><strong>' . __("Steak Weight", "woocommerce") . ':</strong> ' . $cart_item['steak_option'] . '</div>';
}
return $item_name;
}
// Display custom fields values under item name in checkout
add_filter( 'woocommerce_checkout_cart_item_quantity', 'steak_custom_checkout_cart_item_name', 10, 21 );
function steak_custom_checkout_cart_item_name( $item_qty, $cart_item, $cart_item_key ) {
if( isset($cart_item['steak_option']) ) {
$item_qty .= '<div class="my-steak-class"><strong>' . __("Steak Weight", "woocommerce") . ':</strong> ' . $cart_item['steak_option'] . 'гр.</div>';
}
return $item_qty;
}
// Save chosen select field value to each order item as custom meta data and display it everywhere
add_action('woocommerce_checkout_create_order_line_item', 'save_order_item_steak_field', 10, 21 );
function save_order_item_steak_field( $item, $cart_item_key, $values, $order ) {
if( isset($values['steak_option']) ) {
$key = __('Steak Weight', 'woocommerce');
$value = $values['steak_option'];
$item->update_meta_data( $key, $value ,$item->get_id());
}
}
add_action('wp_footer','add_footer_steak_script');
function add_footer_steak_script(){
?>
<script>
( function( $ ) {
$( document ).ready( function() {
$(document).on('change', '#steak_custom_options' ,function() {
$('.add_to_cart_button').data('steak_custom_options', this.value)
});
});
}( jQuery ) );
</script>
<?php
}
But unfortunately, I do not know how to solve one problem ...
The price of this product is calculated per 100 grams. The minimum order quantity is 300 grams. And I need that, when choosing the weight of the product, the final cost of this product should be calculated accordingly.
For example:
for 300g - $regular_price * 3 = $new_price
for 500g - $regular_price * 5 = $new_price
for 1000g - $regular_price * 10 = $new_price
etc.
The total cost of this product should be shown next to the main price per 100 grams.
I think this code will be useful to other developers as well. I will be very happy for your help!
To adjust the product price, on the cart page, etc.. based on the weight selected from the dropdown, add the following code
function my_before_calculate_totals( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Loop through cart items
foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
if( isset( $cart_item['steak_option'] ) ) {
// Remove the last 2 zeros (100g becomes 1, 300g becomes 3, 1000g becomes 10, etc...)
// Remove 'g' from grams
// convert string to integer
$chosen_weight = (int) str_replace( '00', '', str_replace('g', '', $cart_item['steak_option']) );
// Get current price
$current_price = $cart_item['data']->get_price();
// Set new price, price is already known per 100g
$cart_item['data']->set_price( $current_price * $chosen_weight );
}
}
}
add_action( 'woocommerce_before_calculate_totals', 'my_before_calculate_totals', 10, 1 );
To change the price on the single product page, based on the dropdown menu, add this
function add_footer_steak_script() {
global $woocommerce, $product;
?>
<script type="text/javascript">
jQuery(document).ready(function ($) {
console.log('JS works!');
var price = <?php echo $product->get_price(); ?>, currency = '<?php echo get_woocommerce_currency_symbol(); ?>';
$( '[name=steak_custom_options]' ).change(function(){
if (!(this.value < 1)) {
var dropdown_val = this.value;
var remove_g = dropdown_val.replace( 'g', '' );
var remove_double_zero = remove_g.replace( '00', '' );
var product_total = parseFloat( price * remove_double_zero );
$( '.woocommerce-Price-amount' ).html( currency + product_total.toFixed(2));
}
});
});
</script>
<?php
}
add_action('wp_footer','add_footer_steak_script');
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)