1

I want to do something simple: do not ship a particular product to Canada.

Here's the easiest way to do it in my opinion: if that particular product is present in the cart, remove Canada from checkout page.

Particular products:
1- https://constructioncovers.com/product/insulated-cement-curing-blankets/ (ID: 12616)
2- https://constructioncovers.com/product/insulated-construction-tarps/ (ID: 15631)

My research:
This article gave me a way to find products in cart and perform any action if condition is true: https://businessbloomer.com/woocommerce-easily-check-product-id-cart/ This article gave me a way to remove specific country from checkout page How to remove specific country in WooCommerce I have combined and modified the two codes to try and accomplish my task. Here's my code:

function unset_country_on_condition( $country ) {
    $product_id = 15631;
    $product_id_2 = 12616; 
    $product_cart_id = WC()->cart->generate_cart_id( $product_id );
    $product_cart_id_2 = WC()->cart->generate_cart_id( $product_id_2 );
    $in_cart = WC()->cart->find_product_in_cart( $product_cart_id );
    $in_cart_2 = WC()->cart->find_product_in_cart( $product_cart_id_2 );
    if ( $in_cart || $in_cart_2 ) {
        unset($country["CA"]);
        return $country;
    }
}
add_filter( 'woocommerce_countries', 'unset_country_on_condition', 10, 1 );

But the above function doesn't work
. It makes the country dropdown empty resulting in a site-wide warning notice.

Can someone point out what I am doing wrong?

Screenshots: enter image description here enter image description here

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
Danish Muneer
  • 568
  • 1
  • 9
  • 22
  • See similar answer [here](https://stackoverflow.com/questions/46674347/check-for-multiple-product-ids-in-cart-in-woocommerce) – Jamie_D Nov 15 '18 at 14:48
  • @Jamie_D thanks for that link, but that part works fine in my code (although not optimized I agree), the actual problem is the code that unsets the country from checkout. It's breaking the country dropdown field somehow. I have updated my question with screenshots – Danish Muneer Nov 15 '18 at 15:01
  • 1
    Does it work when you move the `return $country` out of your `if` statement? Seems like right now it only returns the list when it finds one of the 2 products and otherwise it returns nothing (aka null). – Dirk Scholten Nov 15 '18 at 15:06
  • moving `return $country` outside of `if` statement rendered the code useless. It's not doing anything – Danish Muneer Nov 15 '18 at 15:10
  • Why don't you just disable the "Add to Cart" button for those products based on visitors country? – zipkundan Nov 15 '18 at 15:42
  • @zipkundan for that we need geolocation enabled in woocommerce which we have disabled due to other reasons. – Danish Muneer Nov 15 '18 at 16:05

5 Answers5

1

Edited from this answer

   add_filter( 'woocommerce_countries', 'unset_country_on_condition', 10, 1 );
   function unset_country_on_condition( $countries ) {

    // Set here your to add product IDS (in the array)
    $product_ids = array( 15631, 12616 );
    $is_in_cart = false;

    // Iterating through cart items and check
    foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item )
        if( in_array( $cart_item['data']->get_id(), $product_ids ) ){
            $is_in_cart = true; // We set it to "true"
            break; // At least one product, we stop the loop
        }

    if( $is_in_cart ){
        unset($countries["CA"]);
    }
    return $countries;
  }
Jamie_D
  • 979
  • 6
  • 13
1

The following code will remove "Canada" from allowed countries when specific products are in cart:

add_filter( 'woocommerce_countries', 'products_disable_country', 10, 1 );
function products_disable_country( $countries ) {
    if( is_cart() || is_checkout() ) {

        $products = array(15631, 12616);

        foreach( WC()->cart->get_cart() as $item ){
            if( in_array( $item['product_id'], $products ) ){
                unset($countries["CA"]);
                return $countries;
            }
        }
    }

    return $countries;
}

Code goes in function.php file of your active child theme (active theme). tested and work.

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
1

Unfortunately, as of WooCommerce 3.6.2, they have removed the ability to check the cart in any backend calls, according to the ticket I opened. I was running a similar filter on woocommerce_countries, to restrict certain items in the cart to only be purchasable at certain locations, and got a fatal error after I updated a few days ago.

WC have said they won't fix the issue, as it's intended behaviour to speed things up. However, you can still access the cart in this filter by doing the following:

function vnm_disable_country_for_products($countries) {
    // get_cart() will fail here in WC > 3.6.2, but we can still access a basic cart via the session:

    $cartArray = WC()->session->cart;

    if (is_array($cartArray) && !empty($cartArray)) {

        $noSaleCountry = 'CA';
        $limitingProducts = array(12616, 15631);

        foreach ($cartArray as $itemArray) {
            if (in_array($itemArray['product_id'], $limitingProducts)) {

            unset($countries[$noSaleCountry]);

        }
    }

    return $countries;
}

add_filter('woocommerce_countries', 'vnm_disable_country_for_products', 10, 1);

This is ostensibly the same as Loic's answer; however as WC()->cart->get_cart() is no longer available, we're accessing the cart via WC()->session->cart instead.

indextwo
  • 5,535
  • 5
  • 48
  • 63
1

Due to a change in the woocommerce API a little change is needed. https://github.com/woocommerce/woocommerce/issues/23758

The cart is not available like before.

add_filter('woocommerce_countries', 'products_disable_country', 10, 1);
function products_disable_country( $countries ) {
    global $woocommerce;
  // is_cart & is_checkout does not to be working anymore.
  //   if( is_cart() || is_checkout() ) {

        $products = array(15631, 12616);
        // but you can get the cart via the session
        $cart = $woocommerce->session->cart; 
        foreach( $cart as $item ){
            if( in_array( $item['product_id'], $products ) ){
                unset($countries["CA"]);
                return $countries;
            }
        }
    // }

    return $countries;
}

Keanu1989
  • 29
  • 1
  • 4
0

Can you try following code.

global $product_check; //declare a global variable
function remove_canada_insulated_blankets_traps(){
    $product_ids = array(12616,15631);
    foreach($product_ids as $product_id){
        $product_cart_id = WC()->cart->generate_cart_id( $product_id );
        $in_cart = WC()->cart->find_product_in_cart( $product_cart_id );
        $product_check[$in_cart]; //global variable is converted into an array storing ids
    }
}
add_action( 'woocommerce_before_cart', 'remove_canada_insulated_blankets_traps' );

//pass the global variable into following function as another argument
function woo_remove_canada_country( $countries, $product_check ){
    if( count(product_check) > 0 ){ //check count of the array
        unset($countries['CA']); //unset country
    }
    return $countries; 
}
add_filter( 'woocommerce_countries', 'woo_remove_canada_country', 10, 2 );

Notice the change in last argument of 'woocommerce_countries' filter. Since additional argument is passed to the function the last argument is changed to 2 instead of 1.

zipkundan
  • 1,754
  • 1
  • 12
  • 16
  • thank you but your code gives this error `Uncaught ArgumentCountError: Too few arguments to function woo_remove_canada_country(), 1 passed in wp-includes/class-wp-hook.php on line 286 and exactly 2 expected in wp-content/themes/genesis-sample/functions.php:370` – Danish Muneer Nov 15 '18 at 15:56