On my WooCommerce site I use this function to add a 4€ fee if you select cash on delivery as payment method:
add_action('woocommerce_cart_calculate_fees', 'increase_cod_cost');
function increase_cod_cost() {
if(WC()->session->chosen_payment_method=='cod')
WC()->cart->add_fee(__('COD Fee'), 4);
}
I also use this function to immediately update the checkout whenever you change payment method so that the fee is added when you select cod, and removed when you select any other method:
add_action('woocommerce_review_order_before_payment', 'custom_checkout_update');
function custom_checkout_update() {
echo '
<script type="text/javascript">
(function($){
$(\'form.checkout\').on(\'change\', \'input[name="payment_method"]\', function() {
$(\'body\').trigger(\'update_checkout\');
});
})(jQuery);
</script>
';
}
Both functions work 100%.
Now, Instead of adding a fee, I'd like to properly increase the actual shipping cost, so I've tried this function instead of the first one:
add_filter('woocommerce_package_rates', 'increase_cod_cost_2', 10, 2);
function increase_cod_cost_2($rates, $package) {
if(WC()->session->chosen_payment_method=='cod')
$rates['flat_rate:1']->cost=$rates['flat_rate:1']->cost+4;
return $rates;
}
This function also works, but only if I empty the cart and then add a product again. For some reason it doesn't immediately update the shipping cost whenever I select cod. I really don't understand why the jQuery function would work with the first php function by adding the fee, but not with this one by changing the shipping cost. Can you please help me and tell me what's wrong? Thank you.
Please try this.
add_filter( 'woocommerce_package_rates', 'custom_shipping_costs', 20, 2 );
function custom_shipping_costs( $rates, $package ) {
// New shipping cost (can be calculated)
$new_cost = 1000;
$tax_rate = 0.4;
foreach( $rates as $rate_key => $rate ){
// Excluding free shipping methods
if( $rate->method_id != 'free_shipping'){
// 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;
}
I cannot actually help you to fully understand what is going wrong, but I can share you some experience:
Try to include log commands in these cases, it helps you to see when the hook is called, if it is called and depending on your log details if the code went through the if clause or fell into the else case
From experience and what you write, the second hook is not always called, therefore it works when you freshly add a product but not during an shipping method change during checkout.
So I am wildly guessing you probably need another hook like
do_action( 'woocommerce_shipping_method_chosen', $chosen_method );
or something better which triggers whenever woocommerce goes to look for the available shipping methods.
Be careful that the hooks are not called sequentially and you add 4 USD more han 1 time...
PD: I've noticed that the suggested hook is not ideal neither, you could try one of these:
add_action( 'woocommerce_load_shipping_methods', 'action_woocommerce_load_shipping_methods', 10, 1 );
add_filter( 'woocommerce_package_rates', 'woocommerce_package_rates' );
or else check here all the woocommerce hooks:
https://docs.woocommerce.com/wc-apidocs/hook-docs.html
Related
I got a Problem. In Woocommerce I added a custom product type (abo), where the user can choose a area size (in square meters) and there is a price per square meter and a dosage information where it says e.g. "You need 0.165kg per squaremeter" (it´s a fertilizer).
So for example if the user chooses 100 square meters and the price per square meter is 2.30€ and the dosage is 0.165kg/sqm the total price of the abo is 37.95€ and at a tax rate of 10.7% the tax amount should be 4.06€.
My Problem now is, I have normal products in the shop as well, where the standard calculation of the price and tax etc. works perfectly fine and you can buy the abo and a normal product.
So my question is, how can I recalculate the tax amount if I got an abo in cart?
I tried a few things for testing so far:
Adding a custom fee (that isn´t what I´m looking for, cause the tax has to be the same on cart, checkout and receipt):
add_action( 'woocommerce_cart_calculate_fees','custom_tax_surcharge_for_swiss', 10, 1 );
function custom_tax_surcharge_for_swiss( $cart ) {
if(current_user_can('administrator')) :
$percent = 10.7;
// Calculation
$surcharge = ( $cart->cart_contents_total + $cart->shipping_total ) * $percent / 100;
// Add the fee (tax third argument disabled: false)
$cart->add_fee( __( 'TAX', 'woocommerce')." ($percent%)", $surcharge, false );
endif;
}
calc_tax function (I think this one is the one I need) :
/ define the woocommerce_calc_tax callback
function filter_woocommerce_calc_tax( $taxes, $price, $rates, $price_includes_tax, $suppress_rounding ) {
if(current_user_can('administrator')) :
foreach(WC()->cart->cart_contents as $item) :
if($item['data']->get_type() == 'abo') :
$qty = $item['quantity'];
$ppqm = str_replace(',', '.', get_post_meta($item['product_id'], 'abo_product_price_per_kg')[0]);
$kgpqm = str_replace(',', '.', get_post_meta($item['product_id'], 'abo_product_kg_qm')[0]);
$realQty = floatval($kgpqm) * floatval($qty);
$priceCustom = $priceCustom + $realQty * floatval($ppqm);
endif;
endforeach;
return $taxes;
};
// add the filter
add_filter( 'woocommerce_calc_tax', 'filter_woocommerce_calc_tax', 10, 5 );
With the above function I got the problem, that I don´t have the cart contents directly so it could be, that the order of the items isn´t the same as the Tax order which I get from the function.
I would also note, that the woocommerce_calc_tax fires twice. I had for debug reasons an output in the console and if I have 2 items in cart, I got 4 outputs.
Hope my Problem is clear and that anyone could help me with that problem.
Thanks in advance.
I would look into the tax class for WooCommerce. You can specific target a item with a different tax rate. Unless I'm missing something this should actually work how you need it to out of the box.
When checking out it will also separate the taxes.
I am trying to remove the free shipping option in Woocommerce when someone uses a specific coupon code. I found this question which is very relevant to my question. The answer bellow seems really close to what I am looking for. I am new to php and trying to figure out where I would add an id for the coupon code I want to exclude.
add_filter( 'woocommerce_shipping_packages', function( $packages ) {
$applied_coupons = WC()->session->get('applied_coupons', array());
if (!empty($applied_coupons)) {
$free_shipping_id = 'free_shipping:2';
unset($packages[0]['rates'][ $free_shipping_id ]);
}
return $packages;
});
This is my first question on stack over flow but this site has helped me so much with so many issues. Forgive me if I am asking too much. Thanks in advance for any help/guidance.
Use instead woocommerce_package_rates filter hook. In the code below you will set the related coupon codes that will hide the free shipping method:
add_filter( 'woocommerce_package_rates', 'hide_free_shipping_method_based_on_coupons', 10, 2 );
function hide_free_shipping_method_based_on_coupons( $rates, $package )
{
$coupon_codes = array('summer'); // <== HERE set your coupon codes
$applied_coupons = WC()->cart->get_applied_coupons(); // Applied coupons
if( empty($applied_coupons) )
return $rates;
// For specific applied coupon codes
if( array_intersect($coupon_codes, $applied_coupons) ) {
foreach ( $rates as $rate_key => $rate ) {
// Targetting "Free shipping"
if ( 'free_shipping' === $rate->method_id ) {
unset($rates[$rate_key]); // hide free shipping method
}
}
}
return $rates;
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
Clearing shipping caches:
You will need to empty your cart, to clear cached shipping data
Or In shipping settings, you can disable / save any shipping method, then enable back / save.
i just implemented a custom shipping solution.
It depends on the total price of items in the cart. For example:
if total is < 20 -> display free shipping
if total is >= 20 -> paid delivery
But i have a problem with woocommerce cache...I think that the fact is that woocommerce caches shipping rates, not taking in account order qty change.
So is the problem is with the calculate_shipping_for_package() method?
If I enable shipping debug mode everything works just right, without it got no updates.
I tried to disable cache, without success with.
add_action('woocommerce_checkout_update_order_review', function() {
$packages = WC()->cart->get_shipping_packages();
foreach ($packages as $key => $value) {
$shipping_session = "shipping_for_package_$key";
unset(WC()->session->$shipping_session);
}
}, 10, 2);
So. Do you ever had this kind of problem? How did you solve? Thanks a lot for any help!
Updated: There is some mistakes in your code, instead try the following:
add_action('woocommerce_checkout_update_order_review', 'checkout_update_refresh_shipping_methods', 10, 1);
function checkout_update_refresh_shipping_methods( $post_data ) {
$packages = WC()->cart->get_shipping_packages();
foreach ($packages as $package_key => $package ) {
WC()->session->set( 'shipping_for_package_' . $package_key, false ); // Or true
}
}
Code goes in function.php file of your active child theme (active theme). Tested and works.
But it will refresh shipping methods cache each time on ajax checkout update event.
Related: Custom checkout field and shipping methods ajax interaction in Woocommerce 3
I am running into issues with the cart total only displaying 0
Essentially what I am trying to do is only accept a deposit total of a certain amount after all cart items have been added to the carts subtotal.
So for example if the customer adds $100 worth of items, they would only pay $10 initially or (10%) of the subtotal as the total value.
I took the code from here: Change total and tax_total Woocommerce and customize it this way:
add_action('woocommerce_cart_total', 'calculate_totals', 10, 1);
function calculate_totals($wc_price){
$new_total = ($wc_price*0.10);
return wc_price($new_total);
}
But the total amount shows 0.00 when that code is enabled. If removed the code, I get the standard total.
I also could not find on the woocommerce site where the full api is listed, only generic articles related to how to create a plugin.
Any help or a point in the right direction would be great.
This does not answer this question. Loic's does. This is another way of doing it to show a line item of 10% off:
function prefix_add_discount_line( $cart ) {
$discount = $cart->subtotal * 0.1;
$cart->add_fee( __( 'Down Payment', 'yourtext-domain' ) , -$discount );
}
add_action( 'woocommerce_cart_calculate_fees', 'prefix_add_discount_line' );
Since Woocommerce 3.2+
it does not work anymore with the new Class WC_Cart_Totals ...
New answer: Change Cart total using Hooks in Woocommerce 3.2+
First woocommerce_cart_total hook is a filter hook, not an action hook. Also as wc_price argument in woocommerce_cart_total is the formatted price, you will not be able to increase it by 10%. That's why it returns zero.
Before Woocommerce v3.2 it works as some WC_Cart properties can be accessed directly
You should better use a custom function hooked in woocommerce_calculate_totals action hook this way:
// Tested and works for WooCommerce versions 2.6.x, 3.0.x and 3.1.x
add_action( 'woocommerce_calculate_totals', 'action_cart_calculate_totals', 10, 1 );
function action_cart_calculate_totals( $cart_object ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( !WC()->cart->is_empty() ):
## Displayed subtotal (+10%)
// $cart_object->subtotal *= 1.1;
## Displayed TOTAL (+10%)
// $cart_object->total *= 1.1;
## Displayed TOTAL CART CONTENT (+10%)
$cart_object->cart_contents_total *= 1.1;
endif;
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
Is also possible to use WC_cart add_fee() method in this hook, or use it separately like in Cristina answer.
I am currently working on an online shop with WooCommerce. I faced the problem that I want to grant a discount to customers who chose a specific shipping method. The discount is 0.50 for every single product. I basically solved this problem with the following code in my "functions.php":
add_action('woocommerce_before_calculate_totals', 'woo_add_cart_fee');
function woo_add_cart_fee() {
global $woocommerce;
$cart = $woocommerce->cart->get_cart();
//Calculating Quantity
foreach ($cart as $cart_val => $cid) {
$qty += $cid['quantity'];
}
if ($woocommerce->cart->shipping_label == "specific shipping method") {
$woo_fee = $qty * (-0.5);
$woo_name = "discount for specific shipping method";
}
$woocommerce->cart->add_fee(__($woo_name, 'woocommerce'), $woo_fee, true);
}
The code technically works, the only problem I have now is that if a customer changes the shipping method i.e. from the "specific shipping method" to a "normal one" (without any discount) or the other way round, it always displays and calculates the discount value from the previously chosen shipping method. In other words it is always one step back and therefore displays the customer the wrong total amount.
Does anyone has an idea to solve this problem?
This solutions is for Woocommerce 2.1.X!
I am not sure if this might help. I was facing a similar problem, where I needed to retrieve the chosen shipping method. In the file \wp-content\plugins\woocommerce\includes\wc-cart-functions.php I found a method called wc_cart_totals_shipping_html().
Within this method there is a check of the current selected shipping method that contains the following code:
$packages = WC()->shipping->get_packages();
foreach ( $packages as $i => $package ) {
$chosen_method = isset( WC()->session->chosen_shipping_methods[ $i ] ) ? WC()->session->chosen_shipping_methods[ $i ] : '';
}
I used this code in my own functions.php to check for the currently selected shipping method and it works. Example:
add_filter( 'woocommerce_billing_fields', 'wc_change_required_fields');
function wc_change_required_fields($address_fields) {
$packages = WC()->shipping->get_packages();
foreach ( $packages as $i => $package ) {
$chosen_method = isset( WC()->session->chosen_shipping_methods[ $i ] ) ? WC()->session->chosen_shipping_methods[ $i ] : '';
}
if ($chosen_method == 'local_delivery') {
$address_fields['billing_address_1']['required'] = true;
// place your changes that depend on the shipping method here...
}
}
Hope that helps!
This is very old, but I ran into this issue myself and had to work out the solution.
Woocommerce stores pre-calculated cart totals in the database, rather than calculating them on the fly. But the shipping method choice is stored as a session variable. So shipping changes are not reflected immediately at checkout without a visit or refresh of a cart page.
With the original posted code, the shipping changes were not reflected because they aren't recalculated and stored. To do this, the function needs to be tricked into thinking it's a cart page first, and then recalculating the totals to be stored.
GLOBAL $woocommerce;
if ( ! defined('WOOCOMMERCE_CART') ) {
define( 'WOOCOMMERCE_CART', true );
}
And then at the end of the function, after all the desired changes have been made refresh and store.
WC()->cart->calculate_totals();
See also CODEX for WC_AJAX::update_shipping_method()
http://docs.woothemes.com/wc-apidocs/source-class-WC_AJAX.html#148-174
Mark's answer worked for me, however I had to delete all transient values prior to running the code. Otherwise, it would simply restore the saved values.
public function clear_shipping_transients() {
global $wpdb;
$wpdb->query( "DELETE FROM `$wpdb->options` WHERE `option_name` LIKE ('_transient_cp_quote_%') OR `option_name` LIKE ('_transient_timeout_cp_quote_%') OR `option_name` LIKE ('_transient_wc_ship_%')" );
}