How to update cart item quantities and price based on custom select field on checkout in woocommerce - ajax

I'm trying to update cart item quantities and its price based on the custom selection field on checkout. If someone selects "Single person" option from the dropdown then the quantity will be 1 and if someone select "Two person" from the Dropdown then the cart quantity will update to 2 and the price will update as well.
Users can only add one product to the cart, Here is the reference link from where I got some help.
https://www.webroomtech.com/woocommerce-checkout-change-quantity-and-delete-products/
Im using wordpress ajax call to acheive this.
In functions.php I have this code
function customjs_script_add_quanity_js() {
wp_enqueue_script( 'checkout_script', get_stylesheet_directory_uri() . '/assets/js/add_quantity.js', array('jquery') );
wp_localize_script( 'checkout_script', 'add_quantity', array( 'ajax_url' => admin_url( 'admin-ajax.php' ) ) );
}
add_action( 'wp_enqueue_scripts', 'customjs_script_add_quanity_js' );
function custom_addqty_load_ajax() {
if ( !is_user_logged_in() ){
add_action( 'wp_ajax_nopriv_update_order_review', 'anp_update_order_review' );
} else{
add_action( 'wp_ajax_update_order_review', 'anp_update_order_review' );
}
}
add_action( 'init', 'custom_addqty_load_ajax' );
function anp_update_order_review() {
$numberof_people = intval($_POST['number_of_people']);
$values = array();
parse_str($_POST['post_data'], $values);
$cart = $values['cart']; //This have no values in it,
foreach ( $cart as $cart_key => $cart_value ){
WC()->cart->set_quantity( $cart_key, $numberof_people);
WC()->cart->calculate_totals();
woocommerce_cart_totals();
}
wp_die();
}
In my **add_quantity.js** file
jQuery(function() {
jQuery( "form.checkout" ).on( "change", ".woocommerce-additional-fields select#______NumberOfTravelers______", function( ) {
//console.log('on chage called');
var number_of_people = jQuery(this).val();
var data = {
action: 'update_order_review',
security: wc_checkout_params.update_order_review_nonce,
number_of_people: number_of_people,
post_data: jQuery( 'form.checkout' ).serialize()
};
jQuery.post( add_quantity.ajax_url, data, function( response )
{
jQuery( 'body' ).trigger( 'update_checkout' );
console.log('success');
});
});
});
My $cart variable in the ajax function shows empty and the cart is not updating on the selection field change. Any help will be appreciated. Thanks
I've updated my code function.php and add_quantity.js
function customjs_script_add_quanity_js() {
wp_enqueue_script( 'checkout_script', get_stylesheet_directory_uri() . '/assets/js/add_quantity.js', array('jquery') );
wp_localize_script( 'checkout_script', 'add_quantity', array( 'ajax_url' => admin_url( 'admin-ajax.php' ) ) );
}
add_action( 'wp_enqueue_scripts', 'customjs_script_add_quanity_js' );
function anp_update_order_review() {
$numberof_people = intval($_POST['number_of_people']);
$cart = WC()->cart->get_cart();
foreach ( $cart as $cart_item_key => $item ){
WC()->cart->set_quantity( $cart_item_key, $numberof_people);
WC()->cart->calculate_totals();
woocommerce_cart_totals(); //checkout order total not calculating correctly and cart items quantity and price not changing
}
wp_die();
}
add_action( 'wp_ajax_nopriv_update_order_review', 'anp_update_order_review' );
add_action( 'wp_ajax_update_order_review', 'anp_update_order_review' );
updated add_quantity.js file
jQuery(function() {
jQuery( "form.checkout" ).on( "change", ".woocommerce-additional-fields select#________NumberOfTravelers________", function( ) {
var number_of_people = jQuery(this).val();
//console.log('nop: ' + number_of_people);
var data = {
action: 'update_order_review',
number_of_people: number_of_people,
security: wc_checkout_params.update_order_review_nonce
};
jQuery.post( add_quantity.ajax_url, data, function( response )
{
jQuery( 'body' ).trigger( 'update_checkout' );
console.log('success');
});
});
});
Now this start working but it calculating wrong price on checkout order table at bottom and cart item quantity and price is not updating on dropdown selection.

I think you don't need serialize checkout form data.
just in your ajax function use the following code:
$cart = WC()->cart;

for your ajax function you don't have to check if the user is logged in. By default, WordPress checks the hook if its admin side or front end side. In addition, you are calling the hooks from inside a function.
function anp_update_order_review() {
$numberof_people = intval($_POST['number_of_people']);
$values = array();
parse_str($_POST['post_data'], $values);
$cart = $values['cart']; //This have no values in it,
foreach ( $cart as $cart_key => $cart_value ){
WC()->cart->set_quantity( $cart_key, $numberof_people);
WC()->cart->calculate_totals();
woocommerce_cart_totals();
}
wp_die();
}
add_action( 'wp_ajax_nopriv_update_order_review', 'anp_update_order_review' );
add_action( 'wp_ajax_update_order_review', 'anp_update_order_review' );

I've resolved the issue and managed to update the cart quantity and its price without an ajax call.
My functions.php file only includes a custom add_quantity.js file
function customjs_script_add_quanity_js() {
wp_enqueue_script( 'checkout_script', get_stylesheet_directory_uri() . '/assets/js/add_quantity.js', array('jquery') );
wp_localize_script( 'checkout_script', 'add_quantity', array( 'ajax_url' => admin_url( 'admin-ajax.php' ) ) );
}
add_action( 'wp_enqueue_scripts', 'customjs_script_add_quanity_js' );
Above enqueue scripts Code goes in function.php file of your active child theme (or active theme).
updated add_quantity.js file
jQuery(function() {
jQuery( "form.checkout" ).on( "change", ".woocommerce-additional-fields select#_number_of_travlers_", function( ) {
var number_of_people = parseInt(jQuery(this).val());
if(number_of_people){
jQuery( 'input.qty' ).attr('value', number_of_people);
}else{
jQuery( 'input.qty' ).attr('value', 1);
}
jQuery(".cart [name='update_cart']").removeAttr('disabled').attr('aria-disabled','false').trigger("click");
});
});
Tested and works perfectly!

Related

Stripe payments are not working if I load Woocommerce checkout via AJAX

I'm trying to load Woocommerce checkout form via Ajax on a custom landing page so that it can have an instant checkout for the visitor.
I'm using the following code:
PHP function for AJAX:
add_action( 'wp_ajax_getCheckoutPageContent', 'getCheckoutPageContentCallBack' );
add_action( 'wp_ajax_nopriv_getCheckoutPageContent', 'getCheckoutPageContentCallBack' );
function getCheckoutPageContentCallBack() {
$product_id = absint( $_POST['product_id'] );
$quantity = absint( $_POST['quantity'] );
$product_status = get_post_status( $product_id );
$passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity );
if ( WC()->cart->add_to_cart( $product_id, $quantity ) ) {
do_action( 'woocommerce_ajax_added_to_cart', $product_id );
global $woocommerce;
$items = $woocommerce->cart->get_cart();
wc_setcookie( 'woocommerce_items_in_cart', count( $items ) );
wc_setcookie( 'woocommerce_cart_hash', md5( json_encode( $items ) ) );
do_action( 'woocommerce_set_cart_cookies', true );
define( 'WOOCOMMERCE_CHECKOUT', true );
echo do_shortcode('[woocommerce_checkout]');
}else{
define( 'WOOCOMMERCE_CHECKOUT', true );
echo do_shortcode('[woocommerce_checkout]');
}
die();
}
Javascrip code:
var wp_ajax_url= myAjax.ajaxurl;
var data = {
action: 'getCheckoutPageContent',
product_id: $('#land_prod_id').val(),
quantity: 1
};
jQuery.post( wp_ajax_url, data, function(content) {
jQuery("#buy_form_location").html(content);
});
} else{
All payment methods work except for Stripe where I get redirected to the actual checkout page, with the following error: Payment processing failed. Please retry.
On the regular checkout page the payment works, but I'd like it to work on the one loaded via AJAX too.
I'm using this plugin: woocommerce.com/products/stripe

Ajax in checkout + select2 - how to fix the address field problematic update?

For checkout I use one script to show and hide billing fields depending on the shipping way.
Earlier it worked fine, but that time - I think because of using select2 in the city field instead of the text input - the address text field is making issues.
When user writes their address not so fast, the form is starting to update too quick, can delete the new written after that short pause characters or not delete, randomly, and when I want to delete something - the select 2 can appear on the top of the page and not closing.
add_filter( 'woocommerce_update_order_review_fragments', 'awoohc_add_update_form_billing', 99 );
function awoohc_add_update_form_billing( $fragments ) {
$checkout = WC()->checkout();
ob_start();
?>
<div class="woocommerce-billing-fields__field-wrapper">
<?php
$fields = $checkout->get_checkout_fields( 'billing' );
foreach ( $fields as $key => $field ) {
if ( isset( $field['country_field'], $fields[ $field['country_field'] ] ) ) {
$field['country'] = $checkout->get_value( $field['country_field'] );
}
woocommerce_form_field( $key, $field, $checkout->get_value( $key ) );
}
?>
</div>
<?php
$art_add_update_form_billing = ob_get_clean();
$fragments['.woocommerce-billing-fields'] = $art_add_update_form_billing;
return $fragments;
}
add_filter( 'woocommerce_checkout_fields' , 'override_billing_checkout_fields', 20, 1 );
function override_billing_checkout_fields( $fields ) {
$chosen_methods = WC()->session->get( 'chosen_shipping_methods' );
if ( 'local_pickup:1' === $chosen_methods[0] ) {
unset( $fields['billing']['billing_address_1'] );
unset( $fields['billing']['billing_city'] );
unset( $fields['billing']['billing_state'] );
}
return $fields;
}
add_filter( 'woocommerce_checkout_fields' , 'b_override_billing_checkout_fields', 20, 1 );
function b_override_billing_checkout_fields( $fields ) {
$chosen_methods = WC()->session->get( 'chosen_shipping_methods' );
if ( 'boxberry_self:4' === $chosen_methods[0] || 'boxberry_self_after:6' === $chosen_methods[0] ) {
unset( $fields['billing']['billing_address_1'] );
}
return $fields;
}
// Just hide woocommerce billing country
add_action( 'woocommerce_before_checkout_form', 'hide_checkout_billing_country', 5 );
function hide_checkout_billing_country() {
echo '<style>#billing_country_field{display:none;}</style>';
}
add_filter('woocommerce_billing_fields', 'customize_checkout_fields', 100 );
function customize_checkout_fields( $fields ) {
if ( is_checkout() ) {
// HERE set the required key fields below
$chosen_fields = array( 'postcode', 'country', 'company','last_name', 'address_2');
foreach( $chosen_fields as $key ) {
if( isset($fields['billing_'.$key]) && $key !== 'country') {
unset($fields['billing_'.$key]); // Remove all define fields except country
}
}
}
return $fields;
}
/*
* Updating the form
*/
add_action( 'wp_footer', 'awoohc_add_script_update_shipping_method' );
function awoohc_add_script_update_shipping_method() {
if ( is_checkout() ) {
?>
<script>
jQuery(document).ready(function ($) {
$(document.body).on('updated_checkout updated_shipping_method', function (event, xhr, data) {
$('input[name^="shipping_method"]').on('change', function () {
$('.woocommerce-billing-fields__field-wrapper').block({
message: null,
overlayCSS: {
background: '#fff',
'z-index': 1000000,
opacity: 0.3
}
});
$('select#billing_city').select2();
});
var first_name = $('#billing_first_name').val(),
phone = $('#billing_phone').val(),
email = $('#billing_email').val(),
city = $('#billing_city').val(),
address_1 = $('#billing_address_1').val(),
$(".woocommerce-billing-fields__field-wrapper").html(xhr.fragments[".woocommerce-billing-fields"]);
$(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_first_name"]').val(first_name);
$(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_phone"]').val(phone);
$(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_email"]').val(email);
$(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_city"]').val(city);
$('select#billing_city').select2();
$(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_address_1"]').val(address_1);
$('.woocommerce-billing-fields__field-wrapper').unblock();
});
});
</script>
<?php
}
}
Yes, I needed to use select2 twice, in other ways it didn't work as needed. But when even I use it once, it didn't help me with that issue. I can't disable select2, the client needs it, and needs to fix changing the address.
How I replaced the city input to select
add_filter( 'woocommerce_checkout_fields' , 'override_checkout_city_fields' );
function override_checkout_city_fields( $fields ) {
// Define here in the array your desired cities (Here an example of cities)
$option_cities = array(
'city1' => 'city1',
'city2' => 'city2',
);
$fields['billing']['billing_city']['type'] = 'select';
$fields['billing']['billing_city']['options'] = $option_cities;
$fields['shipping']['shipping_city']['type'] = 'select';
$fields['shipping']['shipping_city']['options'] = $option_cities;
return $fields;
}
Or maybe it's a delivery service plugin. Maybe you see what I don't see.
I know that exist many plugins, but I need to find what's wrong manually.
I tried to /comment/ different parts of the script, so I think it's something with select, or with delicery service plugin if not it.

WP: Trying to update associative array in post_meta with AJAX call

I'm cycling through an associative array and adding each child array as a row in a table.
The last column in that table is a button in which I want to use to remove that array from the parent array in the database.
Here's my markup:
<?php
$response_cost = get_post_meta( 379, '_wc_booking_pricing', true );
?>
<div id="response-cost" class="hidden"><?php echo json_encode( $response_cost); ?></div>
<?php
$i = 0;
foreach ($response_cost as $response) { ?>
<tr id="array-<?php echo $i; ?>">
<!-- table construction -->
<td><button class="remove-array" data-id="<?php echo $i; ?>"><i class="fa fa-times"></i></button></td>
</tr>
<?php
$i++;
};
?>
Here's my jQuery that takes care of removing the child array:
(function($) {
$(document).ready (function () {
$(function(){
var arrString= $('#response-cost').text();
const arr = JSON.parse(arrString);
$('.remove-array').click(function(){
var val = $(this).attr("data-id");
arr.splice(val, 1);
var arr_stringify = JSON.stringify(arr);
$.ajax({
url: ajax_object.ajaxurl,
type: 'POST',
data:{
action: 'update_cost_rule_table_action',
stringified_arr: arr_stringify,
},
success: function( data ){
console.log( data );
}
});
});
});
});
}) (jQuery);
Here's the function in my function.php file:
add_action( 'wp_ajax_update_cost_rule_table_action', 'update_cost_rule_table' );
add_action( 'wp_ajax_nopriv_update_cost_rule_table_action', 'update_cost_rule_table' );
function update_cost_rule_table(){
$response_cost = json_decode($_POST['stringified_arr']);
update_field('_wc_booking_pricing', $response_cost, 379);
wp_die();
}
And here's how I'm enqueuing the script:
function gt21_scripts() {
wp_register_script( 'cost-rule', get_template_directory_uri() . '/js/cost-rule.js', array( 'jquery' ) );
wp_enqueue_script( 'cost-rule' );
wp_localize_script( 'cost-rule', 'ajax_object', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ) ) );
};
add_action( 'wp_enqueue_scripts', 'gt21_scripts' );
When I click the button on the front end to remove the corresponding child array, It's currently emptying the whole cell in the dB and therefore removing the row altogether so my table returns empty.

Updating Shipping Rate on checkout page on WooCommerce site

We are having a custom code to allow end user select city, date and time of his delivery. The delivery rate should change according to the city. City select box is on the checkout page.
Changing the city triggers ajax that change successfully the charge for the delivery that displayed under order review section. So far so good.
Unfortunately, the price does not reach the order after submitting the order. It gets lost somewhere.
A hint may be in the fact that the same city select box is on the product page as well, and if a user does select it when adding to cart, and just then goes to checkout, the delivery rate is displayed and does not got lost when submitting the order.
Is there any refresh needs to be triggered in order for the shipping rate to be updated?
The function that updates the rates (successfully)
function adjust_shipping_rate( $rates ){
global $woocommerce;
foreach ($rates as $rate) {
$cost = $rate->cost;
$rate->cost = $_COOKIE['shipping_city_cost'];
}
return $rates;
}
add_filter( 'woocommerce_package_rates', 'adjust_shipping_rate', 50, 1 );
UPDATE 21-05-2017
This is how the cookie is updated: Once the select box is changed, async ajax call is fired and execute the following PHP code.
function get_and_set_shipping_rate(){
$shipping_city = $_POST['city'];
$shipping_cost = get_shipping_cost_by_city($shipping_city);
setcookie('shipping_city_cost', $shipping_cost, time() + (86400 * 30), '/');
$_COOKIE['shipping_city_cost'] = $shipping_cost;
echo 'Shipping cost updated: '.$shipping_city.' : '.$shipping_cost;
}
add_action( 'wp_ajax_get_and_set_shipping_rate', 'get_and_set_shipping_rate' );
add_action( 'wp_ajax_nopriv_get_and_set_shipping_rate', 'get_and_set_shipping_rate' );
Here is the ajax call:
jQuery(document).on('change', '#shipping_delivery_city', function(){
var requested_city = jQuery(this).val();
var data = {
'action': 'get_and_set_shipping_rate',
'city': requested_city
};
jQuery.ajax({
type: "POST",
url: shipping_dates.ajax_url,
data: data,
async: false,
success: function (response) {
console.log(response);
}
});
});
With this, I have two assumptions. 1. ) You are using this code on checkout page. 2. ) You already have your own function get_shipping_cost_by_city
add_action( 'woocommerce_checkout_update_order_review', 'woocommerce_checkout_update_order_review' );
function woocommerce_checkout_update_order_review( $post_data ){
$data = array();
$vars = explode('&', $post_data);
foreach ($vars as $k => $value){
$v = explode('=', urldecode($value));
$data[$v[0]] = $v[1];
}
$shipping_cost = get_shipping_cost_by_city( $data['billing_city'] );
WC()->session->set( 'shipping_city_cost', $shipping_cost );
foreach ( WC()->cart->get_shipping_packages() as $package_key => $package ) {
// this is needed for us to remove the session set for the shipping cost. Without this, we can't set it on the checkout page.
WC()->session->set( 'shipping_for_package_' . $package_key, false );
}
}
add_filter( 'woocommerce_package_rates', 'adjust_shipping_rate', 50 );
function adjust_shipping_rate( $rates ){
foreach ($rates as $rate) {
$cost = $rate->cost;
$rate->cost = WC()->session->get( 'shipping_city_cost' );
}
return $rates;
}
the setting/getting of shipping_city_cost is done by WC()->session->set and WC()->session->get. I need not to put an ajax function for woocommerce_checkout_update_order_review is an action hook inside an ajax call whenever an update is done on the checkout page.
for this test, I use this function:
function get_shipping_cost_by_city( $city ) {
if ( $city == 'Ormoc City' ){
return 100;
}
return 130;
}

Remove product in the cart using ajax in woocommerce

I would like to remove the product in the woocommerce cart using ajax without click the link.
If you have encounter this kind of functionality, please help us.
add_action( 'wp_footer', 'add_js_to_wp_wcommerce');
function add_js_to_wp_wcommerce(){ ?>
<script type="text/javascript">
jQuery('.remove-product').click(function(){
var product_id = jQuery(this).attr("data-product_id");
jQuery.ajax({
type: 'POST',
dataType: 'json',
url: "/wp-admin/admin-ajax.php",
data: { action: "product_remove",
product_id: product_id
},success: function(data){
console.log(data);
}
});
return false;
});
</script>
<?php }
add_action( 'wp_ajax_product_remove', 'product_remove' );
add_action( 'wp_ajax_nopriv_product_remove', 'product_remove' );
function product_remove() {
global $wpdb, $woocommerce;
session_start();
foreach ($woocommerce->cart->get_cart() as $cart_item_key => $cart_item){
if($cart_item['product_id'] == $_POST['product_id'] ){
// Remove product in the cart using cart_item_key.
$woocommerce->cart->get_remove_url($cart_item_key);
}
}
print_r($woocommerce->cart->get_cart());
//echo json_encode(array('status' => 0));
exit();
}
you could use the WC_Cart set_quantity method
And do as this in your php:
$cart = WC()->instance()->cart;
$id = $_POST['product_id'];
$cart_id = $cart->generate_cart_id($id);
$cart_item_id = $cart->find_product_in_cart($cart_id);
if($cart_item_id){
$cart->set_quantity($cart_item_id,0);
}
use this :
$cart = $woocommerce->cart;
foreach ($woocommerce->cart->get_cart() as $cart_item_key => $cart_item){
if($cart_item['product_id'] == $_POST['product_id'] ){
// Remove product in the cart using cart_item_key.
$cart->remove_cart_item($cart_item_key);
}
}
Try this one :
foreach ( $woocommerce->cart->cart_contents as $cart_item_key => $cart_item ) {
if($cart_item['product_id'] == $product_id){
unset($cartdetails->cart_contents[$cart_item_key]);
}
}

Resources