I currently have free shipping on items over $75, but if an admin login and place an order. I want the admin to have free shipping regardless if the cart is not over $75. I've been trying to use the following code, but it's not triggering it. Am I doing something wrong?
add_filter( 'woocommerce_package_rates', 'show_free_shipping', 10, 2 );
function show_free_shipping( $rates, $package ) {
global $current_user;
$free = array();
foreach ( $rates as $rate_id => $rate ) {
if ( 'free_shipping' === $rate->method_id ) {
if ($current_user->ID) {
$user_roles = $current_user->roles;
$user_role = array_shift($user_roles);
if ($user_role == 'administrator') {
$free[ $rate_id ] = $rate;
break;
}
}
}
}
return ! empty( $free ) ? $free : $rates;
//return $rates;
}
the filter woocommerce_package_rates is used after that the free shipping is set has "not available" because the minimum is not reached.
to make the free shipping available again, you can use the filter woocommerce_shipping_free_shipping_is_available
https://docs.woocommerce.com/wc-apidocs/source-class-WC_Shipping_Free_Shipping.html#200-202
try this :
add_filter("woocommerce_shipping_free_shipping_is_available"
, function ($is_available, $package, $method)
{
$current_user = wp_get_current_user();
if (in_array("administrator", $current_user->roles)) {
$is_available = TRUE;
}
return $is_available;
}, 10, 3);
Related
I added a plugin that creates a shipping method with Id "pisol_extended_flat_shipping:52540" when the product category is "baby-care" and the country is "India" and It's working fine. When someone adds different products from different categories in the cart and one of the categories is "baby-care". then I want to hide the "pisol_extended_flat_shipping:52540" shipping when there are other category products at the checkout along with "baby-care".
I tried this code, but not working
function check_cart_categories() {
$baby_care = false;
$other_categories = false;
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$product = $cart_item['data'];
if ( has_term( 'baby-care', 'product_cat', $product->id ) ) {
$baby_care = true;
} else {
$other_categories = true;
}
}
return ( $baby_care && $other_categories );
}
add_filter( 'woocommerce_available_shipping_methods', 'hide_pisol_extended_flat_shipping' );
function hide_pisol_extended_flat_shipping( $available_methods ) {
if ( check_cart_categories() ) {
unset( $available_methods['pisol_extended_flat_shipping:52540'] );
}
return $available_methods;
}
Here is the full code:
function check_cart_categories() {
// your logic here...
}
add_filter( 'woocommerce_package_rates', 'hide_pisol_extended_flat_shipping' , 10, 2 );
function hide_pisol_extended_flat_shipping( $rates, $package ) {
if ( check_cart_categories() ) {
unset( $rates['pisol_extended_flat_shipping:52540'] );
}
return $rates;
}
If it doesn't work, try increasing the priority of the hook (I set default 10). Some plugins are also using this hook and could interfere.
So here's what I need to do: find out the cheapest shipping method and change the "Free Shipping" method label to "Free Shipping" + "Cheapest method label". This way the customer will be able to know what is the shipping method used for the free shipping.
First I thought about global variables.. then used the wp_options, but I am not certain if the wp_options are user shared or user-specific data.
Would you have any good ideas or corrections on the following code, which by the way isn't working? Thanks!
add_filter( 'woocommerce_shipping_chosen_method', 'wf_default_shipping_method', 10 );
function wf_default_shipping_method( $method ) {
$the_cheapest_cost = 1000000;
$packages = WC()->shipping()->get_packages()[0]['rates'];
foreach ( array_keys( $packages ) as $key ) {
if ( ( $packages[$key]->cost > 0 ) && ( $packages[$key]->cost < $the_cheapest_cost ) ) {
$the_cheapest_cost = $packages[$key]->cost;
$transport_label = $packages[$key]->label;
$method_id = $packages[$key]->id;
}
}
if(get_option('atransport')){
update_option('atransport', $transport_label);
}else{
add_option('atransport', $transport_label);
}
return $method_id;
}
add_filter( 'woocommerce_package_rates', 'change_shipping_methods_label_names', 20, 2 );
function change_shipping_methods_label_names( $rates, $package ) {
$transport = get_option('atransport');
foreach( $rates as $rate_key => $rate ) {
if ( __( 'Free Shipping', 'woocommerce' ) == $rate->label ){
$rates[$rate_key]->label = __( 'FreeShipping -' . $transport, 'woocommerce' ); // New label name
}
}
delete_option('atransport');
return $rates;
}
You can iterate a loop of $rates and get the cheapest method. then based on the found cheapest method you can change the label. code will go in your active theme functions.php file.
add_filter( 'woocommerce_package_rates', 'hide_other_shipping_when_free_is_available', 100, 1 );
function hide_other_shipping_when_free_is_available( $rates ) {
$cheapest_method = '';
// Loop through shipping rates
if ( is_array( $rates ) ) {
foreach ( $rates as $key => $rate ) {
// Set variables when the rate is cheaper than the one saved
if ( empty( $cheapest_method ) || $rate->cost < $cheapest_method->cost ) {
$cheapest_method = $rate;
}
}
}
// Return the cheapest rate when possible
if ( ! empty( $cheapest_method ) ) {
$free = array();
foreach ( $rates as $rate_id => $rate ) {
if ( $rate->id === $cheapest_method->id ) {
if ( __( 'Free Shipping', 'woocommerce' ) == $rate->label ){
$rates[ $rate_id ] = $rate;
// Here we append the labbelled saved price formated display
$rates[ $rate_id ]->label = 'Free Shipping '.$rates[ $rate_id ]->label;
break; // stop the loop
}
}
}
return $rates;
}
return $rates;
}
Tested and works
Just starting in my WordPress/WooCommerce journey.
I found a question on here from a while back asking about restricting payment gateways for a specific attribute selection. I've tried to integrate this into my site and I cannot get it working! I've added the Attribute and Terms, and the slugs are as follows - pay_now_deposit (attr) and pay_now / deposit (terms).
I'm looking to restrict Klarna payments for those paying by deposit, and the code I have is below -
function conditional_payment_gateways( $available_gateways ) {
$in_cart = false;
foreach ( WC()->cart->get_cart_contents() as $key => $values ) {
// See if there is an attribute called 'pa_size' in the cart
// Replace with whatever attribute you want
if (array_key_exists('pa_pay_now_deposit', (array) $values['data']->get_attributes() ) ) {
foreach ($values['data']->get_attributes() as $attribute => $variation);
// Replace 'small' with your value.
if ($variation == 'deposit') $in_cart = true; //edited
}
}
if ( $in_cart ) {
unset($available_gateways['klarna_payments']);
}
else {
unset($available_gateways['cod']);
}
return $available_gateways;
}
Any advice on where I'm going wrong would be greatly appreciated!
Try to add a break statement.
add_filter('woocommerce_available_payment_gateways', 'conditional_payment_gateways', 10, 1);
function conditional_payment_gateways( $available_gateways ) {
$in_cart = false;
foreach ( WC()->cart->get_cart_contents() as $key => $values ) {
if ( array_key_exists( 'pa_pay_now_deposit', (array) $values['data']->get_attributes() ) ) {
foreach ( $values['data']->get_attributes() as $attribute => $variation ){
if ( $variation == 'deposit' ){
$in_cart = true; //edited
break;
}
}
}
if( $in_cart ){
break;
}
}
if ( $in_cart ) {
unset( $available_gateways['klarna_payments'] );
}else {
unset( $available_gateways['cod'] );
}
return $available_gateways;
}
How can I change shipping cost in Woocommerce by Ajax request?
I tried this:
add_action('wp_ajax_set_shipping_price', 'set_shipping_price');
add_action('wp_ajax_nopriv_set_shipping_price', 'set_shipping_price');
function set_shipping_price(){
$packages = WC()->cart->get_shipping_packages();
foreach ($packages as $package_key => $package){
$session_key = 'shipping_for_package_'.$package_key;
$stored_rates = WC()->session->__unset( $session_key );
$WC_Shipping = new WC_Shipping();
$WC_Shipping->calculate_shipping_for_package( $package, $package_key = 0);
WC()->cart->calculate_shipping();
WC()->cart->calculate_totals();
}
wp_die();
}
and:
add_filter( 'woocommerce_package_rates', 'custom_shipping_costs', 20, 2 );
function custom_shipping_costs( $rates, $package ) {
if (isset($_POST['cost'])){
$new_cost = $_POST['cost'];
}
$new_cost = 0;
$tax_rate = 0.2;
foreach( $rates as $rate_key => $rate ){
if( $rate->method_id != 'free_shipping'){
$rates[$rate_key]->cost = $new_cost;
$taxes = array();
foreach ($rates[$rate_key]->taxes as $key => $tax){
if( $rates[$rate_key]->taxes[$key] > 0 )
$taxes[$key] = $new_cost * $tax_rate;
}
$rates[$rate_key]->taxes = $taxes;
}
}
return $rates;
}
The hook woocommerce_package_rates works at page load but do nothing by ajax. Help please.
Here is the explanation on how to get post data from an Ajax call.
Additionally, you can use the WooCommerce update_checkout event to update the checkout (and thus recalculate shipping costs) after a field in the checkout has changed or been clicked (instead of creating a custom Ajax call). Here you will find a complete list.
So, assuming you want to recalculate shipping costs when the field with id custom_shipping_price (for example) changes, you can use this script:
// updates the checkout (and shipping charge calculation) when the value of a field changes
add_action( 'wp_footer', 'update_checkout' );
function update_checkout() {
// only in the checkout
if ( ! is_checkout() ) {
return;
}
?>
<script type="text/javascript">
jQuery( document ).ready(function( $ ) {
// when the value of the field with id "custom_shipping_price" changes it updates the checkout
jQuery('#custom_shipping_price').change(function(){
jQuery('body').trigger('update_checkout');
});
});
</script>
<?php
}
Now you will need to use the woocommerce_package_rates hook to calculate the new shipping cost.
Make sure you initialize the value of the $new_cost variable in
case the custom field is not set or is empty.
Here the function:
// update the shipping cost based on a custom field in the checkout
add_filter( 'woocommerce_package_rates', 'update_shipping_cost_based_on_custom_field', 10, 2 );
function update_shipping_cost_based_on_custom_field( $rates, $package ) {
if ( ! $_POST || is_admin() || ! is_ajax() ) {
return;
}
// gets the post serialized data sent with the Ajax call
if ( isset( $_POST['post_data'] ) ) {
parse_str( $_POST['post_data'], $post_data );
} else {
$post_data = $_POST;
}
// set the cost of shipping (if the custom field should be empty)
$new_cost = 0;
// if the field is set it gets the value
if ( isset( $post_data['custom_shipping_price'] ) && ! empty( $post_data['custom_shipping_price'] ) ) {
// forces conversion of value into number
$new_cost = (float) str_replace( ',', '.', $post_data['custom_shipping_price'] );
}
// set the percentage of taxes (ex. 22%)
$tax_rate = 0.22;
foreach( $rates as $rate_key => $rate ) {
if ( 'free_shipping' !== $rate->method_id ) {
// set rate cost
$rates[$rate_key]->cost = $new_cost;
// set taxes rate cost (if enabled)
$taxes = array();
foreach ( $rates[$rate_key]->taxes as $key => $tax ) {
if ( $rates[$rate_key]->taxes[$key] > 0 ) {
$taxes[$key] = $new_cost * $tax_rate;
}
}
$rates[$rate_key]->taxes = $taxes;
}
}
return $rates;
}
Both codes have been tested and work. Add them to your active theme's functions.php.
I'd like to do per product shipping programmatically. And I don't want to use the plugin. This seems like it would easy:
add meta field called "shipping_price" for each product.
hook into checkout and update shipping based off each products "shipping_price" that's in your cart
I know how to do #1. but any ideas on the best way to achieve #2?
This can be done in the following way, using get_post_meta to get the meta field 'shipping_price'
Note 1: to test this code added a line with dummy data
Note 2: Don't forget to specify the $rate->method_id
function filter_woocommerce_package_rates( $rates, $package ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;
// Set variable
$cost = 0;
// Loop through line items
foreach( $package['contents'] as $line_item ) {
// Get product id
$product_id = $line_item['product_id'];
// Quantity
$quantity = $line_item['quantity'];
// Get post meta
$shipping_price = get_post_meta( $product_id, 'shipping_price', true);
// DEBUG, for testing purposes, REMOVE AFTERWARDS!!
$shipping_price = 10;
if ( $shipping_price ) {
$cost += $shipping_price * $quantity;
}
}
if ( $cost > 0 ) {
// (Multiple)
foreach ( $rates as $rate_key => $rate ) {
// Targeting
if ( in_array( $rate->method_id, array( 'free_shipping', 'distance_rate', 'table_rate' ) ) ) {
// Set rate cost
$rates[$rate_key]->cost = $cost;
}
}
// Single
// Set rate cost
// $rates['free_shipping']->cost = $cost;
}
return $rates;
}
add_filter( 'woocommerce_package_rates', 'filter_woocommerce_package_rates', 100, 2 );
Expanding on 7uc1f3rs answer. I had to slightly modify to get it to work with WooCommerce 3.0+ new way of doing it.
$rates[$rate_key]->cost = $cost;
needs to be
$rates[$rate_key]->set_cost($cost);
You can use functions from https://docs.woocommerce.com/wc-apidocs/class-WC_Shipping_Rate.html for $rates
function filter_woocommerce_package_rates( $rates, $package ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;
// Set variable
$cost = 0;
// Loop through line items
foreach( $package['contents'] as $line_item ) {
// Get product id
$product_id = $line_item['product_id'];
// Quantity
$quantity = $line_item['quantity'];
// Get post meta
//$shipping_price = get_post_meta( $product_id, 'shipping_price', true);
// DEBUG, for testing purposes, REMOVE AFTERWARDS!!
$shipping_price = 10;
if ( $shipping_price ) {
$cost += $shipping_price * $quantity;
}
}
if ( $cost > 0 ) {
// (Multiple)
foreach ( $rates as $rate_key => $rate ) {
// Targeting
if ( in_array( $rate->method_id, array( 'free_shipping', 'flat_rate' ) ) ) {
// Set rate cost
// use functions from https://docs.woocommerce.com/wc-apidocs/class-WC_Shipping_Rate.html
$rates[$rate_key]->set_cost($cost);
}
}
// Single
// Set rate cost
//$rates['flat_rate:1']->set_cost($cost);
// use this to see your rates
//echo '<pre>'; print_r($rates); echo '</pre>';
}
return $rates;
}
add_filter( 'woocommerce_package_rates', 'filter_woocommerce_package_rates', 100, 2 );