I'm using Woocommerce and Woocommerce Bookings for a villa booking website and I have a small issue I can't fix.
I'd like to add a field "Details" into my cart (Cart Details). This field will display the duration of the booking and also the price / night of each villa.
My villas have some resources with a specific block cost.
Regarding the duration value, I can display it by using this code bellow :
<?php
/*display_card_data();*/
$items = WC()->cart->get_cart();
foreach($items as $item) {
$duration = $item['booking']['duration'];
}
// displaying values for test
echo $duration. ' x Night Price' .$price ;
?>
I'm wondering how I can display the block cost in this field.
Please any help will be useful.
This can be done with the following code (but they can be many unit prices in WC Bookings):
add_filter( 'woocommerce_cart_item_name', 'booking_details_after_name', 30, 3 );
function booking_details_after_name( $product_name, $cart_item, $cart_item_key ) {
if ( isset( $cart_item['booking']['duration'] ) ){
// Duration
$duration = $cart_item['booking']['duration'];
// Price cost ( they can be many different )
$base_cost = get_post_meta( $cart_item['product_id'], '_wc_booking_cost', true );
$block_cost = get_post_meta( $cart_item['product_id'], '_wc_booking_block_cost', true );
// Output
$product_name .= '<br><span class="booking-details">';
$product_name .= $duration . __(" x Night Price ", "woocommerce") . wc_price($base_cost);
$product_name .= '</span>';
}
return $product_name;
}
Code goes in function.php file of your active child theme (or active theme). Tested and work.
Specific Update:
add_filter( 'woocommerce_cart_item_name', 'booking_details_after_name', 30, 3 );
function booking_details_after_name( $product_name, $cart_item, $cart_item_key ) {
if ( isset( $cart_item['booking']['duration'] ) ){
// Duration
$duration = (int) $cart_item['booking']['_duration'];
$resource_id = $cart_item['booking']['_resource_id'];
$start_time = $cart_item['booking']['_start_date'];
$end_time = $cart_item['booking']['_end_date'];
$loop_time = (int) $start_time;
$day = 86400; // In seconds
// Price cost ( they can be many different )
$res_block_cost = get_post_meta( $cart_item['product_id'], '_resource_block_costs', true );
$booking_pricing = get_post_meta( $cart_item['product_id'], '_wc_booking_pricing', true );
foreach ( $res_block_cost as $key => $value ){
if( $key == $resource_id ){
$bloc_cost = $value;
break;
}
}
$cost = array();
foreach ( $booking_pricing as $key => $value ){
$from = strtotime($value['from']);
$to = strtotime($value['to']) + 86399;
for( $i = 0; $i < $duration; $i++ ){
if( $loop_time >= $from && $loop_time <= $to ){
$cost[] = $value['cost'];
$loop_time += $day;
}
}
}
$cost = array_sum( $cost ) / $duration;
// Output
$product_name .= '<br><span class="booking-details">';
$product_name .= $duration . __(" x Night Price ", "woocommerce") . wc_price($bloc_cost + $cost);
$product_name .= '</span>';
}
return $product_name;
}
Code goes in function.php file of your active child theme (or active theme). Tested and work.
Related
I want to add weight metadata to an order in the Frontend: My Account - Orders. I tried some things but it is not working.
I want to add is $order->get_weight(); as meta data to the order but I am getting an error.
I am already half way using this code to add a new column and show product description and quantity:
add_filter( 'woocommerce_my_account_my_orders_columns', 'additional_my_account_orders_column', 10, 1 );
function additional_my_account_orders_column( $columns ) {
$new_columns = [];
foreach ( $columns as $key => $name ) {
$new_columns[ $key ] = $name;
if ( 'order-status' === $key ) {
$new_columns['order-items'] = __( 'Descripción', 'woocommerce' );
}
}
return $new_columns;
}
add_action( 'woocommerce_my_account_my_orders_column_order-items', 'additional_my_account_orders_column_content', 10, 1 );
function additional_my_account_orders_column_content( $order ) {
$details = array();
foreach( $order->get_items() as $item )
$details[] = $item->get_name() . ' × ' . $item->get_quantity();
echo count( $details ) > 0 ? implode( '<br>', $details ) : '–';
}
Hope someone can help me get in the right direction.
This snippet inserts a new, custom column in the table of orders shown in My Account > Orders populated with the total weight of the order so the customer is aware how heavy their order was.
Specifically, this snippet has two blocks of code. The first block inserts the column. In this example, we have inserted this column between the Order Total and Order Actions column. This can be changed by changing the column key in the code. Your custom will appear after the column key you define.
The second block of code is where the magic happens. It first loops through each item in the order and gets it weight and times this by the quantity of this product. It then adds this weight of each product to a variable we have called $total_weight. The total weight is then output to the new column followed by the weight unit you have defined in your store settings under WordPress Dashboard > WooCommerce > Settings > Products > General > Measurements > Weight Unit.
/**
* Snippet Name: WooCommerce Show Order Weight Column In My Account Order View Table
* Snippet Author: ecommercehints.com
*/
// First, create the new table column between Total and Actions columns
add_filter( 'woocommerce_my_account_my_orders_columns', 'ecommercehints_weight_column_my_account_orders_table', 10, 1 );
function ecommercehints_weight_column_my_account_orders_table( $columns ) {
$weight_column = [];
foreach ( $columns as $key => $name ) {
$weight_column[ $key ] = $name;
if ( 'order-total' === $key ) { // Insert new column after Total column
$weight_column['order-items'] = __( 'Order Weight', 'woocommerce' );
}
}
return $weight_column;
}
// Second, insert the data from the order into the new column
add_action( 'woocommerce_my_account_my_orders_column_order-items', 'ecommercehints_get_order_weight', 10, 1 );
function ecommercehints_get_order_weight( $order ) {
$weight_unit = get_option('woocommerce_weight_unit');
$total_weight = 0;
foreach( $order->get_items() as $item_id => $item ){
$quantity = $item->get_quantity();
$product = $item->get_product();
$product_weight = $product->get_weight();
$total_weight += floatval( $product_weight * $quantity );
}
echo $total_weight . $weight_unit;
}
Just came across this, have you tried it? https://gist.github.com/kloon/5299119?permalink_comment_id=1415838
Here is a copy of the code in case the link eventually dies:
<?php
add_filter( 'manage_edit-shop_order_columns', 'woo_order_weight_column' );
function woo_order_weight_column( $columns ) {
$columns['total_weight'] = __( 'Weight', 'woocommerce' );
return $columns;
}
add_action( 'manage_shop_order_posts_custom_column', 'woo_custom_order_weight_column', 2 );
function woo_custom_order_weight_column( $column ) {
global $post, $woocommerce, $the_order;
if ( empty( $the_order ) || $the_order->get_id() !== $post->ID )
$the_order = new WC_Order( $post->ID );
if ( $column == 'total_weight' ) {
$weight = 0;
if ( sizeof( $the_order->get_items() ) > 0 ) {
foreach( $the_order->get_items() as $item ) {
if ( $item['product_id'] > 0 ) {
$_product = $item->get_product();
if ( ! $_product->is_virtual() ) {
$weight += $_product->get_weight() * $item['qty'];
}
}
}
}
if ( $weight > 0 ) {
print $weight . ' ' . esc_attr( get_option('woocommerce_weight_unit' ) );
} else {
print 'N/A';
}
}
}
?>
I try to manipulate the calculated price in cart but with no luck...
I hope someone can help me here.
I have found this article and implemented the code as written on the Page which is to 98% exactly what i searched for. I need to add a cart calculation if the price type is per 100g.
So this is the actual working code:
add_filter( 'woocommerce_get_price_html', 'wb_change_product_html', 10, 2 );
// Adding a custom field to the price markup
function wb_change_product_html( $price, $product ) {
//$wb_price_type = get_field('product_price_type');
$wb_price_type = get_post_meta( $product->get_id(), 'product_price_type', true);
if($wb_price_type) {
$price_html = '<span class="amount">' . $price . ' ' . $wb_price_type . '</span>';
}
else {
$price_html = '<span class="amount">' . $price . '</span>';
}
return $price_html;
}
add_filter( 'woocommerce_cart_item_price', 'wb_change_product_price_cart', 10, 3 );
// Adding a custom field to the price in the cart
function wb_change_product_price_cart( $price, $cart_item, $cart_item_key ) {
//$wb_price_type = get_field( 'product_price_type', $cart_item['product_id'] );
$wb_price_type = get_post_meta( $cart_item['product_id'], 'product_price_type', true );
if ($wb_price_type) {
$price = $price . ' ' . $wb_price_type;
}
else {
$price = $price;
}
return $price;
}
add_filter( 'woocommerce_checkout_cart_item_quantity', 'wb_checkout_review', 10, 3 );
// Adding a custom field to the price in the checkout items
function wb_checkout_review ( $quantity, $cart_item, $cart_item_key ) {
//$wb_price_type = get_field( 'product_price_type', $cart_item['product_id'] );
$wb_price_type = get_post_meta( $cart_item['product_id'], 'product_price_type', true);
if ( $wb_price_type ) {
$cart_item = ' ' . sprintf( '× %s ', $cart_item['quantity'] ) . $wb_price_type . '';
}
else {
$cart_item = ' ' . sprintf( '× %s', $cart_item['quantity'] ) . '';
}
return $cart_item;
}
Well i thought its really easy to calculate so i do something like this:
add_filter( 'woocommerce_cart_item_price', 'wb_change_product_price_cart', 10, 3 );
// Adding a custom field to the price in the cart
function wb_change_product_price_cart( $price, $cart_item, $cart_item_key ) {
//$wb_price_type = get_field( 'product_price_type', $cart_item['product_id'] );
$wb_price_type = get_post_meta( $cart_item['product_id'], 'product_price_type', true );
if ($wb_price_type) {
if ($wb_price_type == "per 100g") {
$price = $price / 100 . ' ' . $wb_price_type;
}
else {
$price = $price . ' ' . $wb_price_type;
}
}
else {
$price = $price;
}
return $price;
}
But this didnt work... i too try to manipulate the price directly but i get anytime 0 in the value of the price...
so i google a little bit more and found this article, which makes in the end exactly what i want. in the last print screen we can see that the price will displayed per kg (which is completely fine) and in the calculation it uses an other price (which i try to do with the code above).
i think that the problem is that $price has for example the value "20$" and this is a string and i cannot calculate withe a string. And it makes no sense to split this string there should be an other way.
For better understanding here a picture. in the upper part we see the working calculation (which is good) but this is not exactly what i need. in the part down we see that it will only calculate on the calculated price (right side). the left side of the downer part remain with the correct price.
i think that the problem is that $price has for example the value "20$" and this is a string and i cannot calculate withe a string. And it makes no sense to split this string there should be an other way.
This is true, it's actually a full html string. Try changing the woocommerce_cart_item_price filter to get the price out of string:
add_filter('woocommerce_cart_item_price', 'wb_change_product_price_cart', 10, 3);
// Adding a custom field to the price in the cart
function wb_change_product_price_cart($price, $cart_item, $cart_item_key)
{
$wb_price_type = get_post_meta($cart_item['product_id'], 'product_price_type', true);
if ($wb_price_type) {
if ($wb_price_type == "per 100g") {
preg_match("/([0-9]+\.[0-9]+)/", $price, $matches);
$_price = $matches[0];
$per100 = $_price / 100;
$price = $per100 . ' ' . $wb_price_type;
} else {
$price = $price . ' ' . $wb_price_type;
}
} else {
$price = $price;
}
return $price;
}
In Woocommerce, I am using the following code to calculate and display 'Total Saving' on the order in Cart and checkout pages:
function wc_discount_total_30() {
global $woocommerce;
$discount_total = 0;
foreach ( $woocommerce->cart->get_cart() as $cart_item_key => $values) {
$_product = $values['data'];
if ( $_product->is_on_sale() ) {
$regular_price = $_product->get_regular_price();
$sale_price = $_product->get_sale_price();
$discount = ($regular_price - $sale_price) * $values['quantity'];
$discount_total += $discount;
}
}
if ( $discount_total > 0 ) {
echo '<tr class="cart-discount">
<th>'. __( 'Saved', 'tsavedis' ) .'</th>
<td data-title=" '. __( 'Saved', 'tsavedis' ) .' ">'
. wc_price( $discount_total + $woocommerce->cart->discount_cart ) .'</td>
</tr>';
}
}
// Hook our values to the Basket and Checkout pages
add_action( 'woocommerce_cart_totals_after_order_total', 'wc_discount_total_30', 99);
add_action( 'woocommerce_review_order_after_order_total', 'wc_discount_total_30', 99);
I need to display this total saving in 'Order Edit' page at backend as a custom field.
How to do that ?
Here is the way to add the same thing to orders totals table:
// Display the chosen delivery information
add_filter( 'woocommerce_get_order_item_totals', 'add_saving_total_order_totals', 10, 3 );
function add_saving_total_order_totals( $total_rows, $order, $tax_display ) {;
$saving_total = 0;
// Loop through Order items
foreach($order->get_items() as $item ){
$product = $item->get_product();
if( $product->is_on_sale() ){
$regular_price = (float) $product->get_regular_price();
$active_price = (float) $product->get_price();
$saving_total += ($regular_price - $active_price) * $item->get_quantity();
}
}
if( $saving_total > 0 ) {
$discount_total = $order->get_discount_total();
$label = __( 'Saved', 'tsavedis' );
$value = wc_price( $saving_total + $discount_total );
$total_rows['saving'] = array( 'label' => $label,'value' => $value );
}
return $total_rows;
}
Code goes in function.php file of your active child theme (or active theme). tested and works.
In Woocommerce, I've set the number of decimal to 7 on in Woocommerce general settings, so I can display the product price like this $0.0453321.
I'm wondering if I can set/round the cart total to only 2 decimals (something like this $2.34)?
The correct way is to change just the number of allowed decimals for cart and checkout pages only:
add_filter( 'wc_get_price_decimals', 'change_prices_decimals', 20, 1 );
function change_prices_decimals( $decimals ){
if( is_cart() || is_checkout() )
$decimals = 2;
return $decimals;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
To set the dispayed cart gran total with 2 decimals use this instead (for Woocommerce 3.3+ only):
add_filter( 'woocommerce_cart_tax_totals', 'change_decimals_cart_tax_totals', 20, 2 );
function change_decimals_cart_tax_totals( $tax_totals, $cart ){
$decimals = array('decimals' => 2);
$taxes = $cart->get_taxes();
$tax_totals = array();
foreach ( $taxes as $key => $tax ) {
$code = WC_Tax::get_rate_code( $key );
if ( $code || $key === apply_filters( 'woocommerce_cart_remove_taxes_zero_rate_id', 'zero-rated' ) ) {
if ( ! isset( $tax_totals[ $code ] ) ) {
$tax_totals[ $code ] = new stdClass();
$tax_totals[ $code ]->amount = 0;
}
$tax_totals[ $code ]->tax_rate_id = $key;
$tax_totals[ $code ]->is_compound = WC_Tax::is_compound( $key );
$tax_totals[ $code ]->label = WC_Tax::get_rate_label( $key );
$tax_totals[ $code ]->amount += wc_round_tax_total( $tax );
$tax_totals[ $code ]->formatted_amount = wc_price( wc_round_tax_total( $tax_totals[ $code ]->amount ), $decimals );
}
}
if ( apply_filters( 'woocommerce_cart_hide_zero_taxes', true ) ) {
$amounts = array_filter( wp_list_pluck( $tax_totals, 'amount' ) );
$tax_totals = array_intersect_key( $tax_totals, $amounts );
}
return $tax_totals;
}
add_filter( 'woocommerce_cart_totals_order_total_html', 'change_decimals_cart_totals_order_total_html', 20, 1 );
function change_decimals_cart_totals_order_total_html( $formatted_price ){
$decimals = array('decimals' => 2);
$value = '<strong>' . wc_price( WC()->cart->get_total('edit'), $decimals ) . '</strong> ';
// If prices are tax inclusive, show taxes here.
if ( wc_tax_enabled() && WC()->cart->display_prices_including_tax() ) {
$tax_string_array = array();
$cart_tax_totals = WC()->cart->get_tax_totals();
if ( get_option( 'woocommerce_tax_total_display' ) == 'itemized' ) {
foreach ( $cart_tax_totals as $code => $tax ) {
$tax_string_array[] = sprintf( '%s %s', $tax->formatted_amount, $tax->label );
}
} elseif ( ! empty( $cart_tax_totals ) ) {
$tax_string_array[] = sprintf( '%s %s', wc_price( WC()->cart->get_taxes_total( true, true ), $decimals ), WC()->countries->tax_or_vat() );
}
if ( ! empty( $tax_string_array ) ) {
$taxable_address = WC()->customer->get_taxable_address();
$estimated_text = WC()->customer->is_customer_outside_base() && ! WC()->customer->has_calculated_shipping()
? sprintf( ' ' . __( 'estimated for %s', 'woocommerce' ), WC()->countries->estimated_for_prefix( $taxable_address[0] ) . WC()->countries->countries[ $taxable_address[0] ] )
: '';
$value .= '<small class="includes_tax">' . sprintf( __( '(includes %s)', 'woocommerce' ), implode( ', ', $tax_string_array ) . $estimated_text ) . '</small>';
}
}
return $value;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
You can't really round the prices in cart totals appart. If you do it with different hooks, you will get calculation errors. My code is just changing the number of decimals on displayed formatted prices and will not alter the calculations on real prices…
Related: Change number of decimals on Woocommerce displayed cart subtotal
If you want to change cart total only at cart and checkout page then you need to copy template in your theme
First copy 2 files from woocommerce plugin template to your theme .
1) Copy plugins\woocommerce\templates\cart\cart-totals.php to your-theme-folder\woocommerce\cart\cart-totals.php
2) Copy plugins\woocommerce\templates\checkout\review-order.php to your-theme-folder\woocommerce\checkout\review-order.php
And in both files find the wc_cart_totals_order_total_html() comment this code and put below code instead.
$args=array('decimals'=> 2);
echo wc_price(WC()->cart->total,$args);
This code is tested and its working fine.Hope it will help you as well.
In my case I needed to keep 3 decimal places "visible" throughout the site but approximate only the grand total to 2 decimal places (with a zero for the third decimal place) because my payment gateway accepted only 2 significant digits after the decimal.
Prices displayed as: 0.336
Taxes: 0.017
But grand total needed to be: 0.350 (instead of 0.353)
I ended up not using the code because it was so horrific but you can say it was a mental excercise:
add_filter( 'wc_get_price_decimals', 'change_prices_decimals', 20, 1 );
function change_prices_decimals( $decimals ){
if( is_cart() || is_checkout() )
{
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS,0);
$length = count($trace);
for ($i = 0; $i < $length; $i++)
{
if($trace[$i]["function"] == "set_total"){
$decimals = 2;
return $decimals;
}
}
}
return $decimals;
}
I want to show the total summed price of my grouped products instead of the price range. I already fixed this on the product page with this snippet.
How can I use this code or fix my issue on the shop page?
global $product;
$price = $product->get_price_html();
if ( $product->get_type() == 'grouped') {
$children = $product->get_children();
$price = 0;
foreach ($children as $key => $value) {
$_product = wc_get_product( $value );
$price += $_product->get_price();
}
$price = get_woocommerce_currency_symbol( '' ) . ' ' . $price;
}
?>
<p class="price"><?php echo $price; ?></p>
Try to use global filter to change the price returned in shop loop:
add_filter( 'woocommerce_get_price_html', 'highest_price', 10, 2 );
function highest_price( $price, $product ) {
if ( $product->get_type() == 'grouped') {
$children = $product->get_children();
$price = 0;
foreach ($children as $key => $value) {
$_product = wc_get_product( $value );
$price += $_product->get_price();
}
$price = get_woocommerce_currency_symbol( '' ) . ' ' . $price;
}
return $price;
}
I can not test this solution, but if it's not working, try to add filter to another function, not woocommerce_get_price_html, for example woocommerce_template_loop_price
I tested the following solution and it works on Woocommerce 3.6.3. After this the group product displays a price which is the sum total of all its child products.
function max_grouped_price( $price_this_get_price_suffix, $instance, $child_prices ) {
return wc_price(array_sum($child_prices));
}
add_filter( 'woocommerce_grouped_price_html', 'max_grouped_price', 10, 3 );