Hide Coupons Based on Products in Basket - php

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' );

Related

WooCommerce: Limit purchases to one product per order

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;
}
}
}

WooCommerce subscriptions: Check if product type is a simple subscription

With WooCommerce I am using WooCommerce Subscriptions plugin. I have mainly Variable Subscription products and some few simple Subscription products.
I am using woocommerce_dropdown_variation_attribute_options_args filter hook, to update dropdown attribute values on my Variable Subscription products.
For Simple Subscriptions products I would like to add some conditions to allow or deny access to the product page.
So my question is: Which hook could I use to check if a product is a simple subscription, to allow or deny access to the product?
Any help/suggestion will be highly appreciated.
You can check product type on the WC_Product object for simple subscription like:
if( $product->get_type() === 'subscription' ) {
// Do something
}
or
if( $product->is_type('subscription') ) {
// Do something
}
And here below is an example usage that will avoid access to simple subscription product pages, redirecting customer to main shop page and displaying an error notice:
add_action('template_redirect', 'conditional_single_product_page_access');
function conditional_single_product_page_access(){
// Targeting single product pages
if ( is_product() ) {
$product = wc_get_product( get_the_ID() ); // Get the WC_Product Object
// Targeting simple subscription products
if( $product->get_type() === 'subscription' ) {
wc_add_notice( __("You are not allowed to access this product"), 'error' ); // Notice
wp_safe_redirect( get_permalink( wc_get_page_id( 'shop' ) ) ); // Redirection
exit();
}
}
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
Notes:
To target variable subscription product type use the slug variable-subscription.
To target a variation subscription, the product type slug is: subscription_variation.
You can use is_subscription() of WC_Subscriptions_Product. You need to pass $product object in is_subscription() function as parameter. Check the below code.
if( WC_Subscriptions_Product::is_subscription( $product ) ) {
// product is subscription.
} else {
// product is not subscription.
}
Update
Use woocommerce_product_is_visible filter hook for remove the product from product catalog. check below code.
add_filter( 'woocommerce_product_is_visible', 'hide_product_if_is_subscription', 20, 2 );
function hide_product_if_is_subscription( $is_visible, $product_id ){
if( WC_Subscriptions_Product::is_subscription( $product_id ) ) {
$is_visible = false;
}
return $is_visible;
}

Woocommerce add free products with ACF Post Object field

I am trying to build a custom functionality in Woocommerce with Advanced Custom Fields (ACF) plugin.
I created some code already but it is not working properly.
I want to select (simple) products at a Woocommerce product with ACF post object field. That products must be added free into the Woocommerce cart when that product will be added to the cart. Also when I add two or more items the free products must be added together.
I got also some images how the situation should work.
This is the ACF field setup.
For example: this is the product we give away.
This is the product we give away selected at a Woocommerce single product.
This is my actual code:
add_action('woocommerce_check_cart_items', 'free_products' );
function free_products() {
if( ( is_cart() || is_checkout () ) {
$free_product = get_field('gratis_producten'); // Incentive product we are giving away
$cart_id = WC()->cart->generate_cart_id( $free_product );
$free_products_in_cart = WC()->cart->find_product_in_cart( $cart_id );
if( $free_product ) {
// Removing existing "free products" from the cart.
WC()->cart->remove_cart_item( $free_products_in_cart );
// Adding to cart 40 free products
WC()->cart->add_to_cart( $free_product, 40 );
}
}
}
Any help will be highly appreciated.

Avoid checkout for specific products on specific country in Woocommerce

Here's what I want to do:
If customer selects Canada as shipping country and there's a specific product in cart, checkout should not happen and an error message should generate informing customer of why checkout didn't happen.
My research:
Add or remove woocommerce error messages has code to generate WooCommerce error messages. I asked a question yesterday that gave me codes to check if certain product is in cart and if shipping country is set to Canada Remove Canada from country list when specific products are in cart on Woocommerce I am not sure which filter/action I must hook my code to so it runs whenever "Place Order" is clicked on checkout page.
Update:
So I tried combining the two codes I have listed in my research but it didn't work. Any help if I need to approach this differently will be really appreciated
Product IDs are 15631 & 12616
This can be done using the action hook woocommerce_check_cart_items through this custom function:
add_action( 'woocommerce_check_cart_items', 'products_not_shipable_in_canada' );
function products_not_shipable_in_canada() {
// Only on checkout page (allowing customer to change the country in cart shipping calculator)
if( ! is_checkout() ) return;
// Set your products
$products = array(15631, 12616);
// Get customer country
$country = WC()->session->get('customer')['shipping_country'];
if( empty($country) ){
$country = WC()->session->get('customer')['billing_country'];
}
// For CANADA
if( $country == 'CA' ){
// Loop through cart items
foreach( WC()->cart->get_cart() as $item ){
// IF product is in cart
if( in_array( $item['product_id'], $products ) ){
// Avoid checkout and display an error notice
wc_add_notice( sprintf(
__("The product %s can't be shipped to Canada, sorry.", "woocommerce" ),
'"' . $item['data']->get_name() . '"'
), 'error' );
break; // Stop the loop
}
}
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
Related: Set minimum allowed weight for a specific country in WooCommerce

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