Woocommerce: Pay half (50%) on Cash on delivery using ajax - php

I am trying to make a ajax function to make users pay half the price of total amount, on custom cash on delivery method. When user selects yes or no radio button total amount is changed accordingly.
This: Link is a great example what I am trying to follow but I need to make it as a ajax call.
Here's how I added new fields before payment block:
add_action("woocommerce_review_order_before_payment", "new_buttons");
function new_buttons(){
echo '<div id="cash-on-delivery-wrap" class="cash-on-delivery-wrap">';
echo '<h5>Cash on delivery: </h5><div style="clear:both"></div>';
echo '<h6>Pay half 50%</h6><div style="clear:both"></div>';
echo '<div class="cod_button_wrap">';
echo '<label><input type=radio value="no" name="new-cod" checked/>No</label>';
echo '<label><input type=radio value="yes" name="new-cod" />Yes</label>';
echo '</div>';
echo '</div>';
}
Here is the JS:
jQuery(document).ready(function(){
jQuery("form.checkout").on("change", "#cash-on-delivery-wrap input",
function(){
var data = {
action: 'change_cod',
security: wc_checkout_params.update_order_review_nonce,
post_data: jQuery( 'form.checkout' ).serialize()
};
jQuery.post( ajaxurl, data, function( response )
{
jQuery( 'body' ).trigger( 'update_checkout' );
});
});
});
Here is the function:
function custom_cart_total() {
$current_state = $_POST['post_data'];
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if($current_state=='yes'){
WC()->cart->total *= 0.50;
}else{
WC()->cart->total;
}
exit;
}
add_action( 'wp_ajax_nopriv_change_cod', 'custom_cart_total' );
add_action( 'wp_ajax_change_cod', 'custom_cart_total' );
Cant seem to make it work, what am I missing here.

Note: The code in the linked answer, is only changing the displayed total amount in cart and checkout, but doesn't change it for real.
This answer is also changing the displayed checkout total amount. We need another function hooked in the order creation process, to update the total amount.
For Wordpress Ajax you need to register your script in an external JS file that you will upload in your active theme folder inside a js subfolder. Let say that this external file name will be pay_half.js.
1) Here is the function that will do that registration and will enable WordPress Ajax functionality:
add_action( 'wp_enqueue_scripts', 'ajax_change_shipping' );
function ajax_change_shipping() {
// Only on front-end and checkout page
if( is_admin() || ! is_checkout() ) return;
// Get the Path to the active theme or child theme or plugin folder
# $path = plugin_dir_url( __FILE__ ); // A plugin
# $path = get_template_directory_uri(); // A Normal theme
$path = get_stylesheet_directory_uri(); // A child theme
// Define the subfolder name
$subfolder = 'js';
// Define the file name
$filename = 'pay_half.js';
// Reference name of the script (should be unique)
$handle = 'pay-half';
// Set the ajaxurl parameter used in your script
$data = array(
'ajaxurl' => admin_url( 'admin-ajax.php' ),
);
// The variable name whichwill contain the data
$name = 'pay_half';
// Registering the javascript file and enqueues it.
wp_enqueue_script( $handle, $path."/$subfolder/$filename", array( 'jquery' ), '1.0', true );
// Localizing the registered script (Here using Ajax)
wp_localize_script( $handle, $name, $data );
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
2) Now the Javascript/jQuery external file (named: pay_half.js):
jQuery(document).ready(function ($) {
var selectHalf = '#cash-on-delivery-wrap input[type="radio"]',
paymentMethod = 'input[name^="payment_method"]',
codWrap = $('.cash-on-delivery-wrap'),
cartGTotal = $('input#cart_gtotal').val(),
codPartial;
// Detecting payment method on load to show/hide cod custom options
if( $(paymentMethod+':checked').val() == 'cod' )
codWrap.show("fast");
else
codWrap.hide("fast");
// Live detecting choosen payment method to show/hide cod custom options
$( 'form.checkout' ).on( 'change', 'input[name^="payment_method"]', function() {
if ( $(paymentMethod+':checked').val() == 'cod' ) {
codWrap.show("fast");
} else {
codWrap.hide("fast");
$('#cash-on-delivery-wrap input#cod-options_no').prop('checked', true);
}
$(document.body).trigger("update_checkout");
// console.log($(paymentMethod+':checked').val());
});
// The "Cod" custom options (ajax)
$(selectHalf).click(function(){
if($(selectHalf+':checked' ).val() == 'yes' ) codPartial = 'yes';
else codPartial = 'no';
$.ajax({ // This does the ajax request
url: pay_half.ajaxurl,
type : 'post',
data: {
'action':'cod_partial_payment', // Name of the php function
'cod_partial' : codPartial // Passing this variable to the PHP function
},
success:function(data) {
// Displaying the price (Ajax)
$( 'table.shop_table > tfoot > tr.order-total > td > strong > span' ).html(data.price_html);
if(codPartial == 'yes')
$('input#cart_remaining').val(data.price_remaining);
else
$('input#cart_remaining').val(0);
$(document.body).trigger("wc_fragment_refresh");
console.log(data);
},
error: function(error){
console.log(error);
}
});
});
});
3) The Display of your custom fields (revisisted).
I have added 2 hidden fields with the total amount and the remaining amount to pay.
add_action( 'woocommerce_review_order_before_payment', 'cod_payment_options', 10 );
function cod_payment_options(){
echo '<style>.cod-button-options label{display:inline-block; margin:0 6px;}</style>
<div id="cash-on-delivery-wrap" class="cash-on-delivery-wrap">
<h3>' . __( 'Cash on delivery option' ) . '</h3>';
woocommerce_form_field( 'cod-options', array(
'type' => 'radio',
'class' => array('form-row-wide', 'cod-button-options'),
'label' => __( '<b>Pay half (50%): </b>' ),
'required' => false,
'options' => array(
'no' => __( 'No' ),
'yes' => __( 'Yes' ),
)
), 'no' );
// Some additional hidden fields
echo '<input type="hidden" id="cart_gtotal" name="cart_gtotal" value="'. WC()->cart->total .'">
<input type="hidden" id="cart_remaining" name="cart_remaining" value="0" />
</div>';
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
4) The driven php function (Wordpress Ajax):
add_action( 'wp_ajax_nopriv_cod_partial_payment', 'cod_partial_payment' );
add_action( 'wp_ajax_cod_partial_payment', 'cod_partial_payment' );
function cod_partial_payment() {
if( ! isset($_POST['cod_partial']) ) return;
$current_state = $_POST['cod_partial'];
$remaining = 0;
if( $current_state == 'yes' ){
WC()->cart->total /= 2;
}
WC()->session->set( 'total', WC()->cart->total );
$response = array(
'price_html' => wc_price( WC()->cart->total ),
'price_remaining' => WC()->cart->total,
);
header( 'Content-Type: application/json' );
echo json_encode( $response );
die(); // Always (to avoid an error 500)
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
5) All Other php functions (Update order amount, save metadata, display a message):
// Replacing the total amount when COD option is enabled
add_action( 'woocommerce_checkout_create_order', 'cod_options_change_order_total_ammount', 10, 2 );
function cod_options_change_order_total_ammount( $order, $data ) {
if ( ! empty( $_POST['cod-options'] ) && $_POST['cod-options'] == 'yes' ) {
$remaining = sanitize_text_field( $_POST['cart_remaining'] );
$total = WC()->cart->total - floatval($remaining);
WC()->session->set( 'total', $total );
$order->set_total( $total );
}
}
// Updating order meta data for Cod selected option
add_action( 'woocommerce_checkout_update_order_meta', 'cod_options_update_order_meta', 10, 1 );
function cod_options_update_order_meta( $order_id ) {
if ( ! empty( $_POST['cod-options'] ) && $_POST['cod-options'] == 'yes' ) {
update_post_meta( $order_id, '_cod_remaining_amount', sanitize_text_field( $_POST['cart_remaining'] ) );
update_post_meta( $order_id, '_cod_partial_paid_amount', sanitize_text_field( $_POST['cart_gtotal'] - $_POST['cart_remaining'] ) );
}
update_post_meta( $order_id, '_cod_partial_payment_option', sanitize_text_field( $_POST['cod-options'] ) );
}
// Displaying the remaining amount to pay in a custom message on Order received page (thank you)
add_action( 'woocommerce_thankyou_cod', 'cod_options_woocommerce_thankyou', 10, 1 );
function cod_options_woocommerce_thankyou( $order_id ) {
if( get_post_meta( $order_id, '_cod_partial_payment_option', true ) == 'yes' ){
$ra = '<span style="color:#96588a;">'.wc_price( get_post_meta( $order_id, '_cod_remaining_amount', true )).'</span>';
?>
<ul class="woocommerce-order-overview woocommerce-thankyou-cod-options order_details">
<li class="woocommerce-order-overview__remaining_total order">
<strong><?php echo __("There is a remaining amount of $ra to pay on this order."); ?></strong>
</li>
</ul>
<?php
}
}
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 3+ and works.

Related

Add column and action button to restore stock at woo commerce admin products list table

Am trying to add an extra column to the admin products list table in woo commerce. I want the action button to restore stoke or just increase stock by one on-click. So far, the code I have adds the column, and I can add an icon, or a button, but I do not know how to proceed with making the button restore stock or increment it by one. Action button only visible when product is out of stock
Below is the code: Any help will be much appretiated.
add_filter( 'manage_edit-product_columns', 'add_to_admin_products_column', 9999 );
function add_to_admin_products_column( $columns ){
$columns['Columnx'] = 'Columnx';
return $columns;
}
add_action( 'manage_product_posts_custom_column', 'admin_products_action_column_content', 10, 2 );
function admin_products_action_column_content( $column, $product_id ){
if ( $column == 'columnx' ) {
$product = wc_get_product( $product_id );
echo $product->get_catalog_visibility();
}
}
I finally got it to work after hours of try to do what I don't know. I know its dirty, but works for me. Improvements are welcome.
add_filter( 'manage_edit-product_columns', 'wins_admin_products_visibility_column', 9999 );
function wins_admin_products_visibility_column( $columns ){
$columns['act'] = 'Actions';
return $columns;
}
add_action( 'manage_product_posts_custom_column', 'wins_admin_products_action_column_content', 10, 2 );
function wins_admin_products_action_column_content( $column, $product_id ){
$product = wc_get_product( $product_id );
$stock_status = $product->get_stock_status();
if ( $column == 'act' && $stock_status=='outofstock' ) {
echo '<label style ="padding: 5px; border-radius:3px; border-color:white-smoke; background-color:silver;" ><input type="checkbox" id="some_checkbox" style ="display:none" data-productid="' . get_the_ID() .'" class="some_checkbox" ' . checked( 'yes', get_post_meta( get_the_ID(), 'some_meta_key', true ), false ) . '/><small style="display:none;color:#7ad03a"></small>
Make Vacant
</label>';
}
}
// this code adds jQuery script to website footer that allows to send AJAX request
add_action( 'admin_footer', 'misha_jquery_event' );
function misha_jquery_event(){
echo "<script>jQuery(function($){
$('.some_checkbox').click(function(){
var checkbox = $(this),
_stock = (checkbox.is(':checked') ? '1' : '0' );
$.ajax({
type: 'POST',
data: {
action: 'productmetasave', // wp_ajax_{action} WordPress hook to process AJAX requests
value: _stock,
product_id: checkbox.attr('data-productid'),
myajaxnonce : '" . wp_create_nonce( "activatingcheckbox" ) . "'
},
beforeSend: function( xhr ) {
checkbox.prop('disabled', true );
},
url: ajaxurl, // as usual, it is already predefined in /wp-admin
success: function(data){
checkbox.prop('disabled', false ).next().html(data).show().fadeOut(400);
window.location.reload();
}
});
});
});</script>";
}
// this small piece of code can process our AJAX request
add_action( 'wp_ajax_productmetasave', 'misha_process_ajax' );
function misha_process_ajax(){
check_ajax_referer( 'activatingcheckbox', 'myajaxnonce' );
if( update_post_meta( $_POST[ 'product_id'] , '_stock', $_POST['value'] ) ) {
//$product->set_stock_status('instock');
update_post_meta( $_POST[ 'product_id'] , '_stock_status','instock');
exit;
}
//header("refresh:0");
//exit;
//die();
}

Calculate fee from cart totals (subtotal + shipping) without adding it to order total value in WooCommerce

Based on Add a checkout checkbox field that enable a percentage fee in Woocommerce answer code I created a checkbox on the checkout page.
When it is checked, it applies a 15% freight forwarding fee.
// Add a custom checkbox fields before order notes
add_action( 'woocommerce_before_order_notes', 'add_custom_checkout_checkbox', 20 );
function add_custom_checkout_checkbox(){
// Add a custom checkbox field
woocommerce_form_field( 'forwarding_fee', array(
'type' => 'checkbox',
'label' => __('15% forwarding fee'),
'class' => array( 'form-row-wide' ),
), '' );
}
// jQuery - Ajax script
add_action( 'wp_footer', 'checkout_fee_script' );
function checkout_fee_script() {
// 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=forwarding_fee]', 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;
}
// Get Ajax request and saving to WC session
add_action( 'wp_ajax_enable_fee', 'get_enable_fee' );
add_action( 'wp_ajax_nopriv_enable_fee', 'get_enable_fee' );
function get_enable_fee() {
if ( isset($_POST['enable_fee']) ) {
WC()->session->set('enable_fee', ($_POST['enable_fee'] ? true : false) );
}
die();
}
// Add a custom dynamic 15% fee
add_action( 'woocommerce_cart_calculate_fees', 'custom_percetage_fee', 20, 1 );
function custom_percetage_fee( $cart ) {
// Only on checkout
if ( ( is_admin() && ! defined( 'DOING_AJAX' ) ) || ! is_checkout() )
return;
$percent = 15;
if( WC()->session->get('enable_fee') )
$cart->add_fee( __( 'Forwarding fee', 'woocommerce')." ($percent%)", ($cart->get_subtotal() * $percent / 100) );
}
Currently, this fee is calculated from the subtotal and added up to the order total value.
I need a solution where this fee is calculated from a sum of subtotal + shipping AND IS NOT added to the order total value.
I will rename "fee" to "deposit".
Please see a screenshot:
Since you don't want to add it to the total, you can add a custom table row to the woocommerce-checkout-review-order-table instead of a cart fee. So my answer is not based on the WooCommerce fee and is completely separate from it.
The custom table row will then show/hide the percentage, based on if the checkbox is checked.
Explanation via one-line comments, added to my answer.
So you get:
// Add checkbox field
function action_woocommerce_before_order_notes( $checkout ) {
// Add field
woocommerce_form_field( 'my_id', array(
'type' => 'checkbox',
'class' => array( 'form-row-wide' ),
'label' => __( '15% and some other text', 'woocommerce' ),
'required' => false,
), $checkout->get_value( 'my_id' ));
}
add_action( 'woocommerce_before_order_notes', 'action_woocommerce_before_order_notes', 10, 1 );
// Save checkbox value
function action_woocommerce_checkout_create_order( $order, $data ) {
// Set the correct value
$checkbox_value = isset( $_POST['my_id'] ) ? 'yes' : 'no';
// Update meta data
$order->update_meta_data( '_my_checkbox_value', $checkbox_value );
}
add_action( 'woocommerce_checkout_create_order', 'action_woocommerce_checkout_create_order', 10, 2 );
// Add table row on the checkout page
function action_woocommerce_before_order_total() {
// Initialize
$percent = 15;
// Get subtotal & shipping total
$subtotal = WC()->cart->subtotal;
$shipping_total = WC()->cart->get_shipping_total();
// Total
$total = $subtotal + $shipping_total;
// Result
$result = ( $total / 100 ) * $percent;
// The Output
echo '<tr class="my-class">
<th>' . __( 'My text', 'woocommerce' ) . '</th>
<td data-title="My text">' . wc_price( $result ) . '</td>
</tr>';
}
add_action( 'woocommerce_review_order_before_order_total', 'action_woocommerce_before_order_total', 10, 0 );
// Show/hide table row on the checkout page with jQuery
function action_wp_footer() {
// Only on checkout
if ( is_checkout() && ! is_wc_endpoint_url() ) :
?>
<script type="text/javascript">
jQuery( function($){
// Selector
var my_input = 'input[name=my_id]';
var my_class = '.my-class';
// Show or hide
function show_or_hide() {
if ( $( my_input ).is(':checked') ) {
return $( my_class ).show();
} else {
return $( my_class ).hide();
}
}
// Default
$( document ).ajaxComplete(function() {
show_or_hide();
});
// On change
$( 'form.checkout' ).change(function() {
show_or_hide();
});
});
</script>
<?php
endif;
}
add_action( 'wp_footer', 'action_wp_footer', 10, 0 );
// If desired, add new table row to emails, order received (thank you page) & my account -> view order
function filter_woocommerce_get_order_item_totals( $total_rows, $order, $tax_display ) {
// Get checkbox value
$checkbox_value = $order->get_meta( '_my_checkbox_value' );
// NOT equal to yes, return
if ( $checkbox_value != 'yes' ) return $total_rows;
// Initialize
$percent = 15;
// Get subtotal & shipping total
$subtotal = $order->get_subtotal();
$shipping_total = $order->get_shipping_total();
// Total
$total = $subtotal + $shipping_total;
// Result
$result = ( $total / 100 ) * $percent;
// Save the value to be reordered
$order_total = $total_rows['order_total'];
// Remove item to be reordered
unset( $total_rows['order_total'] );
// Add new row
$total_rows['my_text'] = array(
'label' => __( 'My text:', 'woocommerce' ),
'value' => wc_price( $result ),
);
// Reinsert removed in the right order
$total_rows['order_total'] = $order_total;
return $total_rows;
}
add_filter( 'woocommerce_get_order_item_totals', 'filter_woocommerce_get_order_item_totals', 10, 3 );

Update totals after applying or removing a coupon programmatically In WooCommerce

I have created a checkbox (It does not look like a checkbox anymore) that apply/remove a coupong on change. This works good. But the total does not update on the apply, the page has to be refreshed. I have build this function with some cut and paste from other functions, it was once a radio field, and it might not be the best practise. The coupong ads a discount for 500 SEK.
But how to do I recalculate the total after the coupong is applied?
As you can see in the end, I have tried WC()->cart->calculate_totals();.
This is the site and checkout: https://www.klubbtryck.se/nif/kassa/
This is my code:
// Add a custom checkout field
add_action( 'woocommerce_review_order_after_shipping', 'checkout_shipping_form_delivery_addition_nifny', 20 );
function checkout_shipping_form_delivery_addition_nifny(){
$domain = 'wocommerce';
if ( WC()->session->get( 'chosen_shipping_methods' )[0] == 'local_pickup:3' ) :
echo '<tr class="delivery-radio"><th>' . __('Gift Card', $domain) . '</th><td>';
$chosen = WC()->session->get('chosen_delivery');
$chosen = empty($chosen) ? WC()->checkout->get_value('delivery') : $chosen;
$chosen = empty($chosen) ? 0 : $chosen;
if( $chosen == 1){ $chosen = true; } else { $chosen = false; }
// Add a custom checkbox field
woocommerce_form_field( 'radio_delivery', array(
'type' => 'checkbox',
'label' => '<label for="radio_delivery" class="checkbox-label"><span class="presentkortbesk">I have a gift card</span><span class="priset">-500kr</span></label>',
'class' => array( 'form-row-wide' ),
'required' => false,
//'default' => false,
), $chosen );
echo '</td></tr>';
endif;
}
// jQuery - Ajax script
add_action( 'wp_footer', 'checkout_delivery_script_nifny' );
function checkout_delivery_script_nifny() {
// Only checkout page
if ( ! is_checkout() ) return;
?>
<script type="text/javascript">
jQuery( function($){
if (typeof wc_checkout_params === 'undefined')
return false;
$('form.checkout').on('change', 'input[name=radio_delivery]', function(e){
e.preventDefault();
var d = $(this).prop('checked') === true ? 1 : 0;
//var d = $(this).val();
//alert('value: '+d);
$.ajax({
type: 'POST',
url: wc_checkout_params.ajax_url,
data: {
'action': 'delivery',
'delivery': d,
},
success: function (result) {
$('body').trigger('update_checkout');
//console.log(result); // just for testing | TO BE REMOVED
},
error: function(error){
//console.log(error); // just for testing | TO BE REMOVED
}
});
});
});
</script>
<?php
}
// Get Ajax request and saving to WC session
add_action( 'wp_ajax_delivery', 'wc_get_delivery_ajax_data_nifny' );
add_action( 'wp_ajax_nopriv_delivery', 'wc_get_delivery_ajax_data_nifny' );
function wc_get_delivery_ajax_data_nifny() {
if ( isset($_POST['delivery']) ){
WC()->session->set('chosen_delivery', sanitize_key( $_POST['delivery'] ) );
echo json_encode( $delivery ); // Return the value to jQuery
}
die();
}
// Add a custom dynamic delivery fee
add_action( 'woocommerce_cart_calculate_fees', 'add_packaging_fee_nifny', 20, 1 );
function add_packaging_fee_nifny( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// Only for targeted shipping method
if ( WC()->session->get( 'chosen_shipping_methods' )[0] != 'local_pickup:3' )
return;
if( WC()->session->get( 'chosen_delivery' ) == 1 ){
if (!in_array('nynashamn2020', WC()->cart->get_applied_coupons())) {
WC()->cart->apply_coupon('card2020');
//WC()->cart->calculate_totals();
}
} else {
if (in_array('nynashamn2020', WC()->cart->get_applied_coupons())) {
WC()->cart->remove_coupon('card2020');
}
}
}
You should replace woocommerce_cart_calculate_fees hook that is only made for Fees with similar woocommerce_before_calculate_totals more appropriated hook as follows:
// Add a custom dynamic delivery fee
add_action( 'woocommerce_before_calculate_totals', 'add_packaging_fee_nifny' );
function add_packaging_fee_nifny( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// Only for targeted shipping method
if ( WC()->session->get( 'chosen_shipping_methods' )[0] != 'local_pickup:3' )
return;
if( WC()->session->get( 'chosen_delivery' ) == 1 ){
if (!in_array('nynashamn2020', WC()->cart->get_applied_coupons())) {
WC()->cart->apply_coupon('card2020');
//WC()->cart->calculate_totals();
}
} else {
if (in_array('nynashamn2020', WC()->cart->get_applied_coupons())) {
WC()->cart->remove_coupon('card2020');
}
}
}
Code goes in functions.php file of the active child theme (or active theme). It should better works.

Custom checkbox that adds a fee in WooCommerce cart page

Based on Display a checkbox that add a fee in Woocommerce checkout page answer code I am trying to change usage to Cart page, rather than checkout page per clients request.
So far I managed to display custom fee check box on cart page and trigger Ajax.
However Cart totals are not updated.
If someone can help on code or point me in right direction?
// Display the custom checkbox field in cart
add_action( 'woocommerce_cart_totals_before_order_total', 'fee_installment_checkbox_field', 20 );
function fee_installment_checkbox_field(){
echo '<tr class="packing-select"><th>Priority Dispatch</th><td>';
woocommerce_form_field( 'priority_fee', array(
'type' => 'checkbox',
'class' => array('installment-fee form-row-wide'),
'label' => __(' $20.00'),
'placeholder' => __(''),
), WC()->session->get('priority_fee') ? '1' : '' );
echo '<div class="tooltip">?
<span class="tooltiptext">By selecting this option... </span>
</div></td>';
}
// jQuery - Ajax script
add_action( 'wp_footer', 'woo_add_cart_fee' );
function woo_add_cart_fee() {
// Only on Checkout
if( ! is_wc_endpoint_url() ) :
if( WC()->session->__isset('priority_fee') )
WC()->session->__unset('priority_fee')
?>
<script type="text/javascript">
jQuery( function($){
//if (typeof wc_add_to_cart_params === 'undefined')
// return false;
$('tr.packing-select').on('change', 'input[name=priority_fee]', function(){
console.log('tests');
var fee = $(this).prop('checked') === true ? '1' : '';
$.ajax({
type: 'POST',
//url: wc_add_to_cart_params.ajax_url,
data: {
'action': 'priority_fee',
'priority_fee': fee,
},
success: function (response) {
$('body').trigger('added_to_cart');
},
});
});
});
</script>
<?php
endif;
}
// Get Ajax request and saving to WC session
add_action( 'wp_ajax_priority_fee', 'get_priority_fee' );
add_action( 'wp_ajax_nopriv_priority_fee', 'get_priority_fee' );
function get_priority_fee() {
if ( isset($_POST['priority_fee']) ) {
WC()->session->set('priority_fee', ($_POST['priority_fee'] ? true : false) );
}
die();
}
// Add a custom calculated fee conditionally
add_action( 'woocommerce_cart_calculate_fees', 'set_priority_fee' );
function set_priority_fee( $cart ){
if ( is_admin() && ! defined('DOING_AJAX') )
return;
if ( did_action('woocommerce_cart_calculate_fees') >= 2 )
return;
if ( 1 == WC()->session->get('priority_fee') ) {
$items_count = WC()->cart->get_cart_contents_count();
$fee_label = sprintf( __( "PRIORITY DISPATCH %s %s" ), '×', $items_count );
$fee_amount = 20;
WC()->cart->add_fee( $fee_label, $fee_amount );
}
}
add_filter( 'woocommerce_form_field' , 'remove_optional_txt_from_installment_checkbox', 10, 4 );
function remove_optional_txt_from_installment_checkbox( $field, $key, $args, $value ) {
// Only on checkout page for Order notes field
if( 'priority_fee' === $key ) {
$optional = ' <span class="optional">(' . esc_html__( 'optional', 'woocommerce' ) . ')</span>';
$field = str_replace( $optional, '', $field );
}
return $field;
}
Additionally is this the way this should be done, or is there an easier way to do it?
There are some mistakes in your code. Also, on cart page, jQuery events and Ajax behavior are a bit different, so it requires some changes:
// Display the checkout field in cart page totals section
add_action( 'woocommerce_cart_totals_before_order_total', 'display_priority_fee_checkbox_field', 20 );
function display_priority_fee_checkbox_field(){
echo '<tr class="installment-section">
<th>'.__("Priority Dispatch").'</th><td>';
woocommerce_form_field( 'priority_fee', array(
'type' => 'checkbox',
'class' => array('form-row-wide'),
'label' => __(' $20.00'),
), WC()->session->get('priority_fee') ? '1' : '' );
echo '<div class="tooltip">?
<span class="tooltiptext">'.__("By selecting this option... ").'</span>
</div></td>';
}
// Remove "(optional)" text from the field
add_filter( 'woocommerce_form_field' , 'remove_optional_txt_from_priority_fee_checkbox', 10, 4 );
function remove_optional_txt_from_priority_fee_checkbox( $field, $key, $args, $value ) {
// Only on checkout page for Order notes field
if( 'priority_fee' === $key ) {
$optional = ' <span class="optional">(' . esc_html__( 'optional', 'woocommerce' ) . ')</span>';
$field = str_replace( $optional, '', $field );
}
return $field;
}
// jQuery :: Ajax script
add_action( 'wp_footer', 'priority_fee_js_script' );
function priority_fee_js_script() {
// On Order received page, remove the wc session variable if it exist
if ( is_wc_endpoint_url('order-received')
&& WC()->session->__isset('priority_fee') ) :
WC()->session->__unset('priority_fee');
// On Cart page: jQuert script
elseif ( is_cart() ) :
?>
<script type="text/javascript">
jQuery( function($){
if (typeof woocommerce_params === 'undefined')
return false;
var c = 'input[name=priority_fee]';
$(document.body).on( 'click change', c, function(){
console.log('click');
var fee = $(c).is(':checked') ? '1' : '';
$.ajax({
type: 'POST',
url: woocommerce_params.ajax_url,
data: {
'action': 'priority_fee',
'priority_fee': fee,
},
success: function (response) {
setTimeout(function(){
$(document.body).trigger('added_to_cart');
}, 500);
},
});
});
});
</script>
<?php
endif;
}
// Get Ajax request and saving to WC session
add_action( 'wp_ajax_priority_fee', 'priority_fee_ajax_receiver' );
add_action( 'wp_ajax_nopriv_priority_fee', 'priority_fee_ajax_receiver' );
function priority_fee_ajax_receiver() {
if ( isset($_POST['priority_fee']) ) {
$priority_fee = $_POST['priority_fee'] ? true : false;
// Set to a WC Session variable
WC()->session->set('priority_fee', $priority_fee );
echo $priority_fee ? '1' : '0';
die();
}
}
// Add a custom calculated fee conditionally
add_action( 'woocommerce_cart_calculate_fees', 'set_priority_fee' );
function set_priority_fee( $cart ){
if ( is_admin() && ! defined('DOING_AJAX') )
return;
if ( WC()->session->get('priority_fee') ) {
$item_count = $cart->get_cart_contents_count();
$fee_label = sprintf( __( "PRIORITY DISPATCH %s" ), '× ' . $item_count );
$fee_amount = 20 * $item_count;
$cart->add_fee( $fee_label, $fee_amount );
}
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.

Enable delivery time options for a specific state in Woocommerce checkout

Based on "Add a custom checkbox in WooCommerce checkout which value shows in admin edit order", I am trying to display custom radio buttons fields only if customer state is NewYork to let customer pick a delivery time in checkout page by choosing a delivery time option. Then admin could see the chosen delivery time in admin order edit pages.
This image will explain everything:
Here is my code attempt (with one checkbox for testing):
$user_state = get_user_meta( get_current_user_id(), 'billing_state')
if($user_state=='NY'){
add_action( 'woocommerce_review_order_before_submit', 'my_custom_checkout_field' );
function my_custom_checkout_field() {
echo '<div id="my_custom_checkout_field">';
woocommerce_form_field( 'my_field_name', array(
'type' => 'checkbox',
'class' => array('input-checkbox'),
'label' => __('My custom checkbox'),
'required'
), WC()->checkout->get_value( 'my_field_name' ) );
echo '</div>';
}
// Save the custom checkout field in the order meta, when checkbox has been checked
add_action( 'woocommerce_checkout_update_order_meta', 'custom_checkout_field_update_order_meta', 10, 1 );
function custom_checkout_field_update_order_meta( $order_id ) {
if ( ! empty( $_POST['my_field_name'] ) )
update_post_meta( $order_id, 'my_field_name', $_POST['my_field_name'] );
}
// Display the custom field result on the order edit page (backend) when checkbox has been checked
add_action( 'woocommerce_admin_order_data_after_billing_address', 'display_custom_field_on_order_edit_pages', 10, 1 );
function display_custom_field_on_order_edit_pages( $order ){
$my_field_name = get_post_meta( $order->get_id(), 'my_field_name', true );
if( $my_field_name == 1 )
echo '<p><strong>My custom field: </strong> <span style="color:red;">Is enabled</span></p>';
}}
But the custom field value isn't displayed. Any help is appreciated.
First you need to include your state condition (New York) inside the function that display your custom radio buttons on checkout page:
if( WC()->customer->get_shipping_state() == 'NY' ) {
// Do something for customers from "New York"
}
To make this possible Ajax and WC_Session is required, because checkout page is Ajax refreshed and it will not keep the customer choice.
So I have revisited all your existing code:
// Custom setting function: Here define your delivery options for "New york"
function get_newyork_delivery_options(){
return array(
'9am-2pm' => __('9 AM - 2 PM'),
'2pm-6pm' => __('2 PM - 6 PM'),
'6pm-9pm' => __('6 PM - 9 PM'),
);
}
// Display delivery custom radio buttons field in checkout for "New york" state
add_action( 'woocommerce_review_order_after_shipping', 'display_delivery_choice_field', 20 );
function display_delivery_choice_field(){
// Only for "New York" state
if( WC()->customer->get_shipping_state() == 'NY' ) :
$choice = WC()->session->get('delivery_choice');
// Display the radio buttons
echo '<tr class="delivery-options">
<th>' . __("Delivery time") . '<br><em>(' . __("for New York") . ')</em></th>
<td><span class="woocommerce-input-wrapper">';
foreach ( get_newyork_delivery_options() as $key => $label ) {
$checked = $choice == $key ? 'checked="checked"' : '';
$field_id = 'delivery_time' . $key;
echo '<label for="' . $field_id . '" class="radio " style="display:block">
<input type="radio" class="input-radio " id="' . $field_id . '" name="delivery_time" value="' . $key . '" ' . $checked . '> ' . $label;
echo '</label>';
}
echo '</span></td>
<tr>';
endif;
}
// jQuery - Ajax script
add_action( 'wp_footer', 'checkout_delivery_script' );
function checkout_delivery_script() {
// Only on Checkout
if( is_checkout() && ! is_wc_endpoint_url() ) :
if( WC()->session->__isset('delivery_choice') )
WC()->session->__unset('delivery_choice')
?>
<script type="text/javascript">
jQuery( function($){
if (typeof wc_checkout_params === 'undefined')
return false;
$('form.checkout').on('change', 'input[name="delivery_time"]', function(){
var delivery = $(this).val();
$.ajax({
type: 'POST',
url: wc_checkout_params.ajax_url,
data: {
'action': 'delivery_choice',
'delivery_choice': delivery,
},
success: function (result) {
$('body').trigger('update_checkout');
},
});
});
});
</script>
<?php
endif;
}
// Get Ajax request and saving to WC session
add_action( 'wp_ajax_delivery_choice', 'get_delivery_choice' );
add_action( 'wp_ajax_nopriv_delivery_choice', 'get_delivery_choice' );
function get_delivery_choice() {
if ( isset($_POST['delivery_choice']) ) {
WC()->session->set('delivery_choice', esc_attr( $_POST['delivery_choice'] ) );
echo WC()->session->get('delivery_choice');
}
die();
}
// Save the chosen delivery time as order meta data
add_action('woocommerce_checkout_create_order', 'custom_checkout_field_added_as_order_meta', 10, 2 );
function custom_checkout_field_added_as_order_meta( $order, $data ) {
if ( isset( $_POST['delivery_option'] ) )
$order->add_meta_data( '_delivery_time', esc_attr( $_POST['delivery_time'] ) );
}
// Display the chosen delivery time on order totals lines everywhere (except admin)
add_action('woocommerce_get_order_item_totals', 'display_bacs_option_on_order_totals', 10, 3 );
function display_bacs_option_on_order_totals( $total_rows, $order, $tax_display ) {
if ( $delivery_time = $order->get_meta('_delivery_time') ) {
$sorted_total_rows = [];
foreach ( $total_rows as $key_row => $total_row ) {
$sorted_total_rows[$key_row] = $total_row;
if( $key_row === 'shipping' ) {
$sorted_total_rows['delivery_time'] = [
'label' => __( "Delivery time", "woocommerce"),
'value' => esc_html( get_newyork_delivery_options()[$delivery_time] ),
];
}
}
$total_rows = $sorted_total_rows;
}
return $total_rows;
}
// Display the chosen delivery option on admin order edit page for new york state
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'display_custom_field_on_order_edit_pages', 10, 1 );
function display_custom_field_on_order_edit_pages( $order ){
if( $key = $order->get_meta( '_delivery_time' ) ) {
$label = get_newyork_delivery_options()[$key];
echo '<p><strong>Delivery <em>(New York)</em>: </strong> <span style="color:green;">' . $label . '</span></p>';
}
}
Code goes in function.php file of the active child theme (or active theme). Tested and works.

Categories