Shipping Rates based on Product Quantity on woocommerce - wordpress

I want to set shipping cost quantity wise on my woocommerce theme. I want to do this option :
For 1 to 5 products shiping cost will be 15%.
More than 5 products whipping cost will be $6.99 .
Can i add this shipping option without a plugin ?

You need to hook a function to woocommerce_calculate_totals action which is triggered right before calculating the final cart total. The woocommerce_calculate_totals action provides the WC_Cart instance, on which you can perform manipulation as per your requirement.
add_action('woocommerce_calculate_totals', 'modify_shipping_totals');
function modify_shipping_totals($cart) {
if($cart->get_cart_contents_count() < 6) {
$cart->shipping_total = ( 15/100 ) * $this->cart_contents_total;
// shipping cost will be 15% of cart content total
// you may also want to modify the shipping tax.
$cart->shipping_tax_total = 0;
} else {
$cart->shipping_total = 6.99;
$cart->shipping_tax_total = 0;
}
}
For further reference regarding changable variables refer to WC_Cart documentation.

// **Note**: This code is working only when you set Flat rate Settings
// cost value is 1
add_filter( 'woocommerce_package_rates', 'custom_package_rates', 10, 2 );
function custom_package_rates( $rates, $packages ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;
$cart_count = WC()->cart->get_cart_contents_count();
$cart_total = WC()->cart->cart_contents_total;
foreach($rates as $rate_key => $rate_values ) {
$method_id = $rate_values->method_id;
$rate_id = $rate_values->id;
if( $method_id == 'flat_rate' ){
if( $cart_count < 99 ){
$flat_rate_value = 4.95; //"Applay Flat rate less then 99 quatity"
$cart_10_percent = 0; // No percent discount
}
if( $cart_count > 99 ){
$flat_rate_value = 9.95; // "Applay Flat rate greater then 99 quatity"
$cart_10_percent = 0; // No percent discount
}
$rate_cost = $flat_rate_value > $cart_10_percent ? $flat_rate_value - $cart_10_percent : 0;
// Set the new calculated rate cost
$rates[$rate_id]->cost = number_format( $rates[$rate_id]->cost * $rate_cost, 2 );
}
}
return $rates;
}

The Pranav solution works if you call the add_action inside the init hook, like this:
function init_shop(){
add_action('woocommerce_calculate_totals', 'modify_shipping_totals', 10);
}
add_action( 'init', 'init_shop');

Related

Cart discount for product that cost less in WooCommerce unless that product is already on sale

I want to add a 30% discount on the cheapest item in the cart, except if it already has a discount.
Based on Cart discount for product that cost less in Woocommerce answer code, this is my code attempt:
add_action('woocommerce_cart_calculate_fees', 'discount_on_cheapest_cart_item', 20, 1 );
function discount_on_cheapest_cart_item( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// Only for 2 items or more
if ( $cart->get_cart_contents_count() < 2 ) return;
// Initialising
$percentage = 50; // 10 %
$discount = 0;
$item_prices = array();
// Loop though each cart items and set prices in an array
foreach ( $cart->get_cart() as $cart_item ) {
$product_prices_excl_tax[] = wc_get_price_excluding_tax( $cart_item['data'] );
}
sort($product_prices_excl_tax);
if( ! $cart_item['data']->is_on_sale() ){
$discount = reset($product_prices_excl_tax) * $percentage / 100;
$cart->add_fee( "Discount on cheapest (".$percentage."%)", -$discount );
}
}
Is there a way to make it work so if the product with the lowest price is not on sale, then apply a 30%, if it is on sale, don't. But this applies only to the lowest, if any other product is on sale, we skip it.
The use of if( ! $cart_item['data']->is_on_sale() ) in your code attempt
will not be of any influence as this is used outside the foreach loop.
This anwer will apply a discount calculated on the basis of the product with the lowest price, this only if this product is not already on sale.
So you get:
function action_woocommerce_cart_calculate_fees( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// Only for 2 items or more
if ( $cart->get_cart_contents_count() < 2 ) return;
// Setting
$percentage = 30; // 30 %
// Initialize
$discount = 0;
$product_prices_on_sale = array();
$product_prices_excl_tax = array();
// Loop though each cart items and set prices in an array
foreach ( $cart->get_cart() as $cart_item ) {
// When cart contains a on sale product
if ( $cart_item['data']->is_on_sale() ) {
// On sale, push to on sale array
$product_prices_on_sale[] = wc_get_price_excluding_tax( $cart_item['data'] );
}
// Push to excl tax array
$product_prices_excl_tax[] = wc_get_price_excluding_tax( $cart_item['data'] );
}
// Sort an array in ascending order
sort( $product_prices_on_sale );
sort( $product_prices_excl_tax );
// Set the internal pointer of an array to its first element
$p_o_s = reset( $product_prices_on_sale );
$p_e_t = reset( $product_prices_excl_tax );
// NOT equal
if ( $p_o_s != $p_e_t ) {
// Calculate discount
$discount = $p_e_t * $percentage / 100;
// Apply discount (negative fee)
$cart->add_fee( 'Discount on cheapest (' . $percentage . '%)', -$discount );
}
}
add_action( 'woocommerce_cart_calculate_fees', 'action_woocommerce_cart_calculate_fees', 10, 1 );

Discount amount not correct for woocommerce_coupon_get_discount_amount

I am trying to discount the cheapest item in the cart if my coupon type is used:
add_filter('woocommerce_coupon_get_discount_amount', 'wc_cpn_disc', 10, 5);
function wc_cpn_disc($discount, $discounting_amount, $cart_item, $single, $coupon) {
// IF TYPE MATCHES PERFORM CUSTOM CALCULATION
if ($coupon->type == 'cheapest_free'){
global $woocommerce;
foreach ( $woocommerce->cart->get_cart() as $cart_item_key => $values ) {
$_product = $values['data'];
$product_price[] = get_option('woocommerce_tax_display_cart') == 'excl' ? $_product->get_price_excluding_tax() : $_product->get_price_including_tax(); /*Store all product price from cart items in Array */
}
$lowestprice = min($product_price);
$discount = number_format((float)$lowestprice/10,2,'.','');
}
return $discount;
}
The discount amount is very weird - no matter what I try, it never comes out to the value I expect. At first I thought it was a percentage discount, but I expect this to be a fixed amount. I have tried running my get lowest price bit of function elsewhere on the site and it returns 1.195 when the lowest value item is 11.95 - so I know that part works. But the discount on a total basket of 265.60 is 23.90 - I just don't get it!
I just want to get the lowest priced item in the cart, and discount that amount.
I managed to solve this with the help of Bossman and a couple of other threads. It turns out I needed to change my method - full code below.
add_filter('woocommerce_coupon_get_discount_amount', 'tfcc_cheapest_free', 10, 5);
function tfcc_cheapest_free($discount, $discounting_amount, $cart_item, $single, $coupon) {
// IF TYPE MATCHES PERFORM CUSTOM CALCULATION
if ($coupon->type == 'cheapest_free'){
$items_prices = [];
$items_count = 0;
// Loop through cart items
foreach( WC()->cart->get_cart() as $key => $item ){
// Get the cart item price (the product price)
if ( wc_prices_include_tax() ) {
$price = wc_get_price_including_tax( $item['data'] );
} else {
$price = wc_get_price_excluding_tax( $item['data'] );
}
if ( $price > 0 ){
$items_prices[$key] = $price;
$items_count += $item['quantity'];
}
}
// Only when there is more than one item in cart
if ( $items_count > 1 ) {
asort($items_prices); // Sorting prices from lowest to highest
$item_keys = array_keys($items_prices);
$item_key = reset($item_keys); // Get current cart item key
// Targeting only the current cart item that has the lowest price
if ( $cart_item['key'] == $item_key ) {
return reset($items_prices)/$cart_item['quantity']; // return the lowest item price as a discount
}
} else {
return 0;
}
}
}
I hope this might help someone else out who needs a similar feature in the future.

WooCommerce: Calculate backorder products shipping cost

I need to calculate the extra shipping cost the backorder products will have. I first thought of duplicate the shipping cost per product, but this is not accurate. So I thought I should withing my function run another function (same as woocommerce uses) to calculate the shipping cost for each and all backorder product in the cart.
Here is where I am so far.
// Add a extra shipping fee to each backordered product
add_action('woocommerce_cart_calculate_fees', 'add_backorder_shipping_fee', 20, 1);
function add_backorder_shipping_fee($cart)
{
if (is_admin() && !defined('DOING_AJAX'))
return;
$backorderNumber = 0;
// Loop through the cart items (added products).
foreach (WC()->cart->get_cart() as $cart_item) {
// Product Info
$product = $cart_item['data'];
// Quantity of product in cart (being purchased).
$buyingQuantity = $cart_item['quantity'];
if (!empty($product && $product->backorders_allowed())) {
// Calculate if product quantity in cart is more than stock, returns negative if so.
$isBuyingMoreThanStock = $product->stock_quantity - $buyingQuantity;
if ($isBuyingMoreThanStock < 0) {
//!ITEMS: Calculate the number of items are being backordered.
// $backorderNumber = $backorderNumber + ($isBuyingMoreThanStock * -1);
//!PRODUCTS: Calculate the number of products are being backordered.
$backorderNumber = $backorderNumber + 1;
}
}
}
// Get the shipping cost.
$totalShippingCost = WC()->cart->get_shipping_total() + WC()->cart->get_shipping_tax();
// Calculate and apply the above shipping cost to each backordered item.
$extraShippingFee = $backorderNumber * $totalShippingCost;
// Create the fee.
if ($backorderNumber > 0 && $totalShippingCost > 0) {
$cart->add_fee(__('Backorder Extra Shipping Fee (' . $backorderNumber . ')', 'woocommerce'), $extraShippingFee);
}
}
So instead of get the current shipping cost and multiple by the number of backordered product, I would like to calculate the extra fee just like woocommerce calculate the shipping cost. Another thing that comes to mind is if I could apply this to Fedex live rate API as well.
I suggest an alternative solution based on WooCommerce shipping fee calculate method.
// Calculate and sum all backorder product shipping fees
add_filter( 'woocommerce_shipping_packages', function ( $packages ) {
foreach ( $packages as $package_key => &$package ){
$backorders = array();
foreach ( $package['contents'] as $cartkey => $values ){
if ( $values['data']->is_on_backorder() ){
if ( empty($i) ) $i = 1; else $i++;
$backorders[ $i ][ $cartkey ] = $values; // Get backorders
} else {
$backorders[ 0 ][ $cartkey ] = $values; // Get NOT backorders
}
}
if ( count( $backorders ) <= 1 ) continue; // Break if no backorder
$tax_rates = WC_Tax::get_shipping_tax_rates();
$new_packages = array();
foreach ( $backorders as $i => $backorder ){
$new_packages[ $i ] = $package;
$new_packages[ $i ]['contents'] = $backorder;
$new_packages[ $i ]['contents_cost'] = array_sum( wp_list_pluck( $backorder, 'line_total' ) ); // calculate products total
$new_packages[ $i ] = WC()->shipping()->calculate_shipping_for_package( $new_packages[ $i ] ); // calculate shipping cost
}
foreach ( $package['rates'] as $ratekey => &$rate ){
$rate->cost = 0;
foreach ( $new_packages as $i => $new_package ){
if ( empty( $new_package['rates'][$ratekey] ) ) continue;
$rate->cost += $new_package['rates'][$ratekey]->cost; // sum shipping costs
}
$rate->taxes = WC_Tax::calc_tax( $rate->cost, $tax_rates, true ); // re-calculate taxes
$rate->label = 'Shipped in order'; // change shipping label if you want
}
}
return $packages;
});
The advantage of this method is that it also supports shipping calculation using shipping classes such as flat_rate and table_rate.

Get cost value of chosen shipping method at checkout of Woocommerce

In functions.php of my child theme I have custom script like this:
add_action('woocommerce_checkout_process', 'my_custom_checkout_field_process1');
function my_custom_checkout_field_process1() {
//do something
}
To do some calculations I need to get the cost of chosen shipping method. How to do that?
I got it!
$cart = WC()->cart;
$total = __( 'Free!', 'woocommerce' );
if ( 0 < $cart->get_shipping_total() ) {
if ( $cart->display_prices_including_tax() ) {
$total = $cart->shipping_total + $cart->shipping_tax_total;
} else {
$total = $cart->shipping_total;
}
}

Woocommerce - Extra fee based on product quantity

I found this answer in a earlier post here (below), but I want to know of there is any way to add an extra fee based on quantity if the product quantity changes?
Lets say there is 100 items in one package. (the problem is also that there is not they same amount of item in all packages, some can be 100, some can be 150, 200, or 500)
Example:
1-99 = 1$.
100 = no fee.
101 - 199 1$
200 = no fee
201 - 299 = 1$ and so on..
Total will always be 1$ per product but the total can be more if they order several products that have these breaks. The total can be 4$ if there is 4 products with break-cost.
(Also, not sure where to put the code)
Thank you!
The code I found here: Add additional costs based on quantity in Woocommerce
// Hook before adding fees
add_action('woocommerce_cart_calculate_fees' , 'add_custom_fees');
/**
* Add custom fee on article specifics
* #param WC_Cart $cart
*/
function add_custom_fees( WC_Cart $cart ){
$fees = 0;
foreach( $cart->get_cart() as $item ){
// Check if odds and if it's the right item
if( $item[ 'quantity' ] % 2 == 1 && get_post_meta( $item[ 'product_id' ], 'custom_fee_for_supplier_name', true) ){
// You can also put a custom price in each produt with get_post_meta
$fees += 10;
}
}
if( $fees != 0 ){
// You can customize the descriptions here
$cart->add_fee( 'Custom fee (odds paquets)', $fees);
}
}
The woocommerce_after_cart_item_quantity_update fires right after a quantity is updated. If you modify your function a little bit (to use WC()->cart to access the cart object) you can run the same function on both hooks. I thought it might keep adding additional fees, but in my testing it seems to just recalculate the right cost for the same fee.
add_action('woocommerce_cart_calculate_fees' , 'add_custom_fees');
add_action( 'woocommerce_after_cart_item_quantity_update', 'add_custom_fees' );
/**
* Add custom fee on article specifics
* #param WC_Cart $cart
*/
function add_custom_fees(){
$fees = 0;
foreach( WC()->cart->get_cart() as $item ){
// Check if odds and if it's the right item
if( $item[ 'quantity' ] % 2 == 1 && get_post_meta( $item[ 'product_id' ], '_custom_fee_for_odds', true ) ){
// You can also put a custom price in each produt with get_post_meta
$fees += 10;
}
}
if( $fees > 0 ){
// You can customize the descriptions here
WC()->cart->add_fee( 'Custom fee (odds paquets)', $fees);
}
}

Resources