i'm a bit stuck for this.. i'm trying to remove a condition whenever the user adds a specific product, in this case a box of wines
So when i add a bottle of wine there's a minium amount condition so you have to add 3, but when you add a box the condition must be removed
add_action('woocommerce_after_cart_contents', 'box_special_function', 1, 1);
function box_special_function()
{
// getting cart items
$cart = WC()->cart->get_cart();
$terms = [];
// Getting categories of products in the cart
foreach ($cart as $cart_item_key => $cart_item) {
$product = $cart_item['data'];
$terms[] = get_the_terms( $product->get_id(), 'product_cat' );
}
// Cycling categories to find if there is a box inside of the cart
foreach($terms as $term => $item){
foreach($item as $key){
if($key->name == "BOX"){
// The only thing i did is to remove notices (which doesn't even work .-.)
$notices = WC()->session->get('wc_notices', array());
foreach($notices['error']){
wc_clear_notices();
}
}
}
}
}
I can't even force to checkout so i'm stuck with this.. can somebody clear my mind?
You can update your minimum product add to cart functionality, and remove validation for your specific products like this:
add_filter( 'woocommerce_quantity_input_args', 'custom_woocommerce_quantity_changes', 10, 2 );
function custom_woocommerce_quantity_changes( $args, $product ) {
$product_id = $product->get_id();
$product_title = $product->get_title();
//echo "<pre>"; print_r($product_title); echo "</pre>";
if($product_id == 1002 || $product_title == 'BOX'){
$args['input_value'] = 1;
$args['min_value'] = 1;
$args['max_value'] = 30;
$args['step'] = 1;
}else{
$args['input_value'] = 3; // Start from this value (default = 1)
$args['max_value'] = 30; // Max quantity (default = -1)
$args['min_value'] = 3; // Min quantity (default = 0)
$args['step'] = 1; // Increment/decrement by this value (default = 1)
}
return $args;
}
print your specific product id or product name and use same name or id in your if condition :
if($product_id == 1002 || $product_title == 'BOX')
For rest of product set min, max and input product validation from "else" parts.
Related
I have the following code, it loops trougth woocommerce cart and selects the most expensive item and deducts it from the total of the cart updating the total price in the process.
add_filter( 'woocommerce_cart_total', 'wc_modify_cart_price' );
add_filter( 'woocommerce_order_amount_total', 'wc_modify_cart_price' );
function wc_modify_cart_price( $price ) {
$cart_ct = WC()->cart->get_cart_contents_count();
$tax = WC()->cart->get_taxes();
if($cart_ct > 1 ){
$product_prices = [];
$fee = 0;
$prices = array();
// Loop Through cart items - Collect product prices
foreach ( WC()->cart->get_cart() as $cart_item ) {
$_product = wc_get_product( $cart_item['product_id'] );
$terms = get_the_terms( $cart_item['product_id'], 'product_cat' );
#var_dump($terms[0]->name);
if($terms[0]->name == "pizza"){
$prices[] = $cart_item['data']->get_price();
}
}
$sort_price = max($prices);
$max_price = $sort_price;
$cart_total = WC()->cart->cart_contents_total;
$addition = $cart_total - $max_price;
WC()->cart->total = $addition;
$addition = $cart_total - $max_price + $tax[2];
}else{
echo $tax;
$addition = WC()->cart->cart_contents_total + $tax[2];
}
return $addition;
}
add_filter( 'woocommerce_calculated_total', 'wc_modify_cart_price', 10, 2 );
My issue is when i continue and go to checkout the total price order reverts back and i can't find out why, maybe im just calculating and not actually setting said price? how can i not only calculate but actually set that price as the order price?
I would use a different approach here because the numbers won't add up. If you just change the final amount you'll have something like this:
Pizza 1 $50
Pizza 2 $30
subtotal: $80
Total: $30
There are $50 missing without any explanation.
You can use the hook woocommerce_before_calculate_totals to change the cart items.
For example, this code will change the price of the most expensive item to 0. But there is one problem with this. The customer can increase the quantity of these items and you will give away more than you wanted.
add_action( 'woocommerce_before_calculate_totals', 'set_most_expensive_product_to_zero' );
function set_most_expensive_product_to_zero( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
return;
}
// Set the initial maximum price to 0
$max_price = 0;
// Set the initial product ID to 0
$max_price_product_id = 0;
// Iterate through each item in the cart
foreach ( $cart->get_cart() as $cart_item ) {
// Get the product price
$product_price = $cart_item['data']->get_price();
// Check if the product price is greater than the current maximum price
if ( $product_price > $max_price ) {
// Update the maximum price
$max_price = $product_price;
// Update the product ID of the most expensive product
$max_price_product_id = $cart_item['product_id'];
}
}
// Iterate through each item in the cart again
foreach ( $cart->get_cart() as $cart_item ) {
// Check if the product ID matches the ID of the most expensive product
if ( $cart_item['product_id'] == $max_price_product_id ) {
// Set the price of the most expensive product to 0
$cart_item['data']->set_price( 0 );
}
}
}
Another approach, the one I would use, is creating a one time coupon code with the amount of the free pizza.
Let's say a customer buys 2 pizzas for $20 each and one $16 pizza. You create a $20 coupon and you are done. All the numbers will match and customers will see their discount on the cart/checkout pages.
Here is the code to do just that:
add_action( 'woocommerce_before_calculate_totals', 'generate_coupon_for_most_expensive_product' );
function generate_coupon_for_most_expensive_product( $cart ) {
if(WC()->cart->get_cart_contents_count() < 2){
remove_coupon_from_cart('pizza_');
return;
}
if ( has_coupon_code_with_prefix( 'pizza_' ) ) {
return;
}
// Set the initial maximum price to 0
$max_price = 0;
// Set the initial product ID to 0
$max_price_product_id = 0;
// Iterate through each item in the cart
foreach ( $cart->get_cart() as $cart_item ) {
// Get the product price
$product_price = $cart_item['data']->get_price();
// Check if the product price is greater than the current maximum price
if ( $product_price > $max_price ) {
// Update the maximum price
$max_price = $product_price;
// Update the product ID of the most expensive product
$max_price_product_id = $cart_item['product_id'];
}
}
// Check if the maximum price is greater than 0
if ( $max_price > 0 ) {
// Generate a coupon code
$coupon_code = uniqid( 'pizza_' );
$coupon = new WC_Coupon();
$coupon->set_code($coupon_code);
$coupon->set_amount( $max_price );
$coupon->set_individual_use(true);
$coupon->set_usage_limit(1);
$coupon->set_product_ids(array($max_price_product_id));
$coupon->save();
// Apply the coupon to the cart
$cart->add_discount( $coupon_code );
}
}
function has_coupon_code_with_prefix( $prefix ) {
// Get the applied coupons
$applied_coupons = WC()->cart->get_applied_coupons();
// Check if there are any applied coupons
if ( ! empty( $applied_coupons ) ) {
// Iterate through the applied coupons
foreach ( $applied_coupons as $coupon_code ) {
// Check if the coupon code starts with the specified prefix
if ( strpos( $coupon_code, $prefix ) === 0 ) {
// There is a coupon code applied that starts with the specified prefix
return true;
}
}
}
// No coupon code was found that starts with the specified prefix
return false;
}
function remove_coupon_from_cart($prefix) {
global $woocommerce;
// Get the cart object
$cart = $woocommerce->cart;
// Get the coupon codes applied to the cart
$coupon_codes = $cart->get_coupons();
// Loop through the coupon codes and remove any that start with the specified prefix
foreach ($coupon_codes as $code => $coupon) {
if (strpos($code, $prefix) === 0) {
$cart->remove_coupon($code);
// Save the updated cart
$cart->calculate_totals();
}
}
}
After banging my head against the wall I have finally managed to reduce my stock quantity based on the value in a custom field.
The issue I am having now is that it isn't properly validating (second part). Despite there only being say 20 in stock, I am still able to add 50 to the cart and checkout (leaving my inventory in the negative). I'd preferably like to add-to-cart using AJAX, but for now, I just want to get this validation working. Any insight?
// reduce stock based on custom field
add_filter( 'woocommerce_order_item_quantity', 'filter_order_item_quantity', 10, 3 );
function filter_order_item_quantity( $quantity, $order, $item )
{
$product = $item->get_product();
$term_name = $product->get_meta( 'custom_field', true );
// 'pa_weight' attribute value is "15 grams" - keep only the numbers
$quantity_grams = preg_replace('/[^0-9.]+/', '', $term_name);
// new quantity
if( is_numeric ( $quantity_grams ) && $quantity_grams != 0 )
$quantity *= $quantity_grams;
return $quantity;
}
// check out of stock using custom field
add_filter( 'woocommerce_add_to_cart_validation', 'woocommerce_validate_attribute_weight' );
function woocommerce_validate_attribute_weight()
{
// get product id
if (isset($_REQUEST["add-to-cart"])) {
$productid = (int)$_REQUEST["add-to-cart"];
} else {
$productid = null;
}
// get quantity
if (isset($_REQUEST["quantity"])) {
$quantity = (int)$_REQUEST["quantity"];
} else {
$quantity = 1;
}
// get weight of selected attribute
if (isset($_REQUEST["custom_field"])) {
$weight = preg_replace('/[^0-9.]+/', '', $_REQUEST["custom_field"]);
} else {
$weight = null;
}
// comparing stock
if($productid && $weight)
{
$product = wc_get_product($productid);
$productstock = (int)$product->get_stock_quantity();
if(($weight * $quantity) > $productstock)
{
wc_add_notice( sprintf( 'You cannot add that amount of "%1$s" to the cart because there is not enough stock (%2$s remaining).', $product->get_title(), $productstock ), 'error' );
return;
}
}
return true;
}
I hope someone can help. I've set up some code (thanks to this post) that adds a product to my cart if the value of the cart is over $50.
That works. However, if the value is less than $50, I want to make sure that product is NOT in the cart. So say a user adds a product, the additional product is added, but they then remove a product so the value is under $50 (not including the new product added!) ... I want it to remove the product that was added.
I tried to implement the code as in the post mentioned above:
add_action( 'template_redirect', 'add_product_to_cart' );
function add_product_to_cart() {
// Bonus products
$product_1 = '4751';
$product_2 = '4752';
$product_3 = '24711';
// Get cart value in a clean format
$cart_total = WC()->cart->get_cart_subtotal();
$cart_total = html_entity_decode($cart_total, ENT_QUOTES, 'UTF-8');
$cart_total_format = strip_tags($cart_total);
$cart_value = preg_filter("/[^0-9]/", "", $cart_total_format);
$sum_raw = $cart_value;
// Set the sum level
$level3 = '50';
// Check sum and apply product
if ($sum_raw >= $level3) {
// Cycle through each product in the cart and check for match
$found = 'false';
foreach (WC()->cart->cart_contents as $item) {
global $product;
$product_id = $item['variation_id'];
if ($product_id == $product_3) {
$found = 'true';
}
}
// If product found we do nothing
if ($found == 'true') {}
// else we will add it
else {
//We add the product
WC()->cart->add_to_cart($product_3);
}
}
// Check if sum
if ($sum_raw < $level3) {
foreach ($woocommerce->cart->get_cart() as $cart_item_key => $cart_item) {
if ($cart_item['variation_id'] == $product_3) {
//remove single product
$woocommerce->cart->remove_cart_item($cart_item_key);
}
}
}
}
But it's not removing the product. :/ Anyone got any ideas why it wouldn't work, or if there's a better solution to check the cart: if cart > $50 add a product... if it ever changes and goes < $50, remove that same product...
And also exclude that newly added product from the checks (so < $50 NOT including that product that's just been added programmatically).
Please help! :(
Maybe You have to change this line :
$cart_value = preg_filter("/[^0-9]/", "", $cart_total_format);
to (add - minus):
$cart_value = preg_filter("/[^0-9\-]/", "", $cart_total_format);
Try this code... you are currently trying to update on "add" action while it should on "remove" action.
add_action( 'template_redirect', 'remove_product_from_cart' );
function remove_product_from_cart() {
// Run only in the Cart or Checkout Page
if( is_cart() || is_checkout() ) {
/*
GET THE CART TOTAL
*/
// Set the product ID to remove
$prod_to_remove = PRODUCT-ID-TO-BE-REMOVED;
// Cycle through each product in the cart
foreach( WC()->cart->cart_contents as $prod_in_cart ) {
// Get the Variation or Product ID
$prod_id = ( isset( $prod_in_cart['variation_id'] ) && $prod_in_cart['variation_id'] != 0 ) ? $prod_in_cart['variation_id'] : $prod_in_cart['product_id'];
// Check to see if IDs match
if( $prod_to_remove == $prod_id ) {
// Get it's unique ID within the Cart
$prod_unique_id = WC()->cart->generate_cart_id( $prod_id );
// Remove it from the cart by un-setting it
unset( WC()->cart->cart_contents[$prod_unique_id] );
}
}
}
}
I would like to check item gonna be add to cart is already in cart and belong to which category, if that category have over 5 products then throw an exception message:
function check_product_in_cart() {
//Check to see if user has product in cart
global $woocommerce;
// start of the loop that fetches the cart items
foreach ( $woocommerce->cart->get_cart() as $cart_item_key => $values ) {
$_product = $values['data'];
$terms = get_the_terms( $_product->id, 'product_cat' );
// second level loop search, in case some items have several categories
$cat_ids = array();
foreach ($terms as $term) {
$cat_ids[] = $term->term_id;
}
if(in_array(434, (array)$cat_ids) || in_array(435, (array)$cat_ids)) {
//category is in cart!
$product_in_cart = true;
//count the category?
$count = 1;
while($product_in_cart === true) {
$count +=1;
}
}
}
return $product_in_cart;
return $count;
}
then echo out the error message can not add to cart. what is the best way to count product in cart per category?. Basically, I want to limit item add to cart per category. Thanks guys
In WooCommerce I use an OUTLET category with on sale products and I would like to set a minimum subtotal (30 €) for customers purchasing any "Outlet" product.
I tried to hook into woocommerce_after_calculate_totals to:
check cart items for a specific product category
display a notice when a specific product category is found and the order is lower than 30 €
and eventually redirect to cart page when user tries to checkout with an order lower than 30 €.
Here is my code:
add_action( 'woocommerce_after_calculate_totals', 'check_order_outlet_items', 10, 0 );
function check_order_outlet_items() {
global $woocommerce;
if (is_cart() || is_checkout()) {
// Check if cart contains items in Outlet cat.
$items = $woocommerce->cart->get_cart();
foreach($items as $item => $values) {
$product_id = $values['product_id'];
$terms = get_the_terms( $product_id, 'product_cat' );
foreach ($terms as $term) {
if ($term->name == "OUTLET") {
$outlet_found = 1;
break;
}
}
if ($outlet_found) {break;}
}
if ($outlet_found) {
// Calculate order amount including discount
$cart_subtotal = $woocommerce->cart->subtotal;
$discount_excl_tax_total = $woocommerce->cart->get_cart_discount_total();
$discount_tax_total = $woocommerce->cart->get_cart_discount_tax_total();
$discount_total = $discount_excl_tax_total + $discount_tax_total;
$order_net_amount = $cart_subtotal - $discount_total;
// Check if condition met
if ($order_net_amount < 30) {
if (is_checkout()) {
wp_redirect(WC()->cart->get_cart_url());
exit();
} else {
wc_add_notice( __( 'You must order at least 30 €', 'error' ) );
}
}
}
}
}
This code works perfectly in the cart page (displaying notice if cart's amount < 30 even if carts amount goes below 30 after adding a coupon) and redirecting to cart if users wants to go to checkout.
But If I go to checkout page with an amount >= 30 and then add a coupon (to lower cart amount below 30), then the Ajax recalculating totals loops and the page is blocked. But then if I reload the checkout page I'm correctly redirected to cart page.
The right hook to be used in this case is woocommerce_check_cart_items this way:
add_action( 'woocommerce_check_cart_items', 'check_cart_outlet_items' );
function check_cart_outlet_items() {
$categories = array('OUTLET'); // Defined targeted product categories
$threshold = 30; // Defined threshold amount
$cart = WC()->cart;
$cart_items = $cart->get_cart();
$subtotal = $cart->subtotal;
$subtotal -= $cart->get_cart_discount_total() + $cart->get_cart_discount_tax_total();
$found = false;
foreach( $cart_items as $cart_item_key => $cart_item ) {
// Check for specific product categories
if ( has_term( $categories, 'product_cat', $cart_item['product_id'] ) ) {
$found = true; // A category is found
break; // Stop the loop
}
}
if ( $found && $subtotal < $threshold ) {
// Display an error notice (and avoid checkout)
wc_add_notice( sprintf( __( "You must order at least %s" ), wc_price($threshold) ), 'error' );
}
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.