I am trying to add a product from an on_click event at checkout.
That product's price is calculated dynamically with an external API so it is registered as having a price of 0 in woocommerce itself. I am trying to add this product to the cart (working) and then set that product's price to the one I receive from the API (not working). Here is the relevant code:
AJAX call on click:
jQuery('#theftdaminsurance').on('click', function(){
jQuery.ajax({
type: "POST",
url: "/wp-admin/admin-ajax.php",
data: {
action: 'add_product_to_cart_checkout',
// add your parameters here
price: <?php echo $year_dam_price; ?>,
productid: ins_product_id
},
success: function (output) {
document.getElementById("loaderdiv").style.display = "none";
jQuery(document.body).trigger("update_checkout");
}
});
});
I checked everything here and the call is made properly with all the right info being passed on.
Here is the function being called:
add_action('wp_ajax_add_product_to_cart_checkout', 'add_product_to_cart_checkout');
// register the ajax action for unauthenticated users
add_action('wp_ajax_nopriv_add_product_to_cart_checkout', 'add_product_to_cart_checkout');
function add_product_to_cart_checkout() {
$product_id = $_REQUEST['productid'];
$price = floatval($_REQUEST['price']);
$ebike_ids = array(17386,17385,17382,17378,17375,17372,17370,17369,17364,16132,16130,4561,4550,3490,2376);
$found = false;
//check if there is something in cart
if ( sizeof( WC()->cart->get_cart() ) > 0 ) {
//Delete all preexisting insurance products and find qty of ebikes
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$product_idebike = $cart_item['product_id'];
if ( ($cart_item['product_id'] == "16600") || ($cart_item['product_id'] == "16653") || ($cart_item['product_id'] == "16654") || ($cart_item['product_id'] == "16655") ||($cart_item['product_id'] == "16659") || ($cart_item['product_id'] == "16660")) {
WC()->cart->remove_cart_item( $cart_item_key );
}
//get ebike quantity
for ($x = 0; $x <= 14; $x++) {
if ($product_idebike == $ebike_ids[$x]) {
$quantity = $cart_item['quantity'];
break;
}
}
}
// if command is not to remove add relevant products
if ($price != "-1") {
WC()->cart->add_to_cart( $product_id, $quantity );
foreach( WC()->cart->get_cart() as $cart_item ){
$id = $cart_item['product_id'];
if ($product_id == $id) {
$cart_item['data']->set_price( $price );
}
}
}
}
}
Here I basically debugged everythingcarefully and everything behaves as it should but the set_price function. I cannot get it to update the product's price to the one passed in the AJAX call. The price remains 0 once the checkout is updated and before it is as well. Am i calling or using this function the wrong way?
Thanks a lot for your help!
It seems that you are making things much more complicated than they should be on your Ajax receiver function…
Note that you can add custom cart item data when using WC_Cart add_to_cart() method, so you will add your insurance price as custom cart item data and afterwards use it to set the price.
Here is your revisited Ajax PHP receiver function:
add_action('wp_ajax_add_product_to_cart_checkout', 'add_product_to_cart_checkout');
add_action('wp_ajax_nopriv_add_product_to_cart_checkout', 'add_product_to_cart_checkout');
function add_product_to_cart_checkout() {
if ( isset($_POST['productid']) && isset($_POST['price']) && ! WC()->cart->is_empty() ) {
$product_id = intval($_POST['productid']);
$price = floatval($_POST['price']);
$ebike_ids = array(17386,17385,17382,17378,17375,17372,17370,17369,17364,16132,16130,4561,4550,3490,2376);
$insurance_ids = array(16600,16653,16654,16655,16659,16660);
$found = false;
$ebike_quantity = 0;
// Loop through cart items
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
// Delete all preexisting insurance products
if ( in_array( $cart_item['product_id'], $insurance_ids ) ) {
WC()->cart->remove_cart_item( $cart_item_key );
}
// Get ebikes total quantity
if ( in_array( $cart_item['product_id'], $ebike_ids ) ) {
$ebike_quantity += $cart_item['quantity'];
}
}
// If command is not to remove add insurance product with price as custom cart item data
if ( $price >= 0 && $cart_item['insurance_price'] > 0 ) {
WC()->cart->add_to_cart( $product_id, $ebike_quantity, 0, array(), array( 'insurance_price' => $price ) );
}
die();
}
}
Then to set the price you will use:
// Set insurance price from custom cart item data
add_action( 'woocommerce_before_calculate_totals', 'set_insurance_price' );
function set_insurance_price( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
foreach ( $cart->get_cart() as $cart_item ) {
if( isset($cart_item['insurance_price']) && $cart_item['insurance_price'] > 0 ) {
$cart_item['data']->set_price( $cart_item['insurance_price'] );
}
}
}
Now It should works.
Related
I have a working script that adds a fee for certain products in an array.
But it only adds the fee for the first product in the array.
I have tried different options with my knowledge but it doesn't work. Any advice on what i'm doing wrong?
This is the code:
/* Add fee to specific product*/
add_action('woocommerce_cart_calculate_fees', 'statie_geld');
function statie_geld() {
if (is_admin() && !defined('DOING_AJAX')) {return;}
foreach( WC()->cart->get_cart() as $item_keys => $item ) {
$quantiy = $item['quantity']; //get quantity from cart
if( in_array( $item['product_id'], statiegeld_ids() )) {
WC()->cart->add_fee(__('Statiegeld Petfles 24'), 3.60 * $quantiy );
}
}
}
function statiegeld_ids() {
return array( 4535, 4537, 89694, 89706, 3223, 4742, 14846, 26972, 32925, 32927, 32929, 37475 );
}
Your code contains some mistakes
No need to use WC()->cart, $cart is passed to the function
$quantiy is overwritten on each loop
Same for adding the fee, this is overwritten on each loop
So you get:
function action_woocommerce_cart_calculate_fees( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;
// Initialize
$quantity = 0;
// Loop though each cart item
foreach ( $cart->get_cart() as $cart_item ) {
// Compare
if ( in_array( $cart_item['product_id'], statiegeld_ids() ) ) {
// Addition
// Get product quantity in cart
$quantity += $cart_item['quantity'];
}
}
// Greater than
if ( $quantity > 0 ) {
// Add fee
$cart->add_fee( __( 'Statiegeld Petfles 24', 'woocommerce' ), 3.60 * $quantity );
}
}
add_action( 'woocommerce_cart_calculate_fees', 'action_woocommerce_cart_calculate_fees', 10, 1 );
// Specify the product IDs
function statiegeld_ids() {
return array( 4535, 4537, 89694, 89706, 3223, 4742, 14846, 26972, 32925, 32927, 32929, 37475 );
}
I want to add percentage value to cart item prices based on the selected payment gateway.
The problem I am facing is variation product price is not updating for the product price. Initially selected price is showing all the time.
How can I get the changed price accordingly?
My code so far:
// Set custom cart item price
function add_custom_price( $cart ) {
// This is necessary for WC 3.0+
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// Avoiding hook repetition (when using price calculations for example | optional)
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Loop through cart items
foreach ( $cart->get_cart() as $cart_item ) {
$chosen_payment_method = WC()->session->get('chosen_payment_method');
if($chosen_payment_method == 'cod') {
$increaseby = 3;
} elseif($chosen_payment_method == 'paypal') {
$increaseby = 8;
} else {
$increaseby = 10;
}
$price = get_post_meta($cart_item['product_id'] , '_price', true);
$price = $price + (($price * $increaseby)/100);
$cart_item['data']->set_price( $price );
}
}
add_action( 'woocommerce_before_calculate_totals', 'add_custom_price', 1000, 1);
Any help highly appreciated.
There are some mistakes in your code
Use WC()->session->get( 'chosen_payment_method' ); outside the foreach loop
get_post_meta() is not needed to get the price, you can use get_price()
You will also need jQuery that is triggered when changing the payment method.
So you get:
function action_woocommerce_before_calculate_totals( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Get payment method
$chosen_payment_method = WC()->session->get( 'chosen_payment_method' );
// Compare
if ( $chosen_payment_method == 'cod' ) {
$increaseby = 3;
} elseif ( $chosen_payment_method == 'paypal' ) {
$increaseby = 8;
} else {
$increaseby = 10;
}
// Loop through cart items
foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
// Get price
$price = $cart_item['data']->get_price();
// Set price
$cart_item['data']->set_price( $price + ( $price * $increaseby ) / 100 );
}
}
add_action( 'woocommerce_before_calculate_totals', 'action_woocommerce_before_calculate_totals', 10, 1 );
function action_wp_footer() {
if ( is_checkout() && ! is_wc_endpoint_url() ) :
?>
<script type="text/javascript">
jQuery(function($){
$( 'form.checkout' ).on( 'change', 'input[name="payment_method"]', function() {
$(document.body).trigger( 'update_checkout' );
});
});
</script>
<?php
endif;
}
add_action( 'wp_footer', 'action_wp_footer' );
Hello this is similar question to the link below but just want to ask if it's possible to set a condition where it won't load the free product if the product that's been purchase is a subscription type? Thank you.
Add or remove automatically a free product in Woocommerce cart
/**
* Add another product depending on the cart total
*/
add_action( 'template_redirect', 'add_product_to_cart' );
function add_product_to_cart() {
if ( ! is_admin() ) {
global $woocommerce;
$product_id = 85942; //replace with your product id
$found = false;
$cart_total = 15; //replace with your cart total needed to add above item
if( $woocommerce->cart->total >= $cart_total ) {
//check if product already in cart
if ( sizeof( $woocommerce->cart->get_cart() ) > 0 ) {
$isVirtualOnly = false;
foreach ( $woocommerce->cart->get_cart() as $cart_item_key => $values) {
$_product = $values[‘data’];
if ($_product != null)
if ($_product->get_type() != $_virtual)
$isVirtualOnly = false;
}
if ($isVirtualOnly != true) {
foreach ( $woocommerce->cart->get_cart() as $cart_item_key => $values ) {
$_product = $values['data'];
if ( $_product->get_id() == $product_id )
$found = true;
}
// if product not found, add it
if ( ! $found )
$woocommerce->cart->add_to_cart( $product_id );
}
} else {
// if no products in cart, add it
$woocommerce->cart->add_to_cart( $product_id );
}
}
}
}
/**
* END Add another product depending on the cart total
*/
add_action( 'template_redirect', 'add_product_to_cart_conditionally' );
function add_product_to_cart_conditionally() {
if ( is_admin() ) return; // Exit
// Below define the product Id to be added:
$product_A = 37; // <== For new customers that have not purchased a product before (and guests)
$product_B = 53; // <== For confirmed customers that have purchased a product before
$product_id = has_bought() ? $product_B : $product_A;
// If cart is empty
if( WC()->cart->is_empty() ) {
WC()->cart->add_to_cart( $product_id ); // Add the product
}
// If cart is not empty
else {
// Loop through cart items (check cart items)
foreach ( WC()->cart->get_cart() as $item ) {
// Check if the product is already in cart
if ( $item['product_id'] == $product_id ) {
return; // Exit if the product is in cart
}
}
// The product is not in cart: We add it
WC()->cart->add_to_cart( $product_id );
}
}
So I can use this snippet from the woocommerce docs and it works to add a free product when a cart total is reached. However, our customers need to be able to remove the product from the cart if they don't want it (repeat customers that don't want the same gift every time). With this snippet, the remove from cart button doesn't work for the free product.
/**
* Add another product depending on the cart total
*/
add_action( 'template_redirect', 'add_product_to_cart' );
function add_product_to_cart() {
if ( ! is_admin() ) {
global $woocommerce;
$product_id = 2831; //replace with your product id
$found = false;
$cart_total = 30; //replace with your cart total needed to add above item
if( $woocommerce->cart->total >= $cart_total ) {
//check if product already in cart
if ( sizeof( $woocommerce->cart->get_cart() ) > 0 ) {
foreach ( $woocommerce->cart->get_cart() as $cart_item_key => $values ) {
$_product = $values['data'];
if ( $_product->get_id() == $product_id )
$found = true;
}
// if product not found, add it
if ( ! $found )
$woocommerce->cart->add_to_cart( $product_id );
} else {
// if no products in cart, add it
$woocommerce->cart->add_to_cart( $product_id );
}
}
}
}
``
code above is from here: https://docs.woocommerce.com/document/automatically-add-product-to-cart-on-visit/
Removing the product from the cart will actually work, but the problem is that the code will also automatically re-add the product again.
One way to solve this would be to remember that the product has already been added once, and to not re-add the product if the product would be added another time (after it has been removed from the cart for example).
A simple way to do that would be to save that data inside of the WooCommerce session, using WC()->session. Modified code below:
add_action( 'template_redirect', 'add_product_to_cart' );
function add_product_to_cart() {
if ( ! is_admin() ) {
global $woocommerce;
$product_id = 2831; //replace with your product id
$found = false;
$cart_total = 10; //replace with your cart total needed to add above item
if( $woocommerce->cart->total >= $cart_total && ! WC()->session->get( 'free_item_given', false ) ) { // <--- ADDED CHECK FOR free_item_given HERE
//check if product already in cart
if ( sizeof( $woocommerce->cart->get_cart() ) > 0 ) {
foreach ( $woocommerce->cart->get_cart() as $cart_item_key => $values ) {
$_product = $values['data'];
if ( $_product->get_id() == $product_id )
$found = true;
}
// if product not found, add it
if ( ! $found ) {
$woocommerce->cart->add_to_cart( $product_id );
WC()->session->set( 'free_item_given', true ); // <--- REMEMBER free_item_given HERE
}
} else {
// if no products in cart, add it
$woocommerce->cart->add_to_cart( $product_id );
WC()->session->set( 'free_item_given', true ); // <--- REMEMBER free_item_given HERE
}
}
}
}
I hope that it solves your problem!
I am trying display if a variation of a product is already in a cart or not (in single product page). A simple comparing of the product id with products in cart object is not working for variable product as the variation id is being loaded using ajax.
Here is my code that works in case the product type is other than variable.
<?php
/*
* Check if Product Already In Cart
*/
function woo_in_cart( $product_id ) {
global $woocommerce;
if ( !isset($product_id) ) {
return false;
}
foreach( $woocommerce->cart->get_cart() as $cart_item ) {
if ( $cart_item['product_id'] === $product_id ){
return true;
} else {
return false;
}
}
}
Is there any way to make it work without jQuery?
Do you mean $product_id could be the ID of a variation? If so, you can just get the parent ID if it exists:
/*
* Check if Product Already In Cart
*/
function woo_in_cart( $product_id ) {
global $woocommerce;
if ( ! isset( $product_id ) ) {
return false;
}
$parent_id = wp_get_post_parent_id( $product_id );
$product_id = $parent_id > 0 ? $parent_id : $product_id;
foreach ( $woocommerce->cart->get_cart() as $cart_item ) {
if ( $cart_item['product_id'] === $product_id ) {
return true;
} else {
return false;
}
}
}
If you mean your cart item is a variation, and $product_id is already the parent product ID, then your code should work already as is.
The $cart_item has 2 IDs: $cart_item['product_id'] and $cart_item['variation_id'].
So product_id will always be that of the parent product.
To handle product variations outside single product pages (and simple products everywhere):
// Check if Product Already In Cart (Work with product variations too)
function woo_in_cart( $product_id = 0 ) {
$found = false;
if ( isset($product_id) || 0 == $product_id )
return $found;
foreach( WC()->cart->get_cart() as $cart_item ) {
if ( $cart_item['data']->get_id() == $product_id )
$found = true;
}
return $found;
}
To handle product variations inside single product pages, javascript is needed.
Here is an example that will show a custom message, when the selected variation is already in cart:
// Frontend: custom select field in variable products single pages
add_action( 'wp_footer', 'action_before_add_to_cart_button' );
function action_before_add_to_cart_button() {
if( ! is_product() ) return;
global $product;
if( ! is_object($product) )
$product = wc_get_product( get_the_id() );
// Only for variable products when cart is not empty
if( ! ( $product->is_type('variable') && ! WC()->cart->is_empty() ) ) return; // Exit
$variation_ids_in_cart = array();
// Loop through cart items
foreach( WC()->cart->get_cart() as $cart_item ) {
// Collecting product variation IDs if they are in cart for this variable product
if ( $cart_item['variation_id'] > 0 && in_array( $cart_item['variation_id'], $product->get_children() ) )
$variation_ids_in_cart[] = $cart_item['variation_id'];
}
// Only if a variation ID for this variable product is in cart
if( sizeof($variation_ids_in_cart) == 0 ) return; // Exit
// Message to be displayed (if the selected variation match with a variation in cart
$message = __("my custom message goes here", "woocommerce");
$message = '<p class="custom woocommerce-message" style="display:none;">'.$message.'</p>';
// jQuery code
?>
<script>
(function($){
// Utility function that check if variation match and display message
function checkVariations(){
var a = 'p.woocommerce-message.custom', b = false;
$.each( <?php echo json_encode($variation_ids_in_cart); ?>, function( k, v ){
if( $('input[name="variation_id"]').val() == v ) b = true;
});
if(b) $(a).show(); else $(a).hide();
}
// On load (when DOM is rendered)
$('table.variations').after('<?php echo $message; ?>');
setTimeout(function(){
checkVariations();
}, 800);
// On live event: product attribute select fields "blur" event
$('.variations select').blur( function(){
checkVariations();
});
})(jQuery);
</script>
<?php
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.