Additional fees woocommerce based on product input - php

I've used this code to add additional fees to the woocommerce product https://sarkware.com/woocommerce-change-product-price-dynamically-while-adding-to-cart-without-using-plugins/
my function
function calculate_aditional_fees( $cart_object ) {
if( !WC()->session->__isset( "reload_checkout" ) ) {
$additional_price = get_option( 'additional_person_price' );
$additional_driver_price = get_option( 'additional_driver_price' );
foreach ( $cart_object->cart_contents as $key => $value ) {
if ( $additional_driver_price > 0 || $additional_price > 0 ) {
$additional_price *= $value['additional-persons'];
$additional_driver_price *= $value['additional-drivers'];
$cart_object->cart_contents[$key]['data']->price = floatval( $value['data']->price ) + $additional_driver_price + $additional_price;
}
}
}
}
The first problem was that all the extra fees ware added twice (when using the add to cart button). I've just hacked this by dividing additional charges by two.
The problem after that is that after clicking the make payment in the order form, the final price is changed - the're is one fourth of the additional charges added again.
Example. Original price 500, additional person costs 40 the user chooses to add 1 additional person.
The calculation when adding to cart is 40*1/2 (this is done twice for some reason)
after adding to cart is 540 which is ok.
But after making payment (payment method is bank/wire transfer) the confirmation shows 560 as the order price which is big problem obviously.
I would appreciate any help on this.
The fields for additional persons are added via a hook (basically as in the tutorial).
add_action( 'woocommerce_before_add_to_cart_button', 'add_form_before_booking_product' );
function add_form_before_booking_product() {
global $post;
if( function_exists('get_product') ){
$product = get_product( $post->ID );
if( $product->is_type( 'booking' ) ){
echo '<table class="variations booking__variations"><tbody>
<tr>
<td class="label"><label for="additional-persons">'. __( 'Additional persons' ) .'</label></td>
<td class="value">
<input type="number" name="additional-persons" value="0" min="0" max=10 required />
</td>
</tr>
</tbody>
</table>';
}
}
}
After Loads of searching I changed the hook to woocommerce_cart_calculate_fees
Removed the division. The problem here is that the calculation is ok, after payment also but in the cart table subtotal and total are wrong (the values aren't added).
I've tried to calculate this manually so the function looks
function calculate_aditional_fees( $cart_object ) {
if( !WC()->session->__isset( "reload_checkout" )) {
$additional_price = get_option( 'additional_person_price' );
$additional_driver_price = get_option( 'additional_driver_price' );
foreach ( $cart_object->cart_contents as $key => $value ) {
if ( $additional_driver_price > 0 || $additional_price > 0 ) {
$additional_price = get_option( 'additional_person_price' );
$additional_driver_price = get_option( 'additional_driver_price' );
$additional_price = floatval( $additional_price ) * intval( $value['additional-persons'] );
$additional_driver_price = floatval( $additional_driver_price ) * intval( $value['additional-drivers'] );
$sum =( $additional_driver_price + $additional_price ) * WC()->cart->cart_contents[$key]['quantity'];
$sum2 =( $additional_driver_price + $additional_price );
WC()->cart->cart_contents[$key]['data']->price = floatval( $value['data']->price ) + $sum2;
// $additional_sum = $sum / 1.23;
// $additional_tax = $sum-$additional_sum;
// if (!is_checkout()) {
// WC()->cart->cart_contents[$key]['line_total'] += $additional_sum;
// WC()->cart->cart_contents_total += $additional_sum;
// WC()->cart->subtotal += $sum;
// WC()->cart->subtotal_ex_tax += $additional_sum;
// WC()->cart->taxes[1] += $additional_tax;
// }
// WC()->cart->add_fee('Dodtkowe opcje',($additional_driver_price + $additional_price)*WC()->cart->cart_contents[$key]['quantity']);
}
}
}
}
add_action( 'woocommerce_cart_calculate_fees', 'calculate_aditional_fees');
When removing the comments the sum and subtotal are ok, but then after making payment the additional cost are added once again so Im back to the same problem.
This works but...
It add another line under the subtotal in cart page, the calculation are ok, not yet the solution I'm looking for (the customers don't see per product fees but as one total)
function calculate_aditional_fees( ) {
if( !WC()->session->__isset( "reload_checkout" )) {
$additional_price = get_option( 'additional_person_price' );
$additional_driver_price = get_option( 'additional_driver_price' );
$sum =0;
foreach ( WC()->cart->cart_contents as $key => $value ) {
if ( $additional_driver_price > 0 || $additional_price > 0 ) {
$additional_price = get_option( 'additional_person_price' );
$additional_driver_price = get_option( 'additional_driver_price' );
$additional_price = floatval( $additional_price ) * intval( $value['additional-persons'] );
$additional_driver_price = floatval( $additional_driver_price ) * intval( $value['additional-drivers'] );
$sum += ( $additional_driver_price + $additional_price ) * WC()->cart->cart_contents[$key]['quantity'];
}
}
WC()->cart->add_fee(__('Fee','woocommerce'),$sum);
}
}
add_action( 'woocommerce_cart_calculate_fees', 'calculate_aditional_fees');

Related

Add column total order weight to WooCommerce My account Orders

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';
}
}
}
?>

WooCommerce promotional discount: Buy 10 Get 1 Free

I'm trying to set up a specific discount for three variable products (464, 465 and 466). If a customer buys ten products they get one for free.
Based on WooCommerce discount: buy one get one 50% off answer code, I've come up with the following code:
add_action('woocommerce_cart_calculate_fees', 'add_custom_discount_11th_at_100', 10, 1 );
function add_custom_discount_11th_at_100( $wc_cart ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;
$discount = 0;
$items_prices = array();
// Set HERE your targeted variable product ID
$targeted_product_id = 464;
foreach ( $wc_cart->get_cart() as $key => $cart_item ) {
if( $cart_item['product_id'] == $targeted_product_id ){
$qty = intval( $cart_item['quantity'] );
for( $i = 0; $i < $qty; $i++ )
$items_prices[] = floatval( $cart_item['data']->get_price());
}
}
$count_items_prices = count($items_prices);
if( $count_items_prices > 10 ) foreach( $items_prices as $key => $price )
if( $key % 11 == 1 ) $discount -= number_format($price / 1, 11 );
if( $discount != 0 ){
// Displaying a custom notice (optional)
wc_clear_notices();
wc_add_notice( __("Buy 10 Get 1 Free"), 'notice');
// The discount
$wc_cart->add_fee( 'Buy 10 Get 1 Free', $discount, true );
# Note: Last argument in add_fee() method is related to applying the tax or not to the discount (true or false)
}
}
But it only works for one product ID. How do I expand it to work for three product Ids?
To make it work for multiple products you could use in_array() php function as follow:
add_action('woocommerce_cart_calculate_fees', 'buy_ten_get_one_free', 10, 1 );
function buy_ten_get_one_free( $cart ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;
// Set HERE your targeted variable products IDs
$targeted_product_ids = array( 464, 465, 466 );
$each_n_items = 10; // Number of items required to get a free one
$discount = 0; // Initializing
$items_prices = array(); // Initializing
foreach ( $cart->get_cart() as $cart_item ) {
if( in_array( $cart_item['product_id'], $targeted_product_ids ) ) {
$qty = intval( $cart_item['quantity'] );
for ( $i = 0; $i < $qty; $i++ ) {
$items_prices[] = floatval( $cart_item['data']->get_price() );
}
}
}
$count_items_prices = count($items_prices);
if ( $count_items_prices > $each_n_items ) {
foreach ( $items_prices as $key => $price ) {
if ( $key % ($each_n_items + 1) == 1 ) {
$discount += number_format($price, 2 );
}
}
}
if ( $discount > 0 ) {
// Displaying a custom notice (optional)
wc_clear_notices();
wc_add_notice( __("Buy 10 Get 1 Free"), 'notice');
// The discount
$cart->add_fee( __("Buy 10 Get 1 Free"), -$discount, true );
}
}
Code goes in function.php file of your active child theme (or active theme), tested and works.

Display variable product discounted percentage only on Woocommerce archive pages

I'd like to display the percentage variable products are discounted in the archive pages. With the code below, I was able to get both the discounts % on variable products but also for simple products. Can I do this ONLY for variable products and not simple products? I realize it's probably a simple adjustment in the code but I can't figure it out because I'm an idiot when it comes to PHP.
add_action( 'woocommerce_after_shop_loop_item', 'show_sale_percentage', 25 );
function show_sale_percentage() {
global $product;
if ( $product->is_on_sale() ) {
if ( ! $product->is_type( 'variable' ) ) {
$max_percentage = ( ( $product->get_regular_price() - $product->get_sale_price() ) / $product->get_regular_price() ) * 100;
} else {
$max_percentage = 0;
foreach ( $product->get_children() as $child_id ) {
$variation = wc_get_product( $child_id );
$price = $variation->get_regular_price();
$sale = $variation->get_sale_price();
if ( $price != 0 && ! empty( $sale ) ) $percentage = ( $price - $sale ) / $price * 100;
if ( $percentage > $max_percentage ) {
$max_percentage = $percentage;
}
}
}
echo "<div class='saved-sale'>-" . round($max_percentage) . "%</div>";
}
}
To display the on sale percentage, on archives pages, for variable products only, try the following:
add_action( 'woocommerce_after_shop_loop_item', 'loop_variable_product_sale_percentage', 25 );
function loop_variable_product_sale_percentage() {
global $product;
if ( $product->is_on_sale() && $product->is_type( 'variable' ) ) {
$max_percentage = 0;
foreach ( $product->get_children() as $child_id ) {
$variation = wc_get_product( $child_id );
$percentage = 0;
$price = $variation->get_regular_price();
$sale = $variation->get_sale_price();
if ( $price != 0 && ! empty( $sale ) ) {
$percentage = ( $price - $sale ) / $price * 100;
}
if ( $percentage > $max_percentage ) {
$max_percentage = $percentage;
}
}
echo '<div class="saved-sale">-' . round($max_percentage) . '%</div>';
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.

Change number of decimals in Woocommerce cart totals

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;
}

Set product variation custom calculated price dynamically in WooCommerce

I am trying to dynamically add to a products price when a user checks out based on what they have entered in the product page. The setting of the price is only working on a non variation product.
I need to be able to set the price on the variations of the products as well.
The code that I am using:
function add_cart_item_data( $cart_item_meta, $product_id, $variation_id ) {
$product = wc_get_product( $product_id );
$price = $product->get_price();
$letterCount = strlen($_POST['custom_name']);
$numberCount = strlen($_POST['custom_number']);
if($letterCount != '0') {
$letterPricing = 20 * $letterCount;
$numberPricing = 10 * $numberCount;
$additionalPrice = $letterPricing + $numberPricing;
$cart_item_meta['custom_price'] = $price + $additionalPrice;
}
return $cart_item_meta;
}
function calculate_cart_total( $cart_object ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
return;
}
foreach ( $cart_object->cart_contents as $key => $value ) {
if( isset( $value['custom_price'] ) ) {
$price = $value['custom_price'];
$value['data']->set_price( ( $price ) );
}
}
}
I have completely revisited your code:
// Set custom data as custom cart data in the cart item
add_filter( 'woocommerce_add_cart_item_data', 'add_custom_data_to_cart_object', 30, 3 );
function add_custom_data_to_cart_object( $cart_item_data, $product_id, $variation_id ) {
if( ! isset($_POST['custom_name']) || ! isset($_POST['custom_number']) )
return $cart_item_data; // Exit
if( $variation_id > 0)
$product = wc_get_product( $variation_id );
else
$product = wc_get_product( $product_id );
$price = $product->get_price();
// Get the data from the POST request and calculate new custom price
$custom_name = sanitize_text_field( $_POST['custom_name'] );
if( strlen( $custom_name ) > 0 )
$price += 20 * strlen( $custom_name );
$custom_number = sanitize_text_field( $_POST['custom_number'] );
if( strlen( $custom_number ) > 0 )
$price += 10 * strlen( $custom_number );
// Set new calculated price as custom cart item data
$cart_item_data['custom_data']['price'] = $price;
return $cart_item_data;
}
// Set the new calculated price of the cart item
add_action( 'woocommerce_before_calculate_totals', 'set_new_cart_item_price', 50, 1 );
function set_new_cart_item_price( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
foreach ( $cart->get_cart() as $cart_item ) {
if( isset( $cart_item['custom_data']['price'] ) ) {
// Get the new calculated price
$new_price = (float) $cart_item['custom_data']['price'];
// Set the new calculated price
$cart_item['data']->set_price( $new_price );
}
}
}
This code goes on function.php file of your active child theme (or theme).
Tested and works as well with product variations.

Categories