WooCommerce: Limit purchases to one product per order - php

In WooCommerce I want to restrict the ordering of subscription products so that if one of them is added to the cart then you cannot add any other non-subscription products. The subscription products are in the array listed with their products ids.
The issue I have is that when a customer adds non-subscription products to the cart and then tries to add a subscription product the code works (as it should).
However, if they first add a subscription product and then try to add non-subscription products the code does not work and it allows all products in the cart.
This is the current code:
// If subscription box ordered limit cart to no other products
add_filter( 'woocommerce_add_to_cart_validation', 'wc_limit_one_per_order', 10, 2 );
function wc_limit_one_per_order( $passed_validation, $product_id ) {
if ( ! in_array( $product_id, array( 245632, 245626, 245623, 245620, 245617, 245614, 245610, 245606, 245601 ) ) ) {
return $passed_validation;
}
if ( WC()->cart->get_cart_contents_count() >= 1 ) {
wc_add_notice( __( 'The subscription box cannot be purchased with other non-subscripton box products. Please, empty your cart first and then add it again.', 'woocommerce' ), 'error' );
return false;
}
return $passed_validation;
}

What are you missing, and you can include this in your function, is to check the cart for its contents. You'll need to loop over the cart items, and review the possibilities. I outlined it below
Firstly check the cart for existing products with subscriptions
Then check what is being added ($product_id). If a subscription exists, and another subscription is added. I guess there is no problem?
After that, you want respond to when a non-subscription product is added while the cart has a subscription product
And lastly, in the final else statement, it's possible to check the alternative: When a non-subscription product is in the cart and a subscription product is added.
Anyway, there might be some mistakes in the code, also with validation parameter. I didn't take that into account. Also, it can easily be simplified. Just wanted to outline all possibilities.
$subscription_products = array( 245632, 245626, 245623, 245620, 245617, 245614, 245610, 245606, 245601 );
foreach (WC()->cart->get_cart() as $cart_item_key => $values) {
$_product = $values['data'];
// is there a subscription in the cart?
if( in_array($_product->get_id(), $subscription_products) ) {
// yes!
// are we adding a subscription product?
if( in_array($product_id, $subscription_products) ) {
// we are. All good.
} else {
// no we are not. We can decide to empty the cart
unset(WC()->cart->cart_contents[$cart_item_key]);
wc_add_notice( 'Custom error message', 'error' );
// OR not add the product (return false)
return false;
}
//
} else {
// no!
// are we adding a subscription product?
if( in_array($product_id, $subscription_products) ) {
// we are. A Problem!
// We can decide to empty the cart
unset(WC()->cart->cart_contents[$cart_item_key]);
wc_add_notice( 'Custom error message', 'error' );
// OR not add the product (return false)
return false;
}
}
}

Related

Hide Coupons Based on Products in Basket

I'm trying to develop a small piece of code that builds upon the WooCommerce Subscription & All Products for WooCommerce Subscriptions plugins that hide/shows the coupon option at Checkout depending on whether the customer has chosen a subscription or one-time purchase.
From reading online I've managed to create code that hides the coupon box for all checkout users, but it lacks the functionality of checking whether the basket is a subscription or not.
Here's the code so far:
function hide_coupon_field( $enabled ) {
if ( is_checkout() ) {
$enabled = false;
}
return $enabled;
}
add_filter( 'woocommerce_coupons_enabled', 'hide_coupon_field' );
The documentation for All Products for WooCommerce Subscriptions doesn't appear to be very in-depth and so I'm unsure what, if any, functions are available to check the basket type.
Thanks
This should get the trick done. What it does is it loops over all the products in the cart, checks if they have a specific field (used by the All Products for WooCommerce Subscriptions plugin) that indicates the current product is a subscription product, and then disables the coupon field.
It disables the coupon field if any of the products are subscription products.
function hide_coupon_field( $enabled ){
$cart_contents = WC()->cart->cart_contents;
foreach ( $cart_contents as $cart_item ) {
if ( ! empty( $cart_item[ 'wcsatt_data'][ 'active_subscription_scheme' ] ) ) {
// cart contains a subscription product
$enabled = false;
break;
}
}
// else keeps the field enabled
return $enabled;
}
add_filter( 'woocommerce_coupons_enabled', 'hide_coupon_field' );

Woocommerce: Mandatory coupon for specific products

Based on Make coupon field mandatory for a product category in WooCommerce answer, I am trying to implement a mandatory coupons for specific products in woocommerce.
Here is my code attempt:
add_action( 'woocommerce_check_cart_items', 'mandatory_coupon_for_specific_items' );
function mandatory_coupon_for_specific_items() {
$targeted_ids = array(40, 41, 42, 43, 44); // The targeted product ids (in this array)
$coupon_code = 'summer1, summer2, summer3, summer4, summer5'; // The required coupon code
$coupon_applied = in_array( strtolower($coupon_code), WC()->cart->get_applied_coupons() );
// Loop through cart items
foreach(WC()->cart->get_cart() as $cart_item ) {
// Check cart item for defined product Ids and applied coupon
if( in_array( $cart_item['product_id'], $targeted_ids ) && ! $coupon_applied ) {
wc_clear_notices(); // Clear all other notices
// Avoid checkout displaying an error notice
wc_add_notice( sprintf( 'The product"%s" requires a coupon for checkout.', $cart_item['data']-
>get_name() ), 'error' );
break; // stop the loop
}
}
}
The code works perfectly to make a coupon mandatory but I need that ONE fo the products can be checkedout using only ONE of the coupons.
With the actual code if I add product ID 40 to the cart, is obligatory to add the 5 coupons, summer1, summer2, summer3, summer 4 and summer5, otherwise the checkout will be impossible. I would like the customer can make the checkout using any of the coupons for any of the products listed in the targeted_id array.
In other words, for all the products the use of coupon is mandatory but not necessarily a specific coupon, could be anyone of the listed in the code.
For example purposes I listed 5 products and 5 coupons, but in reality there are 100 products and 100 coupons
Thank you in advance
There are some mistakes in your code, use the following instead:
add_action( 'woocommerce_check_cart_items', 'mandatory_coupon_for_specific_items' );
function mandatory_coupon_for_specific_items() {
$targeted_ids = array(40, 41, 42, 43, 44); // The targeted product ids (in this array)
$coupon_codes = array('summer1', 'summer2', 'summer3', 'summer4', 'summer5'); // Array of required coupon codes
$coupons_found = array_intersect( array_filter( array_map( 'sanitize_title', $coupon_codes) ), WC()->cart->get_applied_coupons() );
// Loop through cart items
foreach(WC()->cart->get_cart() as $item ) {
// Check cart item for defined product Ids and applied coupon
if( in_array( $item['product_id'], $targeted_ids ) && empty($coupons_found) ) {
wc_clear_notices(); // Clear all other notices
// Avoid checkout displaying an error notice
wc_add_notice( sprintf( 'The product"%s" requires a coupon for checkout.', $item['data']->get_name() ), 'error' );
break; // stop the loop
}
}
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.

Prevent add to cart if cart contains a specific Woocommerce product category

My question and code is directly based off of this question -
"
I have a store containing 5 categories. It's multivendor and due to shipping complications, I can only sell products from one specific category ('paint') when they are alone in the cart. So I need to prevent add to cart of any non-paint products when the cart contains paint, and display an error message, and I need to prevent paint products being added to a cart containing any other categories, and disaply an error message.
I've tried to cobble together this code from snippets I've found lying around on Stackoverflow and elsewhere. The logic seems to work in my brain, but when I try to implement the code (through functions.php) it prevents any product from being added to the cart unless the cart is empty."
Link to question -
Prevent add to cart if cart contains a specific category (WooCommerce)
I have checked and modified the accepted answer to suit my needs however it doesnt seem to work at all as i have the same question just related to a different category (ie gas instead of paint), it was my understanding that I would just have to modify the "has_term" function in order to get this to work as it is just a matter of pointing to a particular category?
//*** Prevent mixture of gas and other prods in same cart ***//
function dont_add_gas_to_cart_containing_other($validation, $product_id) {
// Set flag false until we find a product in gas
$cart_has_gas = false;
// Set $cat_check true if a cart item is in gas cat
foreach (WC()->cart->get_cart() as $cart_item_key => $cart_item) {
$product = $cart_item['data'];
if (has_term('Gas', 'Gas Tanks & Accessories', $product->id)) {
$cart_has_gas = true;
// break because we only need one "true" to matter here
break;
}
}
$product_is_gas = false;
if (has_term('Gas', 'Gas Tanks & Accessories', $product_id)) {
$product_is_gas = true;
}
// Return true if cart empty
if (!WC()->cart->get_cart_contents_count() == 0) {
// If cart contains gas and product to be added is not gas, display error message and return false.
if ($cart_has_gas && !$product_is_gas) {
wc_add_notice('Sorry, you can only purchase Helium Gas products on their own. To purchase this product, please checkout your current cart or empty your cart and try again', 'error');
$validation = false;
}
// If cart contains a product that is not gas and product to be added is gas, display error message and return false.
elseif (!$cart_has_gas && $product_is_gas) {
wc_add_notice('Sorry, you can only purchase Helium Gas products on their own. To purchase this product, please checkout your current cart or empty your cart and try again', 'error');
$validation = false;
}
}
// Otherwise, return true.
return $validation;
}
add_filter('woocommerce_add_to_cart_validation', 'dont_add_gas_to_cart_containing_other', 10, 2);
Because you gave the category name to the function, you must give the function a slug, second parameter should be 'product_cat' and last parameter should be a Product ID.
$product = $cart_item['data'];
if( has_term( 'gas', 'product_cat', $product->get_id() ) ) {
......
}

Allow specific products to be purchased only if a coupon is applied in Woocommerce

I am working on a woocommerce website and I am trying to restrict a product to be purchased only if a coupon is applied for it, so it should not be processed without adding a coupon code. User must enter a coupon code to be able to order this specific product (not on all other products).
Any help is appreciated.
For defined products, the following code will not allow checkout if a coupon is not applied, displaying an error message:
add_action( 'woocommerce_check_cart_items', 'mandatory_coupon_for_specific_items' );
function mandatory_coupon_for_specific_items() {
$targeted_ids = array(37); // The targeted product ids (in this array)
$coupon_code = 'summer2'; // The required coupon code
$coupon_applied = in_array( strtolower($coupon_code), WC()->cart->get_applied_coupons() );
// Loop through cart items
foreach(WC()->cart->get_cart() as $cart_item ) {
// Check cart item for defined product Ids and applied coupon
if( in_array( $cart_item['product_id'], $targeted_ids ) && ! $coupon_applied ) {
wc_clear_notices(); // Clear all other notices
// Avoid checkout displaying an error notice
wc_add_notice( sprintf( 'The product"%s" requires a coupon for checkout.', $cart_item['data']->get_name() ), 'error' );
break; // stop the loop
}
}
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
And in checkout:

Avoid checkout for mixed backorder and normal items in Woocommerce

Is it possible to disable checkout if there is backorder item mixed with in stock items.
The code so far is displaying message if there is mixed items in the cart, but they still can checkout the order.
We are using Preorder plugin and on the settings, the preorder and onhand cant be mixed in cart. Below are settings of plugin.
Prevent mixing products If you enable this option, the cart cannot contain Pre-Order products and regular products at the same time.(enabled) But it only work if there is no items present in the cart.
Allow sales of out of stock products By enabling this option, Pre-Order products with no stock can be purchased. (enabled and allowed backorder) All items can be Preorder once stocks become zero.
Problem is if there is already items in cart they can checkout preorder and regular product. Please check example below
I put Product A(5 stocks) and B(10 stocks) in the cart but I dont want to checkout right away.
Then some one purchased the Product A and stocks become 0 (and Product A turn to preorder)
But if I proceed to checkout Product A(0 stocks and preorder) and B(10 stocks)so its already mixed in cart and I can proceed to checkout because backorder is allowed in settings.
Is it possible to automatically delete the Product A in cart or disable checkout?
add_action( 'woocommerce_review_order_before_payment', 'es_checkout_add_cart_notice' );
function es_checkout_add_cart_notice() {
$message = "You have a PREORDER item/s in your cart! Do not mix it if you're ordering on-hand item/s or IGNORE this message if you are ordering all pre-order item/s.";
if ( es_check_cart_has_backorder_product() )
wc_add_notice( $message, 'error' );
}
function es_check_cart_has_backorder_product() {
foreach( WC()->cart->get_cart() as $cart_item_key => $values ) {
$cart_product = wc_get_product( $values['data']->get_id() );
if( $cart_product->is_on_backorder() )
return true;
}
return false;
}
Try the following, that will really check for mixed items and will throw an error message avoiding:
"proceed to checkout" (in cart page)
placing order (checkout page)
The code:
// Display a custom notice when mixed items (backorder items and normal) avoiding checkout and "proceed to checkout" too
add_action( 'woocommerce_checkout_process', 'display_custom_error_notice' );
add_action( 'woocommerce_check_cart_items', 'display_custom_error_notice' );
function display_custom_error_notice() {
$message = __("You have a PREORDER item/s mixed with normal items. They can not be mixed.", "woocommerce");
if ( has_mixed_products() )
wc_add_notice( $message, 'error' );
}
// Utility function checking for mixed items (backorder items and normal)
function has_mixed_products() {
$on_backorder = $normal = false;
foreach( WC()->cart->get_cart() as $cart_item ) {
if( $cart_item['data']->is_on_backorder() )
$on_backorder = true;
else $normal = true;
}
return $on_backorder && $normal ? true : false;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
On cart page:
On checkout page:
Now is also possible to remove mixed items from cart throwing a noticeā€¦
With woocommerce mostly everything is possible, depending on your skills and on time to spend.
What about this?
...
if ( es_check_cart_has_backorder_product() ) {
add_filter('woocommerce_order_button_html', 'sg_remove_payment_button');
}
...
function sg_remove_payment_button ($button){
$output = '<div id="payments-disabled">';
$output .= 'Sorry, you cannot complete this order';
$output .= '</div>';
$output .= '<style>';
$output .= '.payment_methods, .wc-terms-and-conditions {display: none !important}';
$output .= '</style>';
return $output;
}

Categories