WooCommerce change cart tax amount programmatically - php

I want to change the tax amount of the products in my cart programmatically, but I can't get my head around it. Although I think I'm using the proper action hook to alter my tax rates, nothing is changing eventually on the frontend.
When I execute the function below and dump the result in my for loop, I can see that the values in the cart item array are altered.
add_action('woocommerce_before_calculate_totals', 'okappi_alter_price_cart', 9999);
function okappi_alter_price_cart($cart) {
foreach($cart->get_cart() as $cart_item_key => $cart_item) {
$cart_item['line_tax_data']['subtotal'] = 10;
$cart_item['line_tax_data']['total'] = 10;
$cart_item['line_subtotal_tax'] = 10;
$cart_item['line_tax'] = 10;
dd($cart_item);
}
}
But when I dump the cart item after the for loop, nothing has changed.
add_action('woocommerce_before_calculate_totals', 'okappi_alter_price_cart', 9999);
function okappi_alter_price_cart($cart) {
foreach($cart->get_cart() as $cart_item_key => $cart_item) {
$cart_item['line_tax_data']['subtotal'] = 10;
$cart_item['line_tax_data']['total'] = 10;
$cart_item['line_subtotal_tax'] = 10;
$cart_item['line_tax'] = 10;
// dd($cart_item);
}
dd($cart_item);
}
So what am I overlooking here? How can I change the tax amount of my individual products before they are calculated on the cart & checkout page?

Related

How to change product prices in WooCommerce cart when product amount is more than one?

I am a PHP noob. I'm helping my friend set up a WooCommerce shop. He sells digital products. His pricing plan is as follows:
-buy the first copy of a product for full price (10€)
-buy all other copies for a discounted price (1€)
So, when a customer adds a product to their shopping cart, the first copy is full price. The prices of any additional copies of said product are added to the cart with discounted prices.
According to the pricing plan above, if a customer has two copies of a product in their cart, the total cart price should be 11€.
I've cobbled together this code:
add_action('woocommerce_before_calculate_totals', 'change_cart_price', 9999);
function change_cart_price($price_html) {
foreach( WC()->cart->get_cart() as $cart_item ){
//get the quantity of each separate product in the cart
$quantity = $cart_item['quantity'];
//if the quantity of an item is higher than one,
//decrease the for loop iterations by one
if ($quantity > 1) {
$iterations = $quantity -1;
//change the prices of all but one copy of the product
for ($i = 0; $i < $iterations; $i++) {
$price = $cart_item['data']->get_price();
$cart_item['data']->set_price(1);
}
}
}
return $price_html;
}
This code fires when the amount of a product in the cart is above one, but it changes the prices of all copies of the product. So, the cart total for two copies of a single product is 2€, instead of 11€.
Obviously, my "for" loop sucks. I've Googled different resources, but they tend to focus on changing the price of all the items in the cart.
I've thought about using an array that contains all the products. Maybe if I run through it and change prices according to Product ID?
Any help would be appreciated. Cheers.
UPDATE
I've written some code, based on 7uc1f3r's advice.
The first bit changes a product's price on the Shop page, whenever they have added more than one item of said product into their Shopping Cart. This code only applies to products the Customer has not bought previously:
add_filter('woocommerce_get_price_html', 'change_shop_price', 9999, 2);
function change_shop_price($price_html, $product)
{
// only on frontend
if (is_admin()) return $price_html;
// if customer not logged in, don't apply
if (!wc_current_user_has_role('administrator')) return;
if (empty(WC()->cart->get_cart())) {
return $price_html;
} else {
//if customer hasn't yet bought the product
if (!wc_customer_bought_product('', get_current_user_id(), $product->get_id())) {
$cart = WC()->cart->get_cart();
$product_id = $product->get_id();
$product_cart_id = WC()->cart->generate_cart_id($product_id);
$in_cart = WC()->cart->find_product_in_cart($product_cart_id);
//check product quantity in cart
if ($in_cart) {
$quantity = $cart[$product_cart_id]['quantity'];
if ($quantity >= 1) {
$orig_price = wc_get_price_to_display($product);
//what is the desired price for additional copies of product
$copy_price = 1;
//each additional item increases the price by one
$total_price = $orig_price + ($copy_price * ($quantity - 1));
//the price displayed on the shop page
$price_html = wc_price($total_price / $quantity);
}
}
}
return $price_html;
}
}
So, that's the Shop Page sorted. This second code does the same on the Cart Page:
add_action('woocommerce_before_calculate_totals', 'change_cart_price', 9999);
function change_cart_price()
{
if (is_admin() && !defined('DOING_AJAX')) return;
if (did_action('woocommerce_before_calculate_totals') >= 2) return;
if (!wc_current_user_has_role('administrator')) return;
if (empty(WC()->cart->get_cart())) {
return;
} else {
foreach (WC()->cart->get_cart() as $cart_item) {
$product = $cart_item['data'];
if (!wc_customer_bought_product('', get_current_user_id(), $product->get_id())) {
$quantity = $cart_item['quantity'];
if ($quantity > 1) {
$copy_price = 1;
$price = $cart_item['data']->get_price();
$total_price = $price + ($copy_price * ($quantity - 1));
$individual_product_price = $total_price / $quantity;
$cart_item['data']->set_price($individual_product_price);
}
}
}
}
}
There's most likely redundant bits of code in there somewhere. I should also mention that I've gotten most of the code from this page/site:
https://www.businessbloomer.com/woocommerce-set-override-product-price-programmatically/
Thanks to 7uc1f3r for helping out. If anyone wants to suggest changes/revisions to this code, I would be happy to listen.

Auto add a variable quantity of specific product based on WooCommerce cart subtotal

I'm trying to add a gift product to a shopping cart based upon the total cost spent by a customer. It's for a restaurant that sells gift vouchers.
The scheme runs as follows: for every £50 that a customer spends in a single transaction, they automatically receive a £10 gift voucher for free. So, if they spend £100 on gift vouchers, they receive 2x £10 gift vouchers for free (and so on).
To keep things as simple as possible, we're limiting denominations of gift vouchers (£25 - £250 increasing in £25 denominations).
I don't particularly want to pay for/install another plugin that will need to be updated/managed, etc. If I'm honest, it's only for the Christmas period so a snippet of code to do this would be far better.
The way I see it working is that a customer adds the vouchers to the cart and when they go to the cart it shows they've also got the additional 'free' vouchers in there too.
I've found some code online and made some changes to it. The code is as follows:
function auto_add_specific_product_to_cart() {
// select product ID
$product_id = 1428;
// if cart empty, add it to cart
if ( WC()->cart->get_cart_total() <= 99 ) {
WC()->cart->add_to_cart( $product_id, 1 ); }
else if ( WC()->cart->get_cart_total() <= 149 ) {
WC()->cart->add_to_cart( $product_id, 2 ); }
else if ( WC()->cart->get_cart_total() <= 199 ) {
WC()->cart->add_to_cart( $product_id, 3 ); }
else if ( WC()->cart->get_cart_total() <= 249 ) {
WC()->cart->add_to_cart( $product_id, 4 ); }
}
I'm having problems with it right, left and centre. It keeps adding the free vouchers to the shopping cart so eventually customers end up with 20 or 30 vouchers there; the PHP for recognising the different thresholds (50 to 99, 100 to 149, etc) aren't working properly. I'm probably making this far too complicated and not thinking it through but a bit of help would be great.
I've found plenty of code online for discounting products but nothing that gives something away for free.
Here a variable quantity of a "specific product voucher" will be auto added to cart based on cart subtotal.
The code below is based on Add free gifted product for a minimal cart amount in WooCommerce answer code. The code automate the generation of cart subtotal thresholds (min and max amounts) and the related quantity of the voucher product to be added to cart.
The number set in $max_quantity variable, determines the number of iterations on the FOR loop (so the number of different generated thresholds and the max quantity of voucher product that can be added to cart).
// Auto add a quantity of voucher product based on cart subtotal
add_action( 'woocommerce_before_calculate_totals', 'auto_add_voucher_product_based_on_subtotal' );
function auto_add_voucher_product_based_on_subtotal( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// Settings
$voucher_product_id = 37; // Voucher product Id
$max_quantity = 20; // Here set the max item quantity (for voucher product)
$min_subtotal = 50; // Starting threshold min amount
$cart_subtotal = 0; // Initializing
// Loop through cart items (first loop)
foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
// When voucher product is is cart
if ( $voucher_product_id == $cart_item['product_id'] ) {
$voucher_key = $cart_item_key;
$voucher_qty = $cart_item['quantity'];
// $cart_item['data']->set_price(0); // (optional) set the price to zero
} else {
$cart_subtotal += $cart_item['line_total'] + $cart_item['line_tax']; // Get subtotal
}
}
// If voucher product is not already in cart, add it
if ( ! isset($voucher_key) && $cart_subtotal >= $min_subtotal ) {
$cart->add_to_cart( $voucher_product_id );
}
// Check vouvher product quantity and adjust it depending on the subtotal amount.
else {
// Loop dynamically through subtotal threshold min and max amounts setting the right quantity
// $i is the min amount, $j the max amount and $q the related quantity to check
for ( $i = $min_subtotal, $j = 100, $q = 1; $q < $max_quantity; $i += 50, $j += 50, $q++ ) {
if ( $cart_subtotal >= $i && $cart_subtotal < $j && $voucher_qty != $q ) {
$cart->set_quantity( $voucher_key, $q );
}
}
if ( $cart_subtotal >= $i && $voucher_qty != $q ) {
$cart->set_quantity( $voucher_key, $q );
}
}
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.

How can I add a discount to the cart total in woocommerce?

I am trying to add some additional discounts to the cart total and I tried this code but it's not quite working for me.
function mysite_box_discount( ) {
global $woocommerce;
$total_disc = 10;
// Alter the cart discount total
$woocommerce->cart->discount_total = $total_disc;
}
add_action('woocommerce_calculate_totals', 'mysite_box_discount');
I also tried adding $cart as an argument to the function, but it didn't work.
I also tried $cart->discount_total but it is not working for me either.
Try this code
function custom_wc_add_discount() {
$total_disc = 10;
WC()->cart->add_fee( 'Discount note', -$total_disc );
}
add_action( 'woocommerce_cart_calculate_fees','custom_wc_add_discount' );

Building a custom module using PHP

I am currently building a module for an ecommerce website (Lemonstand). My shop sells both normal books and ebooks, and charge different rates of for shipping based on the subtotal of all items in the cart ($7 shipping cost for subtotal of less than $20, $14 shipping cost for between $20 and $50..etc). Currently the website just uses the subtotal of all items in the cart to determine which rate should be applied. The problem is, I want only the subtotal of normal books to be used for calculating shipping, because obviously ebooks don't need shipping.
Lemonstand platform has some built_in functions that I'm using to help with this. update_shipping_quote function is called just before shipping cost is calculated. It will be used to change the subtotal of cart items so that shipping cost can be calculated using the subtotal of non-ebooks instead.
Here is the API documentation for the function: https://v1.lemonstand.com/api/event/shop:onupdateshippingquote/
Here is the bit of code that's giving me trouble. I want to know if the value I get at the end ($non_ebook_subtotal) actually contains the value it's supposed to.
If anyone can come up with a better method for doing what I'm trying to do, please share.
//$params is an array containing things like individual item price
//Here I get the cart items and put them into var
public function
update_shipping_quote($shipping_option, $params) {
$cart_items = $params['cart_items'];
//find all products that are ebooks
foreach ($cart_items-> items as $item)
{
$product = $item->product;
if($product->product_type->code == 'ebook') {
$isEbook = true;
}
}
//add price of all ebooks into $ebook_subtotal
foreach ($cart_items as $item) {
$product = $item -> product;
if ($isEbook == true) {
$ebook_subtotal = $ebook_subtotal + total_price($product);
}
}
//Calculating the subtotal of only the non-ebook products
$non_ebook_subtotal = $params['total_price'] - $ebook_subtotal;
//This returns the non_ebook_subtotal to be used for calculating shipping cost
return array('total_price' => $non_ebook_subtotal);
}
Thanks
// get all variables needed
$totalprice = $params['total_price'];
$items = $params['cart_items'];
foreach ($items AS $item) {
// if is ebook
if ($item->product->product_type->code == 'ebook') {
// minus price of the item from total
$totalprice -= total_price($item->product);
}
}
return $totalprice;

Update magento cart total before saving order

I need to remove some products from cart after the customer press the "Place order" button. I can remove the products with a custom event and observer and update the cart products, but i cant update the cart total.
In the observer i try to update the price with:
$granTotal = $cart->getGrandTotal() - $cart->getShippingAmount();
$cart->setGrandTotal($GranTotal - $sum)->save();
$baseGranTotal = $cart->getBaseGrandTotal() - $cart->getShippingAmount();
$cart->setBaseGrandTotal($baseGranTotal - $sum)->save();
$cart->setTotalsCollectedFlag(false)->collectTotals();
$cart->save();
But the total of the cart don't change. I find that the total is stored in the cart address too and used to set the cart total in Mage_Sales_Model_Quote collectTotals() function.
Here:
$this->setSubtotal((float) $this->getSubtotal() + $address->getSubtotal());
$this->setBaseSubtotal((float) $this->getBaseSubtotal() + $address->getBaseSubtotal());
Then i tried to change the address total before calling the collectTotals(), but the cart total still don't change.
My custom event is triggered at the beginning in Mage_Checkout_Model_Type_Onepage saveOrder() function.
I am running out of ideas of how to update the total of the cart.
My observer looks like this:
public function changeCart(){
$cart = Mage::getSingleton('checkout/session')->getQuote();
$data = array();
$inquiry = array();
$sum = 0;
//remove out of stock items to save them in custom model
foreach ($cart->getAllItems() as $item) {
if($item->getProduct()->getData('is_in_stock') == 0){
$cart->removeItem($item->getItemId())->save();
$inquiry[] = $item->getProduct();
$data['qty'][$item->getProduct()->getId()] = $item->getData('qty');
$sum += $item->getProduct()->getFinalPrice();
$dobavljivost = $item->getProduct()->getResource()->getAttributeRawValue($item->getProduct()->getId(), 'dobavljivost', Mage::app()->getStore());
Mage::getSingleton('core/session')->setDobavljivost($dobavljivost);
}
}
foreach($cart->getAllAddresses() as $a){
$a->setGrandTotal(0);;
$a->setBaseGrandTotal(0);
$a->setData('subtotal', 0);
$a->setData('base_subtotal', 0);
}
$granTotal = $cart->getGrandTotal() - $cart->getShippingAmount();
$cart->setGrandTotal($GranTotal - $sum)->save();
$baseGranTotal = $cart->getBaseGrandTotal() - $cart->getShippingAmount();
$cart->setBaseGrandTotal($baseGranTotal - $sum)->save();
$cart->setTotalsCollectedFlag(false)->collectTotals();
$cart->save();
}
Thank you for any help!
For recomputing totals of the cart you can try do this
$cart = Mage::getSingleton('checkout/session')->getQuote();
foreach ($cart->getAllAddresses() as $address)
{
$address->unsetData('cached_items_nonnominal');
$address->unsetData('cached_items_nominal');
}
$cart->setTotalsCollectedFlag(false);
$cart->collectTotals();

Categories