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.
Related
We want to show a section called "complete the look" in WooCommerce. We are using PureClarity to do that.
PureClarity asked us to extend the WooCommerce feed by adding a code snippet in functions.php to add related peoducts SKUs under RelatedProducts.
We used the following code:
function product_callbackk($data, $product) {
$data['RelatedProducts'] = $product->get_sku() ?? null;
return $data;
}
add_filter( 'pureclarity_feed_get_product_data', 'product_callbackk', 10, 2 );
But it showing only the current product SKU and not the related products SKUs. Any advice?
I don't use PureClarity so I added an example based on a default hook in WooCommerce.
In this I use wc_get_related_products() which allow to obtain related products based on the current product ID.
The product SKU per product can then be obtained via a foreach loop.
So you get:
function action_woocommerce_single_product_summary() {
// Get the global product object
global $product;
// Is a WC product
if ( is_a( $product, 'WC_Product' ) ) {
// Initialize
$skus = array();
// Get product ID
$product_id = $product->get_id();
// Get related products based on product category and tags || second argument = limit of results, default = 5
$related_products = wc_get_related_products( $product_id, 10 );
// Loop through
foreach ( $related_products as $related_product ) {
// Get product
$product = wc_get_product( $related_product );
// Get product SKU
$product_sku = $product->get_sku();
// NOT empty
if ( ! empty( $product_sku ) ) {
// Push to array
$skus[] = $product_sku;
}
}
// Output
echo '<pre>', print_r( $skus, 1 ), '</pre>';
}
}
add_action( 'woocommerce_single_product_summary', 'action_woocommerce_single_product_summary', 10 );
Or in your specific case:
/**
* #param mixed[] $data - the array of data that will be sent to PureClarity
* #param WC_Product $product - the WooCommerce product object
* #return mixed
*/
function filter_pureclarity_feed_get_product_data( $data, $product ) {
// Is a WC product
if ( is_a( $product, 'WC_Product' ) ) {
// Initialize
$skus = array();
// Get product ID
$product_id = $product->get_id();
// Get related products based on product category and tags || second argument = limit of results, default = 5
$related_products = wc_get_related_products( $product_id, 10 );
// Loop through
foreach ( $related_products as $related_product ) {
// Get product
$product = wc_get_product( $related_product );
// Get product SKU
$product_sku = $product->get_sku();
// NOT empty
if ( ! empty( $product_sku ) ) {
// Push to array
$skus[] = $product_sku;
}
}
$data['RelatedProducts'] = $skus;
}
return $data;
}
add_filter( 'pureclarity_feed_get_product_data', 'filter_pureclarity_feed_get_product_data', 10, 2 );
We want to show a section called "complete the look" in WooCommerce. We are using PureClarity to do that.
PureClarity asked us to extend the WooCommerce feed by adding a code snippet in functions.php to add related peoducts SKUs under RelatedProducts.
We used the following code:
function product_callbackk($data, $product) {
$data['RelatedProducts'] = $product->get_sku() ?? null;
return $data;
}
add_filter( 'pureclarity_feed_get_product_data', 'product_callbackk', 10, 2 );
But it showing only the current product SKU and not the related products SKUs. Any advice?
I don't use PureClarity so I added an example based on a default hook in WooCommerce.
In this I use wc_get_related_products() which allow to obtain related products based on the current product ID.
The product SKU per product can then be obtained via a foreach loop.
So you get:
function action_woocommerce_single_product_summary() {
// Get the global product object
global $product;
// Is a WC product
if ( is_a( $product, 'WC_Product' ) ) {
// Initialize
$skus = array();
// Get product ID
$product_id = $product->get_id();
// Get related products based on product category and tags || second argument = limit of results, default = 5
$related_products = wc_get_related_products( $product_id, 10 );
// Loop through
foreach ( $related_products as $related_product ) {
// Get product
$product = wc_get_product( $related_product );
// Get product SKU
$product_sku = $product->get_sku();
// NOT empty
if ( ! empty( $product_sku ) ) {
// Push to array
$skus[] = $product_sku;
}
}
// Output
echo '<pre>', print_r( $skus, 1 ), '</pre>';
}
}
add_action( 'woocommerce_single_product_summary', 'action_woocommerce_single_product_summary', 10 );
Or in your specific case:
/**
* #param mixed[] $data - the array of data that will be sent to PureClarity
* #param WC_Product $product - the WooCommerce product object
* #return mixed
*/
function filter_pureclarity_feed_get_product_data( $data, $product ) {
// Is a WC product
if ( is_a( $product, 'WC_Product' ) ) {
// Initialize
$skus = array();
// Get product ID
$product_id = $product->get_id();
// Get related products based on product category and tags || second argument = limit of results, default = 5
$related_products = wc_get_related_products( $product_id, 10 );
// Loop through
foreach ( $related_products as $related_product ) {
// Get product
$product = wc_get_product( $related_product );
// Get product SKU
$product_sku = $product->get_sku();
// NOT empty
if ( ! empty( $product_sku ) ) {
// Push to array
$skus[] = $product_sku;
}
}
$data['RelatedProducts'] = $skus;
}
return $data;
}
add_filter( 'pureclarity_feed_get_product_data', 'filter_pureclarity_feed_get_product_data', 10, 2 );
This code I wrote displays personalized information on the WooCommerce checkout page.
add_action( 'woocommerce_cart_totals_after_order_total', 'show_total_discount_cart_checkout', 9999 );
add_action( 'woocommerce_review_order_after_order_total', 'show_total_discount_cart_checkout', 9999 );
function show_total_discount_cart_checkout() {
$discount_total = 0;
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$product = $cart_item['data'];
$subtotal = WC()->cart->get_product_subtotal( $product, $cart_item['quantity'] );
$total = WC()->cart->total;
$pctm = 90.00;
$valor_descontado = $total - ($total / 100 * $pctm);
$sale_price = '10%';
$discount = ( WC()->cart->total - $valor_descontado );
$discount_total = $discount;
}
if ( $discount_total > 0 ) {
echo '<tr><th>VOCÊ RECEBERÁ DE CASHBACK:</th><td data-title="You">' . wc_price( $discount_total + WC()->cart->get_discount_total() ) .'</td></tr>';
}
}
The result:
I need this information to also be displayed in WooCommerce orders and emails. I believe I can come up with a solution myself to display this value on several other pages, but can someone first tell me how to save/store the value of this calculation?
First of all, I've rewritten your existing code for the following reasons:
Requesting subtotals and totals is best done outside the foreach loop, because otherwise these values will be overwritten every time
The result of $subtotal is not used anywhere in your code
Since the result of $subtotal is not used anyway, loop through the cart seems unnecessary
$sale_price is also not used anywhere in your code
Since $discount_total = $discount it is not necessary to use a new variable
A session variable is created/added
Your existing code, but optimized:
function action_woocommerce_after_order_total() {
// WC Cart NOT null
if ( ! is_null( WC()->cart ) ) {
// Get cart
$cart = WC()->cart;
// Getters
$cart_total = $cart->total;
$cart_discount_total = $cart->get_discount_total();
// Settings
$pctm = 90;
// Calculations
$discounted_value = $cart_total - ( $cart_total / 100 * $pctm );
$discount_total = $cart_total - $discounted_value;
// Greater than
if ( $discount_total > 0 ) {
// Result
$result = $discount_total + $cart_discount_total;
// The Output
echo '<tr class="my-class">
<th>' . __( 'VOCÊ RECEBERÁ DE CASHBACK', 'woocommerce' ) . '</th>
<td data-title="You">' . wc_price( $result ) . '</td>
</tr>';
// Set session
WC()->session->set( 'session_result', $result );
}
}
}
add_action( 'woocommerce_cart_totals_after_order_total', 'action_woocommerce_after_order_total', 10 );
add_action( 'woocommerce_review_order_after_order_total', 'action_woocommerce_after_order_total', 10 );
To answer your question:
Step 1) We get the result from the session variable and add it as order data, so that we can use/obtain this information everywhere via the $order object
// Add as custom order meta data and reset WC Session variable
function action_woocommerce_checkout_create_order( $order, $data ) {
// Isset
if ( WC()->session->__isset( 'session_result' ) ) {
// Get
$result = (float) WC()->session->get( 'session_result' );
// Add as meta data
$order->update_meta_data( 'result', $result );
// Unset
WC()->session->__unset( 'session_result' );
}
}
add_action( 'woocommerce_checkout_create_order', 'action_woocommerce_checkout_create_order', 10, 2 );
Step 2) Use the woocommerce_get_order_item_totals filter hook, which will allow you to add a new row to the existing tables with the $result.
The new row will be added in:
Email notifications
Order received (thank you page)
My account -> view order
function filter_woocommerce_get_order_item_totals( $total_rows, $order, $tax_display ) {
// Get meta
$result = $order->get_meta( 'result' );
// NOT empty
if ( ! empty ( $result ) ) {
// Add new row
$total_rows['total_result']['label'] = __( 'VOCÊ RECEBERÁ DE CASHBACK', 'woocommerce' );
$total_rows['total_result']['value'] = wc_price( $result );
}
return $total_rows;
}
add_filter( 'woocommerce_get_order_item_totals', 'filter_woocommerce_get_order_item_totals', 10, 3 );
I'm building a website where i'll sell some fine art prints and i would like to set an automated discount when a customer buy the first print of a serie. Each of my prints are limited to 15 pieces and i would like to set a 30% discount for the first sell (#1/15) of each series. Then, for the next prints (#2 to #15), price goes back to normal.
Edit :
I did progress on my problem ! I did some research and tried a lot of different things and i found how to set the custom price i wanted on my product page (regular price + discount price) with the rule to only apply it if the related item available stock is 15.
regular price + discount price
And here's the code is used :
// Generating the product "regular price"
add_filter( 'woocommerce_product_get_regular_price', 'dynamic_regular_price', 10, 2 );
add_filter( 'woocommerce_product_variation_get_regular_price', 'dynamic_regular_price', 10, 2 );
function dynamic_regular_price( $regular_price, $product ) {
$stock_qte = $product->get_stock_quantity();
if( empty($regular_price) || $regular_price == 0 )
return $product->get_price();
else
return $regular_price;
}
// Generating the product "sale price"
add_filter( 'woocommerce_product_get_sale_price', 'dynamic_sale_price', 10, 2 );
add_filter( 'woocommerce_product_variation_get_sale_price', 'dynamic_sale_price', 10, 2 );
function dynamic_sale_price( $sale_price, $product ) {
$stock_qte = $product->get_stock_quantity();
if( $stock_qte == '15')
return $product-> get_regular_price() * 0.7;
else
//return $product->get_regular_price();
return $regular_price;
};
// Displayed formatted regular price + sale price
add_filter( 'woocommerce_get_price_html', 'dynamic_sale_price_html', 20, 2 );
function dynamic_sale_price_html( $price_html, $product ) {
$stock_qte = $product->get_stock_quantity();
if( $stock_qte == '15')
$price_html = wc_format_sale_price( wc_get_price_to_display( $product, array( 'price' => $product->get_regular_price() ) ), wc_get_price_to_display( $product, array( 'price' => $product->get_sale_price() ) ) ) . $product->get_price_suffix();
else
$price_html = wc_price(wc_get_price_to_display( $product, array( 'price' => $product->get_regular_price() ) ). $product->get_price_suffix());
return $price_html;
}
It's almost working but the code related to the cart values updates is not fully working. The cart sub-total is good and take count of the discount but the individual price of each product is not updated and shown as without discount. It's the same on the cart page.
cart (from mini cart)
cart (cart page)
add_action( 'woocommerce_before_calculate_totals', 'alter_price_cart', 9999 );
function alter_price_cart( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 ) return;
// IF CUSTOMER NOT LOGGED IN, DONT APPLY DISCOUNT
//if ( ! wc_current_user_has_role( 'customer' ) ) return;
// LOOP THROUGH CART ITEMS & APPLY 30% DISCOUNT
foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
$product = $cart_item['data'];
$price = $product->get_price();
$cart_item['data']->set_price( $price * 0.7 );
}
}
I'll also have to add the stock availability logic for the mini-cart / cart / checkout page but i'll do that in a second time.
I must have made some mistakes in the loop throught the cart but i cannot catch the issue :( Any idea about how to hook the price of each product and change the displayed value ?
Have a nice day,
Quentin
We have customers who have different discount percentages. This is all programmed and working on the front end when adding products to the cart, but if using the back end order admin to add a new product I can't seem to find a way to calculate the new product price based on the user's discount. Is there a way to change the price in the woocommerce_new_order_item hook when adding a product to an existing order in the order admin?
Here's what I have so far:
function action_woocommerce_new_order_item( $item_id, $item, $order_id ) {
// only run this from the WP admin section
if ( !is_admin() )
return;
$item_type = $item->get_type();
// return if this is not a product (i.e. fee, tax, etc.)
if ( $item_type != 'line_item' )
return;
$product = wc_get_product( $item->get_product_id() );
if ( !$product )
return;
$current_price = $product->get_price();
$quantity = $item->get_quantity();
// here I get the order's user's discount percentage and calculate the new discounted price
// custom function
$discounted_price = get_discounted_price( $current_price, $users_discount );
$new_price = ( !empty($discounted_price) ) ? $discounted_price : $current_price;
// this doesn't work
$item->set_price( $new_price );
// and this doesn't work
$product->set_price( $new_price );
// this appears to work but I'm not sure if this the best way to accomplish this
$item->set_total( $price * $quantity );
}
add_action( 'woocommerce_new_order_item', 'action_woocommerce_new_order_item', 10, 3 );
Any help would be greatly appreciated!
Thanks
This works for adding item meta and changing the item price in the order admin screen:
function action_woocommerce_new_order_item( $item_id, $item, $order_id ) {
// only run this from the WP admin section
if ( !is_admin() )
return;
$item_type = $item->get_type();
// return if this is not a product (i.e. fee, tax, etc.)
if ( $item_type != 'line_item' )
return;
$product = wc_get_product( $item->get_product_id() );
if ( !$product )
return;
$current_price = $product->get_price();
$size = $product->get_attribute('size');
// add custom item meta to order
if ( $size ) {
wc_add_order_item_meta( $item_id, 'Size', $size , false );
}
$order = wc_get_order( $order_id );
$order_user = $order->get_user();
$order_user_id = $order->get_user_id();
// get this order's user's discount %
$users_discount = get_user_meta( $order_user_id, 'discount', true );
$users_discount = ( $users_discount > 0 ) ? $users_discount : 0;
// calculate the discounted price based on the user's discount (custom function)
$discounted_price = get_discounted_price( $current_price, $users_discount );
$quantity = $item->get_quantity();
$new_price = ( !empty($discounted_price) ) ? $discounted_price : $current_price;
$item->set_total( $new_price * $quantity );
};
add_action( 'woocommerce_new_order_item', 'action_woocommerce_new_order_item', 10, 3 );