Display the discounted subtotal after discount on pdf invoice in Woocommerce - php

I want to display the cost of the product after a discount coupon on the invoice.
I have the following code, but it displays only base price, not the cost after coupon code:
add_filter( 'wpo_wcpdf_woocommerce_totals', 'wpo_wcpdf_woocommerce_totals_custom', 10, 2 );
function wpo_wcpdf_woocommerce_totals_custom( $totals, $order ) {
$totals = array(
'subtotal' => array(
'label' => __('Subtotal', 'wpo_wcpdf'),
'value' => $order->get_subtotal_to_display(),
),
);
return $totals;
}
I tred to change $totals to $discount, but it didn't work.

Your actual code is just removing all totals, displaying the subtotal only. Please try the following hooked function, that will display the discounted subtotal after the discount amount:
add_filter( 'wpo_wcpdf_woocommerce_totals', 'add_discounted_subtotal_to_pdf_invoices', 10, 2 );
function add_discounted_subtotal_to_pdf_invoices( $totals, $order ) {
// Get 'subtotal' raw amount value
$subtotal = strip_tags($totals['cart_subtotal']['value']);
$subtotal = (float) preg_replace('/[^0-9.]+/', '', $subtotal);
// Get 'discount' raw amount value
$discount = strip_tags($totals['discount']['value']);
$discount = (float) preg_replace('/[^0-9.]+/', '', $discount);
$new_totals = array();
// Loop through totals lines
foreach( $totals as $key => $values ){
$new_totals[$key] = $totals[$key];
// Inset new calculated 'Subtotal discounted' after total discount
if( $key == 'discount' && $discount != 0 && !empty($discount) ){
$new_totals['subtotal_discounted'] = array(
'label' => __('Subtotal discounted', 'wpo_wcpdf'),
'value' => wc_price($subtotal - $discount)
);
}
}
return $new_totals;
}
Code goes in function.php file of your active child theme (or theme).
Tested and works. It should work for you too.

Related

Modify "get_cart_contents_count()" to count only specific product IDs in WooCommerce cart

I have 3 ticket products in WooCommerce and want to add a name field per ticket product purchased - so if a customer buys three tickets 3 name fields are displayed to fill out in the checkout.
The code below works:
// Custom WooCommerce Checkout Fields based on Quantity
add_action( 'woocommerce_before_order_notes', 'person_details' );
function person_details($checkout) {
global $woocommerce;
$count = WC()->cart->get_cart_contents_count();
$i = 1;
for($k=2; $k<= $count; $k++) {
$i++;
print ('<h3>Please enter details of attendee '.$i.'</h3>');
woocommerce_form_field( 'cstm_full_name'.$i, array(
'type' => 'text',
'class' => array('my-field-class form-row-wide'),
'label' => __('Full name'),
'placeholder' => __('Enter full name'),
),
$checkout->get_value( 'cstm_full_name'.$i ));
echo '<div class="clear"></div>';
}
}
But this includes all products added to cart - how can I target specific Product IDs to be only be part of the cart count?
$count = WC()->cart->get_cart_contents_count();
As an alternative and most recommended way, you can use WC_Cart::get_cart_item_quantities() and in_array() in your callback function to get the cart item quantity from specific product IDs in cart
So you get:
function action_woocommerce_before_order_notes( $checkout ) {
// True
if ( WC()->cart ) {
// Specific product IDs
$product_ids = array( 30, 823, 53, 57 );
// Initialize
$count = 0;
// Loop trough cart items quantities
foreach ( WC()->cart->get_cart_item_quantities() as $product_id => $cart_item_quantity ) {
// Checks if a value exists in an array
if ( in_array( $product_id, $product_ids ) ) {
$count += $cart_item_quantity;
}
}
// Result
echo '<p style="color: red; font-size: 25px;">Count = ' . $count . '</p>';
}
}
add_action( 'woocommerce_before_order_notes', 'action_woocommerce_before_order_notes', 10, 1 );
OR
You could use the woocommerce_cart_contents_count filter hook,
which allows you to customize the get_cart_contents_count() function to your needs:
function filter_woocommerce_cart_contents_count( $array_sum ) {
// True
if ( WC()->cart ) {
// Specific product IDs
$product_ids = array( 30, 823, 53, 57 );
// Loop trough cart items quantities
foreach ( WC()->cart->get_cart_item_quantities() as $product_id => $cart_item_quantity ) {
// Checks if a value NOT exists in an array
if ( ! in_array( $product_id, $product_ids ) ) {
$array_sum -= $cart_item_quantity;
}
}
}
return $array_sum;
}
add_filter( 'woocommerce_cart_contents_count', 'filter_woocommerce_cart_contents_count', 10, 1 );
function action_woocommerce_before_order_notes( $checkout ) {
// True
if ( WC()->cart ) {
// Get number of items in the cart.
$count = WC()->cart->get_cart_contents_count();
// Result
echo '<p style="color: red; font-size: 25px;">Count = ' . $count . '</p>';
}
}
add_action( 'woocommerce_before_order_notes', 'action_woocommerce_before_order_notes', 10, 1 );
Note: although the advantage here is that you can continue to use the get_cart_contents_count() function in this way, the disadvantage is that this function would also display the modified result when used for other purposes

Auto add or update a custom fee via admin edit orders in WooCommerce

We have a special case where we invoice our customers for payment after the order has been received instead of having them pay during checkout. Shipping charges are manually calculated and added to the order and then we add a 3% credit card fee on the grand total.
To automate this process, I created a script that calculates the 3% charge once the shipping charge has been set through the backend and adds this fee item into the order automatically. This works when we add the shipping charge and click save/recalculate the first time.
add_action( 'woocommerce_order_after_calculate_totals', "custom_order_after_calculate_totals", 10, 2);
function custom_order_after_calculate_totals($and_taxes, $order) {
if ( did_action( 'woocommerce_order_after_calculate_totals' ) >= 2 )
return;
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
$percentage = 0.03;
$total = $order->get_total();
$surcharge = $total * $percentage;
$feeArray = array(
'name' => '3% CC Fee',
'amount' => wc_format_decimal($surcharge),
'taxable' => false,
'tax_class' => ''
);
//Get fees
$fees = $order->get_fees();
if(empty($fees)){
//Add fee
$fee_object = (object) wp_parse_args( $feeArray );
$order->add_fee($fee_object);
} else {
//Update fee
foreach($fees as $item_id => $item_fee){
if($item_fee->get_name() == "3% CC Fee"){
$order->update_fee($item_id,$feeArray);
}
}
}
}
If we accidentally add the wrong shipping cost and try to update it, this code above does get triggered again and updates the fee however $total does not get the new order total from the updated shipping cost and so the fee does not change. Strangely enough, if I try to delete the fee item, a new fee is calculated and is added back with the correct fee amount.
Anybody know how I can solve this?
As you are using the order gran total to calculate your fee and as the hook you are using is located inside calculate_totals() method, once order get updated, you will always need to press "recalculate" button to get the correct fee total and the correct order gran total with the correct amounts.
Since WooCommerce 3 your code is outdated and a bit obsolete with some mistakes… For example add_fee() and update_fee() methods are deprecated and replaced by some other ways.
Use instead the following:
add_action( 'woocommerce_order_after_calculate_totals', "custom_order_after_calculate_totals", 10, 2 );
function custom_order_after_calculate_totals( $and_taxes, $order ) {
if ( did_action( 'woocommerce_order_after_calculate_totals' ) >= 2 )
return;
$percentage = 0.03; // Fee percentage
$fee_data = array(
'name' => __('3% CC Fee'),
'amount' => wc_format_decimal( $order->get_total() * $percentage ),
'tax_status' => 'none',
'tax_class' => ''
);
$fee_items = $order->get_fees(); // Get fees
// Add fee
if( empty($fee_items) ){
$item = new WC_Order_Item_Fee(); // Get an empty instance object
$item->set_name( $fee_data['name'] );
$item->set_amount( $fee_data['amount'] );
$item->set_tax_class($fee_data['tax_class']);
$item->set_tax_status($fee_data['tax_status']);
$item->set_total($fee_data['amount']);
$order->add_item( $item );
$item->save(); // (optional) to be sure
}
// Update fee
else {
foreach ( $fee_items as $item_id => $item ) {
if( $item->get_name() === $fee_data['name'] ) {
$item->set_amount($fee_data['amount']);
$item->set_tax_class($fee_data['tax_class']);
$item->set_tax_status($fee_data['tax_status']);
$item->set_total($fee_data['amount']);
$item->save();
}
}
}
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
Once order get updated and after press on recalculate button (to get the correct orders totals) both auto added and updated fee will work nicely.
Related: Add a fee to an order programmatically in Woocommerce 3
Update
Now if it doesn't work for any reason, you should remove the related item to update and add a new one as follows:
add_action( 'woocommerce_order_after_calculate_totals', "custom_order_after_calculate_totals", 10, 2 );
function custom_order_after_calculate_totals( $and_taxes, $order ) {
if ( did_action( 'woocommerce_order_after_calculate_totals' ) >= 2 )
return;
$percentage = 0.03; // Fee percentage
$fee_data = array(
'name' => __('3% CC Fee'),
'amount' => wc_format_decimal( $order->get_total() * $percentage ),
'tax_status' => 'none',
'tax_class' => ''
);
$fee_items = $order->get_fees(); // Get fees
// Add fee
if( empty($fee_items) ){
$item = new WC_Order_Item_Fee(); // Get an empty instance object
$item->set_name( $fee_data['name'] );
$item->set_amount( $fee_data['amount'] );
$item->set_tax_class($fee_data['tax_class']);
$item->set_tax_status($fee_data['tax_status']);
$item->set_total($fee_data['amount']);
$order->add_item( $item );
$item->save(); // (optional) to be sure
}
// Update fee
else {
foreach ( $fee_items as $item_id => $item ) {
if( $item->get_name() === $fee_data['name'] ) {
$item->remove_item( $item_id ); // Remove the item
$item = new WC_Order_Item_Fee(); // Get an empty instance object
$item->set_name( $fee_data['name'] );
$item->set_amount( $fee_data['amount'] );
$item->set_tax_class($fee_data['tax_class']);
$item->set_tax_status($fee_data['tax_status']);
$item->set_total($fee_data['amount']);
$order->add_item( $item );
$item->save(); // (optional) to be sure
}
}
}
}
Update:
Need to use remove_item from $order object.
There is no remove_item function in WC_Order_Item_Fee Class.
After removing item from order invoke the save() function on order.
$order_fees = reset( $order->get_items('fee') );
$fee_data = array(
'name' => __( 'Delivery Fee', 'dsfw' ),
'amount' => wc_format_decimal( $day_fee + $timeslot_fee ),
);
if( !empty( $order_fees ) && $order_fees instanceof WC_Order_Item_Fee ) {
// update fee
if( $order_fees->get_name() === $fee_data['name'] ) {
$order->remove_item( (int)$order_fees->get_id() );
$order->save();
$item = new WC_Order_Item_Fee();
$item->set_name( $fee_data['name'] );
$item->set_amount( $fee_data['amount'] );
$item->set_total( $fee_data['amount'] );
$order->add_item( $item );
$item->save();
}
} else {
// add fee
$item = new WC_Order_Item_Fee();
$item->set_name( $fee_data['name'] );
$item->set_amount( $fee_data['amount'] );
$item->set_total( $fee_data['amount'] );
$order->add_item( $item );
$item->save();
}
}

How to get cart subtotal inside WC_Shipping_Method calculate_shipping() method

So I was trying to create a woocommerce shipping method that takes the cart subtotal and charges a user-defined percentage of the cart subtotal as a shipping fee. As a first step towards this goal, ehat I did was basically like this
class Subtotal_Percentage_Method extends WC_Shipping_Method {
// to store percentage
private $percentage_rate
// constructor that handles settings
// here is where i start calculation
public function calculate_shipping($packages = array()) {
$cost = $this->percentage_rate * 1000;
add_rate(array(
'id' => $this->id,
'label' => $this->title,
'cost' => $cost
));
}
}
This one works. However, it doesn't work when I change the calculate_shipping method to use the cart subtotal in the calculation like this
public function calculate_shipping($packages = array()) {
$subtotal = WC()->cart->subtotal;
$cost = $subtotal * $this->percentage_rate / 100;
add_rate(array(
'id' => $this->id,
'label' => $this->title,
'cost' => $cost
));
}
Can anyone show me what I'm doing wrong?
As this is related to shipping packages (as cart items can be splitted (divided) into multiple shipping packages), you need to use instead the variable $packages argument included in calculate_shipping() method.
So your code will be lightly different without using WC_Cart Object methods:
public function calculate_shipping( $packages = array() ) {
$total = $total_tax = 0; // Initializing
// Loop through shipping packages
foreach( $packages as $key => $package ){
// Loop through cart items for this package
foreach( $package['contents'] as $item ){
$total += $item['total']; // Item subtotal discounted
$total_tax += $item['total_tax']; // Item subtotal tax discounted
}
}
add_rate( array(
'id' => $this->id,
'label' => $this->title,
'cost' => $total * $this->percentage_rate / 100,
// 'calc_tax' => 'per_item'
) );
}
Code goes in functions.php file of your active child theme (active theme). Tested and works.
Note: Here the calculation is made on cart items subtotal after discount (without taxes). You can add easily add make it on cart items subtotal after discount with taxes, replacing:
'cost' => $total * $this->percentage_rate / 100,
by:
'cost' => ($total + $total_tax) * $this->percentage_rate / 100,
You can see how are made the shipping packages looking to:
WC_Cart get_shipping_packages() method source code
If you want to handle also shipping classes and more, check: WC_Shipping_Flat_Rate calculate_shipping() method source code.

Set a min unit displayed price for simple products too in Woocommerce

In Woocommerce I would like to show a minimum price for a simple product and variable products in front of the catalog.
Based on "Set a min unit displayed price for variable products in Woocommerce" answer code, where I have made light changes to the last function as follow:
// Frontend: Display the min price with "From" prefix label for variable products
add_filter( 'woocommerce_variable_price_html', 'custom_min_unit_variable_price_html', 30, 2 );
function custom_min_unit_variable_price_html( $price, $product ) {
$min_unit_price = get_post_meta( $product->get_id(), '_min_unit_price', true );
if( $min_unit_price > 0 ){
$min_price_html = wc_price( wc_get_price_to_display( $product, array( 'price' => $min_unit_price ) ) );
$price = sprintf( __( '%1$s <span class="amount"><div style="font-size:11px;color: #666;text-align: center;">Bulk purchasing</div></span>', 'woocommerce' ), $min_price_html );
}
return $price;
}
I would like to adapt this solution to simple products as well. Any help is appreciated.
From product additional price setting field (see the screenshots below), a minimal unit price replace the displayed product price in Woocommerce shop, archives and single product pages.
Based on "Set a min unit displayed price for variable products in Woocommerce" answer code, the following will work for simple and variable products to be used with Woocommerce 3 and above.
The code (completely revisited and enhanced):
// Backend: Add and display a custom field for simple and variable products
add_action( 'woocommerce_product_options_general_product_data', 'add_custom_price_field_to_general_product_data' );
function add_custom_price_field_to_general_product_data() {
global $product_object;
echo '<div class="options_group hide_if_external">';
woocommerce_wp_text_input(array(
'id' => '_min_unit_price',
'label' => __('Min Unit price', 'woocommerce') . ' (' . get_woocommerce_currency_symbol() . ')',
'description' => __('Enter the minimum unit price here.', 'woocommerce'),
'desc_tip' => 'true',
'value' => str_replace('.', ',', $product_object->get_meta('_min_unit_price') ),
'data_type' => 'price'
));
echo '</div>';
}
// Backend: Save the custom field value for simple and variable products
add_action( 'woocommerce_admin_process_product_object', 'save_product_custom_price_field', 10, 1 );
function save_product_custom_price_field( $product ) {
if ( isset($_POST['_min_unit_price']) ) {
$product->update_meta_data( '_min_unit_price', wc_clean( wp_unslash( str_replace( ',', '.', $_POST['_min_unit_price'] ) ) ) );
}
}
// Frontend variable products: Display the min price with "From" prefix label
add_filter( 'woocommerce_variable_price_html', 'custom_min_unit_variable_price_html', 10, 2 );
function custom_min_unit_variable_price_html( $price, $product ) {
if( $min_unit_price = $product->get_meta('_min_unit_price') ){
$price = wc_price( wc_get_price_to_display( $product, array( 'price' => $min_unit_price ) ) );
$price .= ' <span class="prefix" style="font-size:11px;color: #666;text-align: center;">'.__('Bulk purchasing').'</span>';
}
return $price;
}
// Frontend simple products: Display the min price with "From" prefix label
add_filter( 'woocommerce_get_price_html', 'custom_min_unit_product_price_html', 10, 2 );
function custom_min_unit_product_price_html( $price, $product ) {
if( $product->is_type('simple') && $min_unit_price = $product->get_meta('_min_unit_price') ){
$price = wc_price( wc_get_price_to_display( $product, array( 'price' => $min_unit_price ) ) );
$price .= ' <span class="prefix" style="font-size:11px;color: #666;text-align: center;">'.__('Bulk purchasing').'</span>';
}
return $price;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
In backend, for simple and variables products:
In frontend, for simple and variables products (in shop, archives and single product pages)

Woocommerce custom shipping price inclusive with tax

I have custom shipping method to add calculate the shipping, and using the below code
public function calculate_shipping( $package ) {
$postcode_data = $package['destination']['postcode'];
if(!empty($postcode_data)){
$weight = 0;
$zone_data = checkzone($postcode_data);
if(isset($package['destination']['postcode'])){
if(!empty($zone_data)){
$weight = WC()->cart->cart_contents_weight; // get cart total weight
if($weight <= 100){
$weight_range = ceil($weight / 10 );
}
elseif(($weight > 100 ) && ($weight < 150) ){
$weight_range = 11 ;
}
if($weight >= 150)
{
$weight_range = 12 ;
}
$pricing = getpricing($zone_data , $weight_range);
if($pricing > 0){
$rate = array(
'id' => $this->id,
'label' => 'Delivery',
'cost' => $pricing,
'calc_tax' => 'per_order'
);
// Register the rate
$this->add_rate( $rate );
wc_clear_notices();
}
} // if zone data
}
}
else {
$rate = array(
'id' => $this->id,
'label' => 'Delivery',
'cost' => 0,
'calc_tax' => 'per_order'
);
// Register the rate
$this->add_rate( $rate );
//wc_clear_notices();
//wc_add_notice( 'Please enter zipcode to calculate the shipping', 'error' );
}
} // calculate shipping
But at front end it's adding tax amount exclusive the tax. For the products tax is working fine but for the shipping is exclusive the price, I want inclusive of the shipping price for the tax.

Categories