I want to show a condictional unit price on cart line ..
if the payment method is X(bacs) (auto have added discount coupom) I have to take all %porcent discount from this coupons (multiple, sometime) and display price with and without discount and if no have coupom display normal price, I am working on this code, but I am so far to get this working ...
add_filter( 'woocommerce_cart_item_price', 'kd_custom_price_message' );
function kd_custom_price_message( $price) {
global $woocommerce, $coupon_html, $coupon, $discount_amount_html;
$applied_coupons = WC()->cart->get_applied_coupons();
$percent_coupons_only = $applied_coupons->discount_type('percent');
if ( in_array( $percent_coupons_only,$applied_coupons, true ) ) {
$total_percent_discount = $applied_coupons->get_amount();
}
}
// Get the chosen payment gateway (dynamically)
$chosen_payment_method = WC()->session->get('chosen_payment_method');
if( $chosen_payment_method == 'bacs'){
$price_to_be_disconted = (($price / 100) * $total_percent_discount)
$discounted_price = ($price - $price_to_be_disconted);
// price with discount (I have to add CSS)
}
$card_label = '<div style="font-size:11px; color:#ada9a9; text-align:center;">3x s/ juros</div>';
// split payment 3x label
$discounted_price_label = '<div style="font-size:11px; color:green; text-align:center;">ou a vista por:</div>';
// custom sale price label for $discounted_price
return $card_label.'<div style="text-align:center;">'.$price.'</div>'.$discounted_price_label. $discounted_price ;
}
Related
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.
I'm trying to create a shortcode for a single product page that shows the average sale price of the current product. So the shortcode will be in a description of a product page.
This value should work both with simple and variable products.
I'm able to get average value for simple product but only when there is one item in order and I'm not able to solve this with variable products.
function avg_sales_price () {
global $product;
if ( is_a($product, 'WC_Product') ) {
$product_id = $product->get_id();
$orders = wc_get_orders( array(
'numberposts' => -1,
'post_type' => 'shop_order',
) );
foreach ( $orders as $order ) {
if ( count( $order->get_items() ) > 0 ) {
foreach ( $order->get_items() as $item_id => $item )
$productid = $item->get_variation_id() ? $item->get_variation_id() : $item->get_product_id();
if ( $productid == $product_id) {
$product_price_in_orders += $item->get_total();
}
}
}
$count_orders = 0;
foreach ( $orders as $order ) {
$has_product = false;
foreach ( $order->get_items() as $item_values )
if ( $item_values['product_id'] == $product_id )
$has_product = true;
if ( $has_product )
$count_orders++;
}
$avg_sales_price = round( $product_price_in_orders / $count_orders );
echo '<div class="sales-price-number">
<span class="sales-price-text">Average Sale Price </span>
<span class="sales-price-value">' . esc_html( get_woocommerce_currency_symbol() . $avg_sales_price ). ' </span>
</div>';
}
}
Any ideas how to get this work?
Thanks.
To get orders based on current product you shouldn't have to query all orders every time. It takes up too many resources especially if the orders are many.
From Get all Orders IDs from a product ID in Woocommerce answer code you can use the function get_orders_ids_by_product_id() to get all orders from a specific product Id (where you will have to set the desired order statuses).
Then you can use this other function to calculate the average price of each product (simple or variation) based on the product id.
// calculate the average price based on the product id using the query within the "get_orders_ids_by_product_id()" function
function calculates_average_price_based_on_product_id( $product_id ) {
$orders = get_orders_ids_by_product_id( $product_id );
$count = count( $orders );
if ( $count <= 0 ) {
return; // set the return value in case there are no orders for this product
}
$prices = 0;
foreach ( $orders as $order_id ) {
$order = wc_get_order( $order_id );
if ( $order ) {
foreach ( $order->get_items() as $item ) {
$product = $item->get_product();
if ( $product_id == $product->get_id() ) {
$qty = $item->get_quantity();
$total = $item->get_total();
$prices += $total / $qty;
}
}
}
}
return $prices / $count;
}
HOW DOES IT WORK
If the product is variable:
Calculate the average price ONLY between the product variations that have been purchased
If no variation has been purchased, it calculates the average price between the net prices currently set
If the product is simple:
Calculate the average price based on the selling prices across all orders containing this product
If the product has never been purchased, it displays the currently set net price
SHORTCODE
Finally here is the shortcode which will show the average price based on the product of the current page. If the product is variable, the average price between all variations will be calculated and shown.
The [average_price_product] shortcode will only work on the product page.
// create a shortcode showing the average price of the product
add_shortcode( 'average_price_product', 'shortcode_average_price_product' );
function shortcode_average_price_product() {
global $product;
// initialize prices
$avg_price = 0;
$current_avg_price = 0;
// initializes the number of variations that have been purchased at least once
$count = 0;
// if the product is variable it calculates the average price of all the variations
if ( $product->is_type( 'variable' ) ) {
$variation_ids = $product->get_children();
// gets the total number of variations
$total_variations = count( $variation_ids );
foreach ( $variation_ids as $variation_id ) {
$variation = wc_get_product( $variation_id );
// gets the average price of each single variation
$avg_price_variation = calculates_average_price_based_on_product_id( $variation_id );
// if the average price of the current product variation is greater than zero, it updates the price and count
if ( $avg_price_variation > 0 ) {
$avg_price += $avg_price_variation;
$count++;
}
// sum the current price of each single variation (in case no variation has ever been purchased)
$current_avg_price += $variation->get_price();
}
// if no variation has been purchased, I display the average price between the current net prices
if ( $avg_price == 0 ) {
$avg_price = $current_avg_price / $total_variations;
} else {
$avg_price /= $count;
}
// if the product is simple
} else {
// gets the average price of the product
$avg_price = calculates_average_price_based_on_product_id( $product->get_id() );
// if the simple product has never been ordered, I will display the current price
if ( $avg_price == 0 ) {
$avg_price = $product->get_price();
}
}
// creates the HTML content to display on the product page
$html = '<div class="sales-price-number"><span class="sales-price-text">Average Sale Price </span><span class="sales-price-value">' . wc_price( $avg_price ) . ' </span></div>';
return $html;
}
HOOK
As an alternative to the shortcode you can add the average price on the product page using the woocommerce_before_add_to_cart_form hook, with the following function:
// shows the average price on the product page
add_action( 'woocommerce_before_add_to_cart_form', 'shows_average_price_on_the_product_page', 10 );
function shows_average_price_on_the_product_page() {
global $product;
// initialize prices
$avg_price = 0;
$current_avg_price = 0;
// initializes the number of variations that have been purchased at least once
$count = 0;
// if the product is variable it calculates the average price of all the variations
if ( $product->is_type( 'variable' ) ) {
$variation_ids = $product->get_children();
// gets the total number of variations
$total_variations = count( $variation_ids );
foreach ( $variation_ids as $variation_id ) {
$variation = wc_get_product( $variation_id );
// gets the average price of each single variation
$avg_price_variation = calculates_average_price_based_on_product_id( $variation_id );
// if the average price of the current product variation is greater than zero, it updates the price and count
if ( $avg_price_variation > 0 ) {
$avg_price += $avg_price_variation;
$count++;
}
// sum the current price of each single variation (in case no variation has ever been purchased)
$current_avg_price += $variation->get_price();
}
// if no variation has been purchased, I display the average price between the current net prices
if ( $avg_price == 0 ) {
$avg_price = $current_avg_price / $total_variations;
} else {
$avg_price /= $count;
}
// if the product is simple
} else {
// gets the average price of the product
$avg_price = calculates_average_price_based_on_product_id( $product->get_id() );
// if the simple product has never been ordered, I will display the current price
if ( $avg_price == 0 ) {
$avg_price = $product->get_price();
}
}
// creates the HTML content to display on the product page
$html = '<div class="sales-price-number"><span class="sales-price-text">Average Sale Price </span><span class="sales-price-value">' . wc_price( $avg_price ) . ' </span></div>';
echo $html;
}
The code has been tested and works. Add it to your active theme's functions.php.
Advice
Although the code above works correctly this is not the best way to get the average price of the product.
You could create and update a custom meta for each product whenever the status of each order changes, for example, to "wc-completed".
You will only need a counter of the number of orders for that product and the average price calculated based on the last completed order.
When a new order goes to the completed status you will update the respective fields.
How Can ı set this situation ?
How can i add minimum order limit to flat rate product by zone ?
we can only free shipping products.
You can use the following for this, based on the zone id
function zone_based_minimum_amount() {
// Only run it in Cart or Checkout pages
if( is_cart() || ( is_checkout() && ! is_wc_endpoint_url() ) ) {
// HERE below your targeted zone IDs
$targeted_zones_ids = array( 1, 2 );
$chosen_methods = WC()->session->get( 'chosen_shipping_methods' ); // The chosen shipping mehod
$chosen_method = explode(':', reset($chosen_methods) );
$shipping_zone = WC_Shipping_Zones::get_zone_by( 'instance_id', $chosen_method[1] );
$zone_name = $shipping_zone->get_zone_name(); // Get the zone name
$zone_id = $shipping_zone->get_id(); // Get the zone ID
// Total (before taxes and shipping charges)
$total = WC()->cart->subtotal;
// HERE Set minimum cart total amount
$min_total = 100;
// Add an error notice is cart total is less than the minimum required
if( $total <= $min_total && in_array( $zone_id, $targeted_zones_ids ) ) {
// Display an error message
wc_add_notice( '<strong>' . sprintf(
__("A minimum total purchase amount of %s is required to checkout."),
wc_price($min_total)
) . '<strong>', 'error' );
// Removing the Proceed to checkout button from the Cart page
remove_action( 'woocommerce_proceed_to_checkout', 'woocommerce_button_proceed_to_checkout', 20 );
}
}
}
add_action( 'woocommerce_check_cart_items', 'zone_based_minimum_amount', 10, 0 );
In our woocommerce shop , customer can enter the custom width and height of the product and product price calculated based on this details .
For example if the initial price for a product is 50 . And customer add width =2, height=3 , then the price for this product is going to 50*2*3=300
for this we are using following code
// Save custom field value in cart item as custom data
add_filter( 'woocommerce_add_cart_item', 'calculate_custom_cart_item_prices', 30, 3 );
function calculate_custom_cart_item_prices( $cart_item_data, $product_id, $variation_id ) {
if ( isset($_POST['width']) && isset($_POST['height']) ) {
// Get the correct Id to be used (compatible with product variations)
$the_id = $variation_id > 0 ? $variation_id : $product_id;
$product = wc_get_product( $the_id ); // Get the WC_Product object
$product_price = (float) $product->get_price(); // Get the product price
// Get the posted data
$width = (float) sanitize_text_field( $_POST['width'] );
$height = (float) sanitize_text_field( $_POST['height'] );
$new_price = $width * $height * $product_price; // Calculated price
$cart_item_data['calculated-price'] = $new_price; // Save this price as custom data
}
return $cart_item_data;
}
// Set custom calculated price in cart item price
add_action( 'woocommerce_before_calculate_totals', 'set_calculated_cart_item_price', 20, 1 );
function set_calculated_cart_item_price( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// Loop through cart items
foreach ( $cart->get_cart() as $cart_item ){
if( ! empty( $cart_item['calculated-price'] ) ){
// Set the calculated item price (if there is one)
$cart_item['data']->set_price( $cart_item['calculated-price'] );
}
}
And it is wormking , but the problem is:
when customer apply a 50% coupon code for this product then the
discount is coming as 25 , because it calculating based on 50*(50/100)=25;
But actually product new price is 300, so the discount should be
300*(50/100)=150;
Try updating your 'calculate_custom_cart_item_prices' function to something like this and see if that helps.
add_filter( 'woocommerce_add_cart_item', 'calculate_custom_cart_item_prices', 30, 2 );
function calculate_custom_cart_item_prices( $cart_item_data, $cart_item_key ) {
if ( isset($_POST['width']) && isset($_POST['height']) ) {
// Get the correct Id to be used (compatible with product variations)
$the_id = $cart_item_data['variation_id'] > 0 ? $cart_item_data['variation_id'] : $cart_item_data['product_id'];
$product = wc_get_product( $the_id ); // Get the WC_Product object
$product_price = (float) $product->get_price(); // Get the product price
// Get the posted data
$width = (float) sanitize_text_field( $_POST['width'] );
$height = (float) sanitize_text_field( $_POST['height'] );
$new_price = $width * $height * $product_price; // Calculated price
$cart_item_data['calculated-price'] = $new_price; // Save this price as custom data
}
return $cart_item_data;
}
My guess as to what is happening is a change in Woocommerce has changed the way the 'woocommerce_add_cart_item' filter works and so you need to update this function.
I have created a Woocommerce coupon with a discount type set to fixed cart discount and an initial coupon amount.
I want the coupon to function in such a way that when the customer enters the coupon code the total discount is computed and is set as the coupon amount. I'm using the woocommerce_applied_coupon hook in the theme's function.php.
Here's how I coded:
add_action( 'woocommerce_applied_coupon', 'action_woocommerce_applied_coupon', 10, 3 );
function action_woocommerce_applied_coupon( $array, $int, $int ){
$total_discount = 0;
$wc = wc();//use the WC class
foreach($wc->cart->get_cart() as $cart_item){
//loop through each cart line item
$total_discount += ...; //this is where the total discount is computed
}
//use the WC_COUPON class
$wc_coupon = new WC_Coupon("coupon-code");// create an instance of the class using the coupon code
$wc_coupon->set_amount($total_discount);//set coupon amount to the computed discounted price
var_dump($wc_coupon->get_amount());//check if the coupon amount did update
}
The var_dump displayed the $total_discount. But when I checked on the Cart Totals, I still see the initial coupon amount as the discount.
How do I get to update the coupon amount and apply this as the discount to the cart totals?
Try this
add_action( 'woocommerce_before_calculate_totals', 'auto_add_coupons_total_based', 10, 1 ); function auto_add_coupons_total_based( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// HERE define your coupon code
$coupon_percent = 'xyz20'; # <=== <=== <=== <=== <=== <===
$coupon_fixed = 'fixedamount'; # <=== <=== <=== <=== <=== <=== <===
// Get cart subtotal
$subtotal = 0;
foreach($cart->get_cart() as $cart_item ){
$subtotal += $cart_item['line_subtotal'];
$subtotal += $cart_item['line_subtotal_tax']; // with taxes
}
//Set HERE the limit amount
$limit = 40; //without Tax
// Coupon type "percent" (less than 200)
if( $subtotal < 200 && ! $cart->has_discount( $coupon_percent ) ){
// If coupon "fixed amount" type is in cart we remove it
if( $cart->has_discount( $coupon_fixed ) )
$cart->remove_coupon( $coupon_fixed );
}
// Coupon type "fixed amount" (Up to 200)
if( $subtotal >= 200 && $cart->has_discount( $coupon_percent ) ) {
// If coupon "percent" type is in cart we remove it
if( $cart->has_discount( $coupon_percent ) )
$cart->remove_coupon( $coupon_percent );
// Apply the "fixed amount" type coupon code
$cart->add_discount( $coupon_fixed );
// Displaying a custom message
$message = __( "The total discount limit of $$limit has been reached", "woocommerce" );
wc_add_notice( $message, 'notice' );
}
}