I have different product categories on my store. I have a "nettoyage" category that i'd like to allow clients to order ONLY if the number of "nettoyage" items in the cart is > 3 .
Right now, i've set up the default quantity to 3 and don't allow the user to go below on product pages. But what i'd like is to allow them to add 1 of each for example and allow them to order as long as the number of "nettoyage" products in the cart is > 3. How can i refactor this code to do this ?
Right now, i have the following code in my functions.php :
add_filter('woocommerce_quantity_input_args', 'bloomer_woocommerce_quantity_changes', 10, 2);
function bloomer_woocommerce_quantity_changes($args, $product)
{
if (!is_cart()) {
if (is_singular('product') && (has_term('nettoyage', 'product_cat'))) {
$args['input_value'] = 3; // Start from this value (default = 1)
$args['max_value'] = 10; // Max quantity (default = -1)
$args['min_value'] = 3; // Min quantity (default = 0)
$args['step'] = 1; // Increment/decrement by this value (default = 1)
}
}
return $args;
}
add_filter('woocommerce_quantity_input_args', 'min_qty_filter_callback', 20, 2);
function min_qty_filter_callback($args, $product)
{
$categories = array('Noten'); // The targeted product category(ies)
$min_qty = 3; // The minimum product quantity
$product_id = $product->is_type('simple') ? $product->get_parent_id() : $product->get_id();
if (has_term($categories, 'product_cat', $product_id)) {
$args['min_value'] = $min_qty;
}
return $args;
}
// On shop and archives pages
add_filter('woocommerce_loop_add_to_cart_args', 'min_qty_loop_add_to_cart_args', 10, 2);
function min_qty_loop_add_to_cart_args($args, $product)
{
$categories = array('nettoyage'); // The targeted product category
$min_qty = 3; // The minimum product quantity
$product_id = $product->get_id();
if (has_term($categories, 'product_cat', $product_id)) {
$args['quantity'] = $min_qty;
}
return $args;
}
add_action('woocommerce_check_cart_items', 'wc_min_item_required_qty');
function wc_min_item_required_qty()
{
$categories = array('nettoyage'); // The targeted product category
$min_item_qty = 3; // Minimum Qty required (for each item)
$display_error = false; // Initializing
// Loop through cart items
foreach (WC()->cart->get_cart() as $cart_item_key) {
$item_quantity = $cart_item['quantity']; // Cart item quantity
$product_id = $cart_item['product_id']; // The product ID
// For cart items remaining to "Noten" producct category
if (has_term($categories, 'product_cat', $product_id) && $item_quantity < $min_item_qty) {
wc_clear_notices(); // Clear all other notices
// Add an error notice (and avoid checkout).
wc_add_notice(sprintf("Le service livraison nettoyage n'est valable qu'à partir de %s paires!", $min_item_qty, $item_quantity), 'error');
break; // Stop the loop
}
}
}
Finaly found the solution. For those interested, here is the custom function :
add_action('woocommerce_check_cart_items', 'custom_set_min_total');
function custom_set_min_total()
{
if (is_cart() || is_checkout()) {
global $woocommerce, $product;
$i = 0;
foreach ($woocommerce->cart->cart_contents as $product) :
$minimum_cart_product_total = 3;
if (has_term('nettoyage', 'product_cat', $product['product_id'])) :
$total_quantity += $product['quantity'];
endif;
endforeach;
foreach ($woocommerce->cart->cart_contents as $product) :
if (has_term('nettoyage', 'product_cat', $product['product_id'])) :
if ($total_quantity < $minimum_cart_product_total && $i == 0) {
wc_add_notice(
sprintf(
'<strong>A Minimum of %s products is required from the nettoyage category before checking out.</strong>'
. '<br />Current number of items in the cart: %s.',
$minimum_cart_product_total,
$total_quantity
),
'error'
);
}
$i++;
endif;
endforeach;
}
}
Related
This is the code that I have on my products page template.
<p>Your total: <span class="r_cart_total"><?= WC()->cart->get_cart_total(); ?></span></p>
So whenever a product is added to cart, I have a filter that works
add_filter( 'woocommerce_add_to_cart_fragments', 'wc_refresh_cart_fragments', 50, 1 );
function wc_refresh_cart_fragments( $fragments ){
$cart_count = WC()->cart->get_cart_contents_count();
$cart_total = WC()->cart->get_cart_total();
// Normal version
$count_normal = '<span class="r_cart_total">' . $cart_total . '</span>';
$fragments['.r_cart_total'] = $count_normal;
return $fragments;
}
According to this function I am updating the span content with class '.r_cart_total'.
This function works fine when an item is removed from the cart, but whenever I add a product to cart the fragments do not get updated.
Here is the add to cart function
/**
* Add to cart product using ajax
**/
add_action('wp_ajax_woocommerce_ajax_add_to_cart', 'woocommerce_ajax_add_to_cart');
add_action('wp_ajax_nopriv_woocommerce_ajax_add_to_cart', 'woocommerce_ajax_add_to_cart');
function woocommerce_ajax_add_to_cart() {
$product_id = apply_filters('woocommerce_add_to_cart_product_id', absint($_POST['product_id']));
$quantity = empty($_POST['quantity']) ? 1 : wc_stock_amount($_POST['quantity']);
$variation_id = absint($_POST['variation_id']);
$passed_validation = apply_filters('woocommerce_add_to_cart_validation', true, $product_id, $quantity);
$product_status = get_post_status($product_id);
$is_buy_all = filter_var($_POST['is_buy_all'], FILTER_VALIDATE_BOOLEAN);
//if buy all product then remove all other products in the cart
if($is_buy_all){
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
WC()->cart->remove_cart_item( $cart_item_key );
}
}
if ($passed_validation && WC()->cart->add_to_cart($product_id, $quantity, $variation_id) && 'publish' === $product_status) {
do_action('woocommerce_ajax_added_to_cart', $product_id);
//show success message add to cart
if ('yes' === get_option('woocommerce_cart_redirect_after_add')) {
wc_add_to_cart_message(array($product_id => $quantity), true);
}
if($is_buy_all && !empty($_POST['sub_products'])){
$sub_products = json_decode(stripslashes($_POST['sub_products']));
foreach($sub_products as $sub_product){
$sub_product = trim($sub_product);
$sub_product_id = apply_filters('woocommerce_add_to_cart_product_id', absint($sub_product));
$quantity = 1;
$variation_id = absint($variation_id);
$passed_validation = apply_filters('woocommerce_add_to_cart_validation', true, $sub_product_id, $quantity);
$sub_product_status = get_post_status($sub_product_id);
if($passed_validation && WC()->cart->add_to_cart($sub_product_id, $quantity) && 'publish' === $sub_product_status) {
do_action('woocommerce_ajax_added_to_cart', $sub_product_id);
}
}
}
WC_AJAX :: get_refreshed_fragments();
wp_die();
} else {
$data = array(
'error' => true,
'product_url' => apply_filters('woocommerce_cart_redirect_after_error', get_permalink($product_id), $product_id));
echo wp_send_json($data);
wp_die();
}
}
How can I change shipping cost in Woocommerce by Ajax request?
I tried this:
add_action('wp_ajax_set_shipping_price', 'set_shipping_price');
add_action('wp_ajax_nopriv_set_shipping_price', 'set_shipping_price');
function set_shipping_price(){
$packages = WC()->cart->get_shipping_packages();
foreach ($packages as $package_key => $package){
$session_key = 'shipping_for_package_'.$package_key;
$stored_rates = WC()->session->__unset( $session_key );
$WC_Shipping = new WC_Shipping();
$WC_Shipping->calculate_shipping_for_package( $package, $package_key = 0);
WC()->cart->calculate_shipping();
WC()->cart->calculate_totals();
}
wp_die();
}
and:
add_filter( 'woocommerce_package_rates', 'custom_shipping_costs', 20, 2 );
function custom_shipping_costs( $rates, $package ) {
if (isset($_POST['cost'])){
$new_cost = $_POST['cost'];
}
$new_cost = 0;
$tax_rate = 0.2;
foreach( $rates as $rate_key => $rate ){
if( $rate->method_id != 'free_shipping'){
$rates[$rate_key]->cost = $new_cost;
$taxes = array();
foreach ($rates[$rate_key]->taxes as $key => $tax){
if( $rates[$rate_key]->taxes[$key] > 0 )
$taxes[$key] = $new_cost * $tax_rate;
}
$rates[$rate_key]->taxes = $taxes;
}
}
return $rates;
}
The hook woocommerce_package_rates works at page load but do nothing by ajax. Help please.
Here is the explanation on how to get post data from an Ajax call.
Additionally, you can use the WooCommerce update_checkout event to update the checkout (and thus recalculate shipping costs) after a field in the checkout has changed or been clicked (instead of creating a custom Ajax call). Here you will find a complete list.
So, assuming you want to recalculate shipping costs when the field with id custom_shipping_price (for example) changes, you can use this script:
// updates the checkout (and shipping charge calculation) when the value of a field changes
add_action( 'wp_footer', 'update_checkout' );
function update_checkout() {
// only in the checkout
if ( ! is_checkout() ) {
return;
}
?>
<script type="text/javascript">
jQuery( document ).ready(function( $ ) {
// when the value of the field with id "custom_shipping_price" changes it updates the checkout
jQuery('#custom_shipping_price').change(function(){
jQuery('body').trigger('update_checkout');
});
});
</script>
<?php
}
Now you will need to use the woocommerce_package_rates hook to calculate the new shipping cost.
Make sure you initialize the value of the $new_cost variable in
case the custom field is not set or is empty.
Here the function:
// update the shipping cost based on a custom field in the checkout
add_filter( 'woocommerce_package_rates', 'update_shipping_cost_based_on_custom_field', 10, 2 );
function update_shipping_cost_based_on_custom_field( $rates, $package ) {
if ( ! $_POST || is_admin() || ! is_ajax() ) {
return;
}
// gets the post serialized data sent with the Ajax call
if ( isset( $_POST['post_data'] ) ) {
parse_str( $_POST['post_data'], $post_data );
} else {
$post_data = $_POST;
}
// set the cost of shipping (if the custom field should be empty)
$new_cost = 0;
// if the field is set it gets the value
if ( isset( $post_data['custom_shipping_price'] ) && ! empty( $post_data['custom_shipping_price'] ) ) {
// forces conversion of value into number
$new_cost = (float) str_replace( ',', '.', $post_data['custom_shipping_price'] );
}
// set the percentage of taxes (ex. 22%)
$tax_rate = 0.22;
foreach( $rates as $rate_key => $rate ) {
if ( 'free_shipping' !== $rate->method_id ) {
// set rate cost
$rates[$rate_key]->cost = $new_cost;
// set taxes rate cost (if enabled)
$taxes = array();
foreach ( $rates[$rate_key]->taxes as $key => $tax ) {
if ( $rates[$rate_key]->taxes[$key] > 0 ) {
$taxes[$key] = $new_cost * $tax_rate;
}
}
$rates[$rate_key]->taxes = $taxes;
}
}
return $rates;
}
Both codes have been tested and work. Add them to your active theme's functions.php.
Good afternoon!
Please help me implement this feature in woocommerce.
I have three specific products (with specific IDs 2, 3, 4).
I need to make sure that when adding two of them together to the cart, the prices for both of them change automatically. For example, for a product with id #2, the price was set to $ 100, and for a product with id #3, the price became$200.
Also, when they are all added together (three of them), the price changed for all three of them: product # 2 = 100; product # 3 = 200; product # 4 = 250.
I haven't found anything like this anywhere yet, so I would appreciate any hint.
I managed to solve the problem on my own.
I made sure that the products I need "watch" each other in the basket. And if one of them "notices" the other, the price is updated for both of them.
In the same way, the price is updated if the three of them simultaneously "see" each other in the basket.
It may not be the best code, but it works and it works well.
The original code was taken from this resource and modified by me for my tasks.
(https://www.webroomtech.com/change-product-price-when-other-product-is-in-cart-woocommerce/)
function simple_func_product_in_cart($product_id){
$product_cart_id = WC()->cart->generate_cart_id( $product_id );
$in_cart = WC()->cart->find_product_in_cart( $product_cart_id );
if ( $in_cart ) {
return true;
}
return false;
}
add_action( 'woocommerce_before_calculate_totals', 'simple_change_price_of_product' );
function simple_change_price_of_product( $cart_object ) {
$target_product_id = 7101; // CHANGE THIS WITH YOUR PRODUCT ID
if(simple_product_in_cart(7105)) {
// Product is already in cart
foreach ( $cart_object->get_cart() as $key => $value ) {
if ( $value['product_id'] == $target_product_id ) {
$value['data']->set_price(160); // CHANGE THIS: set the new price
$new_price = $value['data']->get_price();
}
}
}
$target_product_id = 7101; // CHANGE THIS WITH YOUR PRODUCT ID
if(simple_product_in_cart(7107)) {
// Product is already in cart
foreach ( $cart_object->get_cart() as $key => $value ) {
if ( $value['product_id'] == $target_product_id ) {
$value['data']->set_price(160); // CHANGE THIS: set the new price
$new_price = $value['data']->get_price();
}
}
}
$target_product_id = 7105; // CHANGE THIS WITH YOUR PRODUCT ID
if(simple_product_in_cart(7101)) {
// Product is already in cart
foreach ( $cart_object->get_cart() as $key => $value ) {
if ( $value['product_id'] == $target_product_id ) {
$value['data']->set_price(170); // CHANGE THIS: set the new price
$new_price = $value['data']->get_price();
}
}
}
$target_product_id = 7105; // CHANGE THIS WITH YOUR PRODUCT ID
if(simple_product_in_cart(7107)) {
// Product is already in cart
foreach ( $cart_object->get_cart() as $key => $value ) {
if ( $value['product_id'] == $target_product_id ) {
$value['data']->set_price(170); // CHANGE THIS: set the new price
$new_price = $value['data']->get_price();
}
}
}
$target_product_id = 7107; // CHANGE THIS WITH YOUR PRODUCT ID
if(simple_product_in_cart(7101)) {
// Product is already in cart
foreach ( $cart_object->get_cart() as $key => $value ) {
if ( $value['product_id'] == $target_product_id ) {
$value['data']->set_price(180); // CHANGE THIS: set the new price
$new_price = $value['data']->get_price();
}
}
}
$target_product_id = 7107; // CHANGE THIS WITH YOUR PRODUCT ID
if(simple_product_in_cart(7105)) {
// Product is already in cart
foreach ( $cart_object->get_cart() as $key => $value ) {
if ( $value['product_id'] == $target_product_id ) {
$value['data']->set_price(180); // CHANGE THIS: set the new price
$new_price = $value['data']->get_price();
}
}
}
}
I am building on a website using woocommerce.
For some products clients need to write the name of their child in the Child name field on the checkout page. (The site sells music lessons)
However for other products like giftcards I dont need this Child name field. I can't find any plugin that can show a different checkout page depending on the catagory of the product that the client is buying.
Anyone an idea for making this possible?
Thnx in advance!
I think I found a website with the answer:
https://wordimpress.com/create-conditional-checkout-fields-woocommerce/
I'm going to try this and place the outcome here.
*** okay couple of hours later!
It did the job, the codes in the website I posted are used for a single product ID. If you want to check for category ID you can change this code:
/**
* Check if Conditional Product is In cart
*
* #param $product_id
*
* #return bool
*/
function wordimpress_is_conditional_product_in_cart( $product_id ) {
//Check to see if user has product in cart
global $woocommerce;
//flag no book in cart
$book_in_cart = false;
foreach ( $woocommerce->cart->get_cart() as $cart_item_key => $values ) {
$_product = $values['data'];
if ( $_product->id === $product_id ) {
//book is in cart!
$book_in_cart = true;
}
}
return $book_in_cart;
}
for:
/**
* Check if Conditional Product is In cart
*
* #param $product_id
*
* #return bool
*/
function wordimpress_is_conditional_product_in_cart( $product_id ) {
//Check to see if user has product in cart
global $woocommerce;
//flag no book in cart
$book_in_cart = false;
foreach ( $woocommerce->cart->get_cart() as $cart_item_key => $values ) {
$_product = $values['data'];
$terms = get_the_terms( $_product->id, 'product_cat' );
foreach ($terms as $term) {
$_categoryid = $term->term_id;
}
if ( $_categoryid === 14 ) {
//book is in cart!
$book_in_cart = true;
}
}
return $book_in_cart;
}
If you need to check multiple categorie ID's or product ID's you can copy this excample:
/**
* Check if Conditional Product is In cart
*
* #param $product_id
*
* #return bool
*/
function wordimpress_is_conditional_product_in_cart( $product_id ) {
//Check to see if user has product in cart
global $woocommerce;
//flag no book in cart
$book_in_cart = false;
foreach ( $woocommerce->cart->get_cart() as $cart_item_key => $values ) {
$_product = $values['data'];
$terms = get_the_terms( $_product->id, 'product_cat' );
foreach ($terms as $term) {
$_categoryid = $term->term_id;
}
if (( $_categoryid === 14 ) || ( $_categoryid === 16 )) {
//book is in cart!
$book_in_cart = true;
}
}
return $book_in_cart;
}
I Hope this post will hopefully save somebody a lot of time searching all the loose bits of information ;)
This worked better for me:
add_filter( 'woocommerce_checkout_fields' , 'custom_override_checkout_fields' );
add_filter( 'woocommerce_default_address_fields' , 'optional_default_address_fields' );
function optional_default_address_fields( $address_fields ) {
$address_fields['postcode']['required'] = false;
$address_fields['city']['required'] = false;
$address_fields['state']['required'] = false;
$address_fields['address_1']['required'] = false;
$address_fields['country']['required'] = false;
$address_fields['billing_company']['required'] = false;
return $address_fields;
}
function custom_override_checkout_fields( $fields ) {
$categories = array('ajakirjad');
$foundAjakiri = false;
$foundOthers = false;
foreach ( WC()->cart->get_cart() as $cart_item ){
if(has_term( $categories, 'product_cat', $cart_item['product_id'] )) {
$foundAjakiri = true;
} else {
$foundOthers = true;
}
}
if($foundAjakiri == true && $foundOthers == false) {
// echo '1';
} elseif($foundAjakiri == false && $foundOthers == true) {
$fields['billing']['billing_address_1']['required'] = false;
$fields['billing']['billing_address_2']['required'] = false;
$fields['billing']['billing_city']['required'] = false;
$fields['billing']['billing_postcode']['required'] = false;
$fields['billing']['billing_state']['required'] = false;
$fields['billing']['billing_country']['required'] = false;
$fields['billing']['billing_country']['class'][] = 'no-need';
$fields['billing']['billing_company']['required'] = false;
unset($fields['billing']['billing_address_1']);
unset($fields['billing']['billing_address_2']);
unset($fields['billing']['billing_city']);
unset($fields['billing']['billing_postcode']);
// unset($fields['billing']['billing_country']);
unset($fields['billing']['billing_state']);
// unset($fields['billing']['billing_phone']);
//unset($fields['order']['order_comments']);
}
//add_filter( 'woocommerce_enable_order_notes_field', '__return_false', 9999 );
add_filter( 'woocommerce_checkout_fields' , 'remove_order_notes' );
return $fields;
}
I wan to add fee based on quantity.
For eg: If quantity in cart = 5 , then the fee to be added should be 4$,
If quantity in cart = 7, then the fee to be added should be 8$
I have tried this code to get quantity.
add_action('woocommerce_before_calculate_totals', 'woocommerce_custom_surcharge');
function woocommerce_custom_surcharge() {
global $woocommerce;
$amount = $woocommerce->cart->get_cart_item_quantities();
if($amount==5)
{
$excost = 7;
}
else if($amount==7){
$excost = 8;
}
$woocommerce->cart->add_fee('Charges delivery', $excost, $taxable = false, $tax_class = '');
}
Please help with the same.
Please Try this
function woocommerce_custom_surcharge(){
global $woocommerce;
//Getting Cart Contents.
$cart = $woocommerce->cart->get_cart();
//Calculating Quantity
foreach($cart as $cart_val => $cid){
$qty += $cid['quantity'];
}
//Your Condition
if($qty==5)
{
$excost = 7;
}
else if($qty==7){
$excost = 8;
}
$woocommerce->cart->add_fee('Charges delivery', $excost, $taxable = false, $tax_class = '');
}
From Here I found that We need to get the quantity of the cart by summing up the values of the cart.Hope this will Solve your problem.