PHP How to disable shipping methods based on subtotal - php

I try to disable some shipping methods based on cart subtotal (after discounts).
// Disable Woocommerce shipping methods based on cart subtot
function wpsh_hide_shipping_based_on_subtotal( $rates, $package ) {
// Retrieve cart subtotal
//$cart_subtotal = $package['contents_cost'];
$cart_subtotal = WC()->cart->get_cart_contents_total();
// Shipping rate to be excluded
$shipping_id1 = 'flat_rate:73';
$shipping_id2 = 'flat_rate:75';
$shipping_id3 = 'local_pickup:69';
$shipping_id4 = 'flat_rate:76';
$shipping_id5 = 'local_pickup:79';
if ( $cart_subtotal < 99 ){
unset( $rates[ $shipping_id4 ] );
unset( $rates[ $shipping_id5 ] );
}
if ( $cart_subtotal >= 99 ){
unset( $rates[ $shipping_id1 ] );
unset( $rates[ $shipping_id2 ] );
unset( $rates[ $shipping_id3 ] );
}
return $rates;
}
add_filter( 'woocommerce_package_rates', 'wpsh_hide_shipping_based_on_subtotal', 10, 2 );
This seems to work, but sometimes, especially with variable products it doesnt hide shipping methods below 99 subtotal. I dont have any clues why. Can you help me?

Try with woocommerce_shipping_packages to hide the shipping method.
add_filter( 'woocommerce_shipping_packages', array( $this, 'hide_shipping_rates_from_packages'), 10, 1 );
public function hide_shipping_rates_from_packages( $packags ) {
if ( !empty( WC()->cart ) ) {
$cart_subtotal = wc_format_decimal( WC()->cart->get_cart_subtotal( false ) ); // Without include compound taxes.
if ( $cart_subtotal < 80 ) {
foreach( $packags as $index => $package ) {
unset($packags[$index]['rates']['free_shipping:1']);
}
}
}
return $packags;
}
For recurring carts, use the woocommerce_package_rates filter to hide shipping methods. This filter affects the session-stored package details and this filter is not triggered when rates are stored in the WC session data for this package.
add_filter( 'woocommerce_package_rates', array( $this, 'hide_shipping_rates_from_package_rates'), 10, 2 );
public function hide_shipping_rates_from_package_rates( $package_rates, $package ) {
if ( !empty( WC()->cart ) ) {
$cart_subtotal = wc_format_decimal( WC()->cart->get_cart_subtotal( false ) ); // Without include compound taxes.
if ( $cart_subtotal < 80 ) {
unset($packag_rates['free_shipping:1']);
}
}
return $packag_rates;
}

Related

Hide multiple shipping methods for specific products in Woocommerce [duplicate]

I want to hide a set of shipping methods if a set of products are selected on WooCommerce
I think I'm almost there but I am getting an error on the unset( $rates[$method_ids] ) line.
Here's what I've got so far:
add_filter( 'woocommerce_package_rates', 'specific_products_shipping_methods_hide_many', 10, 2 );
function specific_products_shipping_methods_hide_many( $rates, $package ) {
$product_ids = array( 240555 ); // HERE set the product IDs in the array
$method_ids = array( 'flat_rate:7', 'flat_rate:13', 'flat_rate:26', 'flat_rate:27', 'local_pickup:24' ) ; // HERE set the shipping method IDs
$found = false;
// Loop through cart items Checking for defined product IDs
foreach( $package['contents'] as $cart_item ) {
if ( in_array( $cart_item['product_id'], $product_ids ) ){
$found = true;
break;
}
}
if ( $found )
unset( $rates[$method_ids] );
return $rates;
}
Any advice?
You are close, but you need to loop through $rates and if it occurs $rate_id, you can unset it
So you get:
function filter_woocommerce_package_rates( $rates, $package ) {
// Set the product IDs in the array
$product_ids = array( 240555, 30 );
// Set the rate IDs in the array
$rate_ids = array( 'flat_rate:7', 'flat_rate:13', 'flat_rate:26', 'flat_rate:27', 'local_pickup:24', 'local_pickup:1', 'free_shipping:2' );
// Initialize
$found = false;
// Loop through cart items checking for defined product IDs
foreach( $package['contents'] as $cart_item ) {
// Checks if a value exists in an array
if ( in_array( $cart_item['product_id'], $product_ids ) ) {
$found = true;
break;
}
}
// True
if ( $found ) {
// Loop trough
foreach ( $rates as $rate_id => $rate ) {
// Checks if a value exists in an array
if ( in_array( $rate_id, $rate_ids ) ) {
unset( $rates[$rate_id] );
}
}
}
return $rates;
}
add_filter( 'woocommerce_package_rates', 'filter_woocommerce_package_rates', 10, 2 );

Hide free shipping when subtotal is greater than 0 in WooCommerce

I'm using Free Product Sample for the WooCommerce plugin and trying to set conditional free shipping only for a Sample product, sample product cost is 0 and therefore I'm trying to hide free shipping unless the order subtotal amount is 0.
To put it in a nutshell:
I want to offer free shipping only for sample products and, the free shipping method should only visible for a product having subtotal = 0
Screenshot for further clarification:
My attempted code:
function ts_hide_shipping_for_order_total( $rates ) {
$rate = array();
$order_total = WC()->cart->get_subtotal();
if( $order_total > 0 ) {
foreach ( $rates as $rate_id => $rate ) {
if ( 'free_shipping:20' === $rate->get_method_id() ) {
$rate[ $rate_id ] = $rate;
}
}
}
return ! empty( $free ) ? $free : $rates;
}
add_filter( 'woocommerce_package_rates', 'ts_hide_shipping_for_order_total', 100 );
OR
add_filter( 'woocommerce_package_rates', 'hide_shipping_except_local_when_free_is_available', 100 );
function hide_shipping_except_local_when_free_is_available($rates) {
$free = $local = array();
foreach ( $rates as $rate_id => $rate ) {
if ( 'free_shipping:20' === $rate->method_id ) {
$free[ $rate_id ] = $rate;
}
}
return ! empty( $free ) ? array_merge( $free, $local ) : $rates;
}
But both codes do not give me the desired result. Can anyone point me in the right direction?
To hide/unset the "free shipping", as long as the subtotal is greater than 0, you can use the following.
So you get:
function filter_woocommerce_package_rates( $rates, $package ) {
// Get subtotal
$subtotal = $package['cart_subtotal'];
// Hide free shipping if subtotal > 0
if ( $subtotal > 0 ) {
// Loop trough
foreach ( $rates as $rate_id => $rate ) {
if ( $rate->method_id === 'free_shipping' ) {
unset( $rates[$rate_id] );
}
}
}
return $rates;
}
add_filter( 'woocommerce_package_rates', 'filter_woocommerce_package_rates', 10, 2 );
Additional question: Hide the other methods when subtotal <= 0
function filter_woocommerce_package_rates( $rates, $package ) {
// Get subtotal
$subtotal = $package['cart_subtotal'];
// Loop trough
foreach ( $rates as $rate_id => $rate ) {
if ( $rate->method_id === 'free_shipping' ) {
// Hide free shipping if subtotal > 0
if ( $subtotal > 0 ) {
unset( $rates[$rate_id] );
}
} else {
// Hide other methods when subtotal <= 0
if ( $subtotal <= 0 ) {
unset( $rates[$rate_id] );
}
}
}
return $rates;
}
add_filter( 'woocommerce_package_rates', 'filter_woocommerce_package_rates', 10, 2 );

WooCommerce remove shipping method based on product IDs & quantity bought

I am trying to remove a shipping method based on two parameters in my cart.
Parameter 1 = Product ID
Parameter 2 = Quantity added for that Product ID.
I have been searching around and combining different solutions, however using the snippet underneath still doesn't give me the correct result. The expected result would be that if any of the products ( 6 , 9, 69 , 71 ) are added 52 times to my cart, the shipping fee (flexible_shipping_2_1) should dissapear.
All suggestions would be appreciated.
add_filter( 'woocommerce_package_rates', 'specific_products_shipping_methods', 10, 2 );
function specific_products_shipping_methods( $rates, $package ) {
$product_ids = array( 6 , 9, 69 , 71 ); // HERE set the product IDs in the array
$method_id = 'flexible_shipping_2_1'; // HERE set the shipping method ID
$found = false;
// Loop through cart items Checking for defined product IDs
foreach( WC()->cart->get_cart_contents() as $cart_item_key => $cart_item ) {
if ( in_array( $cart_item['product_id'], $product_ids ) && $cart_item['quantity'] == 52){
$found = true;
break;
}
}
if ( $found )
unset( $rates[$method_id] );
return $rates;
}
Maybe this is what you would like (to get the cumulated quantity for all defined product ids):
add_filter( 'woocommerce_package_rates', 'specific_products_shipping_methods', 10, 2 );
function specific_products_shipping_methods( $rates, $package ) {
$product_ids = array( 6 , 9, 69 , 71 ); // HERE set the product IDs in the array
$method_id = 'flexible_shipping_2_1'; // HERE set the shipping method ID
$quantity = 0;
// Get cart items for the current shipping package
foreach( $package['contents'] as $cart_item ) {
if ( in_array( $cart_item['product_id'], $product_ids ) ){
$quantity += $cart_item['quantity'];
}
}
if ( $quantity >= 52 && isset($rates[$method_id]) ) {
unset($rates[$method_id]);
}
return $rates;
}
Don't forget to empty your cart, to refresh shipping cached data…

Allow customer to set the product price and add to cart with certain validations in WooCommerce

I'm working on a giftcard product whereof I need the customer to be able to set the price as long as it is 100 or more. Problem is, I'm not sure how to create the value check.
The customer should then be able to add to cart as normal and to checkout as usual.
I've included a remove_action for the product price (which for some reason does not work) if the product is assigned to the giftcard category.
The field input has been created and the data should be carried over to the cart and checkout and into the order -- but it does not work for some reason.
The next step is to set the product price into whatever the customer submits as the giftcard value (as long as it is 100 or more) and to display that as the product price on cart and checkout.
If anyone can review and help me out, that would be awesome.
add_action( 'woocommerce_before_add_to_cart_form', 'giftcard_price_field' );
function giftcard_price_field() {
global $product;
if( has_term('giftcard', 'product_cat', $product->get_id() ) ) {
// if the product is assigned to the giftcard category, remove the product price
remove_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_price', 10 );
// add a new input field for the price, allowing the customer to set the price
echo '<div class="giftcard-product-price">
<label for="giftcard-product-price">Giftcard value: </label>
<input type="text" id="giftcard-product-price" name="giftcard-product-price" placeholder="Giftcard value" maxlength="1000">
</div>';
}
}
add_filter( 'woocommerce_add_cart_item_data', 'giftcard_price_field_cart_data', 10, 3 );
function giftcard_price_field_cart_data( $cart_item_data, $product_id, $variation_id ) {
if( ! empty ( $_POST[ 'giftcard-product-price' ] ) ) {
// need to check that the value is NOT below 100 and if so, create a wc_notice warning
$cart_item_data['giftcard-product-price'] = sanitize_text_field( $_POST['giftcard-product-price']);
}
return $cart_item_data;
}
add_filter( 'woocommerce_get_item_data', 'giftcard_price_field_display_data', 10, 2 );
function giftcard_price_field_display_data( $item_data, $cart_item ) {
if( ! empty ( $cart_item[ 'giftcard-product-price' ] ) ) {
$item_data[] = array (
'key' => 'Giftcard value',
'value' => $cart_item['giftcard-product-price'],
'display' => '',
);
}
return $item_data;
}
add_action( 'woocommerce_checkout_create_order_line_item', 'giftcard_price_field_order_data', 10, 4 );
function giftcard_price_field_order_data( $item, $cart_item_key, $values, $order ) {
if( ! empty ( $values[ 'giftcard-product-price' ] ) ) {
$item->add_meta_data( 'Giftcard value', $values['giftcard-product-price'] );
}
}
Add a new field 'giftcard_product_price' on single product page if has_term()
Removes the original product price on the single product page
Various validations have been added and are possible
The price of the product (giftcard) is adjusted to the price entered by the customer
function giftcard_price_field() {
global $product;
// Instanceof
if ( $product instanceof WC_Product ) {
// Set category(ies)
$cats = array ( 'giftcard' );
// True
if ( has_term( $cats, 'product_cat', $product->get_id() ) ) {
// add a new input field for the price, allowing the customer to set the price
echo '<div class="giftcard-product-price">
<label for="giftcard-product-price">Giftcard value: </label>
<input type="text" id="giftcard_product_price" name="giftcard_product_price" placeholder="Giftcard value" maxlength="1000">
</div>';
}
}
}
add_action( 'woocommerce_before_add_to_cart_button', 'giftcard_price_field', 10, 0 );
// Remove price
function action_woocommerce_single_product_summary() {
global $product;
// Instanceof
if ( $product instanceof WC_Product ) {
// Set category(ies)
$cats = array ( 'giftcard' );
// True
if ( has_term( $cats, 'product_cat', $product->get_id() ) ) {
remove_action('woocommerce_single_product_summary','woocommerce_template_single_price', 10 );
}
}
}
add_action( 'woocommerce_single_product_summary', 'action_woocommerce_single_product_summary', 5 );
// Validate
function filter_woocommerce_add_to_cart_validation( $passed, $product_id, $quantity, $variation_id = null, $variations = null ) {
// Isset
if ( isset ( $_POST['giftcard_product_price'] ) ) {
$giftcard_product_price = $_POST['giftcard_product_price'];
// Error = empty, not numeric or less than 100
if ( empty ( $giftcard_product_price ) ) {
wc_add_notice( __( 'Field is empty', 'woocommerce' ), 'error' );
$passed = false;
} elseif ( ! is_numeric ( $giftcard_product_price ) ) {
wc_add_notice( __( 'NOT a number or a numeric string', 'woocommerce' ), 'error' );
$passed = false;
} elseif ( $giftcard_product_price < 100 ) {
wc_add_notice( __( 'Less than 100', 'woocommerce' ), 'error' );
$passed = false;
}
}
return $passed;
}
add_filter( 'woocommerce_add_to_cart_validation', 'filter_woocommerce_add_to_cart_validation', 10, 5 );
function filter_add_cart_item_data( $cart_item_data, $product_id, $variation_id ) {
if ( isset ( $_POST['giftcard_product_price'] ) ) {
$cart_item_data['giftcard_product_price'] = sanitize_text_field( $_POST['giftcard_product_price'] );
}
return $cart_item_data;
}
add_filter( 'woocommerce_add_cart_item_data', 'filter_add_cart_item_data', 10, 3 );
// Set price
function action_woocommerce_before_calculate_totals( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Loop through cart items
foreach ( $cart->get_cart() as $cart_item ) {
if ( isset ( $cart_item['giftcard_product_price'] ) ) {
$cart_item['data']->set_price( $cart_item['giftcard_product_price'] );
}
}
}
add_action( 'woocommerce_before_calculate_totals', 'action_woocommerce_before_calculate_totals', 10, 1 );

Hide shipping methods by product category and if not

I am trying to put together a function to hide a shipping method if there is a finished product category in the cart and if that category is not in the cart then hide another one.
This is the function that I have been able to put together, but it doesn't work. Can you help me please?
function product_category_find_id_in_cart() {
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ){
if ( has_term( 'hangers', 'product_cat', $cart_item['product_id'] ) ) {
unset( $rates['free_shipping1'] );
}
else {
unset( $rates['flat_rate:6'] );
}
return $rates;
}
}
I need it to also work for various shipping methods, not just to hide one. Thank you!
Thanks to # 7uc1f3r I have a new code that hides the shipping method if the product belongs to a category, but I need that if the product does not belong to that category other shipping methods are hidden. This would be the new formula, thanks to #LoicTheAztec:
add_filter( 'woocommerce_package_rates', 'specific_products_shipping_methods', 10, 2 );
function specific_products_shipping_methods( $rates, $package ) {
// HERE set the product category in the array (ID, slug or name)
$terms = array( 'hangers' );
$taxonomy = 'product_cat';
// HERE set the shipping methods to be removed (like "fat_rate:5")
$method_instances_ids = array('1');
$found = false;
// Loop through cart items checking for defined product IDs
foreach( $package['contents'] as $cart_item ) {
if ( has_term( $terms, $taxonomy, $cart_item['product_id'] ) ){
$found = true;
break;
}
}
if ( ! $found ) return $rates; // If not found we exit
// Loop through your active shipping methods
foreach( $rates as $rate_id => $rate ) {
// Remove all other shipping methods other than your defined shipping method
if ( in_array( $rate_id, $method_instances_ids ) ){
unset( $rates[$rate_id] );
}
}
return $rates;
}
I found it!!
add_filter( 'woocommerce_package_rates', 'specific_products_shipping_methods', 10, 2 );
function specific_products_shipping_methods( $rates, $package ) {
// HERE set the product category in the array (ID, slug or name)
$terms = array( 'hangers' );
$taxonomy = 'product_cat';
// HERE set the shipping methods to be removed (like "fat_rate:5")
$method_instances_ids = array('flat_rate:12','flat_rate:13','flat_rate:14');
$found = false;
// Loop through cart items checking for defined product IDs
foreach( $package['contents'] as $cart_item ) {
if ( has_term( $terms, $taxonomy, $cart_item['product_id'] ) ){
$found = true;
break;
}
}
if ( ! $found ) { unset( $rates['flat_rate:6'],$rates['flat_rate:7'],$rates['flat_rate:8'],$rates['flat_rate:9'],$rates['flat_rate:10'],$rates['flat_rate:11'] ); return $rates;} // If not found we exit
else {
// Loop through your active shipping methods
foreach( $rates as $rate_id => $rate ) {
// Remove all other shipping methods other than your defined shipping method
if ( in_array( $rate_id, $method_instances_ids ) ){
unset( $rates[$rate_id] );
}
}
return $rates;
}}

Categories