I have been trying to make a Coupon that makes a discount based on the cart weight when a coupon is used.
I have achieved something using a fixed amount coupon (with fixed value zero) and adding fees :
$cart_weight = $cart->get_cart_contents_weight() ;
and
$cart->add_fee( 'Discount ' . $coupon_code, $cost* -1 , false );
Even it works ,I dont like this, I would like to have it as a regular coupon. Is there any way? Thanks
Sure, it's possible, you only have to create a unique coupon and apply it to the cart instead of adding a negative fee
//ADD coupon based of cart weight
add_action( 'woocommerce_before_calculate_totals', 'generate_coupon_based_on_total_weight' );
function generate_coupon_based_on_total_weight( $cart ) {
// Set the threshold weight for applying the coupon
$threshold_weight = 10;
$discount_amount = 2;
$coupon_prefix = 'weight_';
$total_weight = $cart->get_cart_contents_weight() ;
// Check if the total weight is greater than or equal to the threshold weight
if ( $total_weight >= $threshold_weight ) {
// Check if the coupon code with the "weight_" prefix has already been applied
if ( has_coupon_code_with_prefix( $coupon_prefix ) ) {
// Coupon code already applied, do nothing
return;
}
// Generate a unique coupon code
$coupon_code = uniqid( $coupon_prefix );
// Create a new coupon object
$coupon = new WC_Coupon();
$coupon->set_code( $coupon_code );
$coupon->set_amount( $discount_amount ); // Set the coupon amount
$coupon->set_individual_use( true ); // Set the coupon to be used only once
$coupon->set_usage_limit( 1 ); // Set the usage limit to 1
$coupon->save(); // Save the coupon
// Apply the coupon to the cart
$cart->add_discount( $coupon_code );
} else {
// Total weight is less than the threshold, remove the coupon if it exists
remove_coupon_from_cart( $coupon_prefix );
}
}
function has_coupon_code_with_prefix( $prefix ) {
// Get the applied coupons
$applied_coupons = WC()->cart->get_applied_coupons();
// Check if there are any applied coupons
if ( ! empty( $applied_coupons ) ) {
// Iterate through the applied coupons
foreach ( $applied_coupons as $coupon_code ) {
// Check if the coupon code starts with the specified prefix
if ( strpos( $coupon_code, $prefix ) === 0 ) {
// There is a coupon code applied that starts with the specified prefix
return true;
}
}
}
// No coupon code was found that starts with the specified prefix
return false;
}
function remove_coupon_from_cart($prefix) {
global $woocommerce;
// Get the cart object
$cart = $woocommerce->cart;
// Get the coupon codes applied to the cart
$coupon_codes = $cart->get_coupons();
// Loop through the coupon codes and remove any that start with the specified prefix
foreach ($coupon_codes as $code => $coupon) {
if (strpos($code, $prefix) === 0) {
$cart->remove_coupon($code);
// Save the updated cart
$cart->calculate_totals();
}
}
}
The main idea of this function is to apply a discount coupon to the user's cart if the total weight of the products in the cart meets a certain threshold, and to remove the coupon if the total weight falls below the threshold.
The function is triggered by the woocommerce_before_calculate_totals action, which occurs before the total cost of the products in the cart is calculated.
The function first sets a few variables:
$threshold_weight: This is the weight that the total weight of the products in the cart must meet or exceed in order for the coupon to be applied.
$discount_amount: This is the amount of the discount that will be applied if the coupon is applied.
$coupon_prefix: This is a prefix that will be added to the coupon code to make it unique.
The function then calculates the total weight of the products in the cart using the get_cart_contents_weight method.
Next, the function checks if the total weight is greater than or equal to the threshold weight. If it is, the function generates a unique coupon code using the uniqid function and the $coupon_prefix variable, and creates a new coupon object using the WC_Coupon class. The coupon object is then configured with the discount amount, individual use, and usage limit. Finally, the coupon is saved and applied to the cart using the add_discount method.
If the total weight is less than the threshold weight, the function removes the coupon from the cart if it exists by calling the remove_coupon_from_cart function and passing in the $coupon_prefix variable.
Overall, this function allows you to apply a discount coupon to the user's cart if the total weight of the products in the cart meets a certain threshold, and to remove the coupon if the total weight falls below the threshold.
Related
I set 2 methods of delivery:
Express shipping 48 hours at a cost which es a % of the total cart $ amount
Normal Free shipping in 29 days
The functionality that I want is the following:
If the stock of the product is = 0 or less than 0 (backorder is allowed), the Express shipping option should be hidden. Just must show the Normal free shipping option.
If the stock of the product is >0, both options should be shown.
If I selected more than one product, and one or more of the products does not have stock, the Express shipping option also should be hidden.
From another answer related to this question, I tried the script showed in the answer.
The problem is the following: in the last part of the script there is a mention to a variable in an external plugin.
How I adapt the code to a more generic woocommerce without that plugin?
Script:
/* !Hide Shipping Options Woocommerce */
add_filter( 'woocommerce_available_shipping_methods', 'hide_shipping_based_on_quantity' , 10, 1 );
function check_cart_for_oos() {
// load the contents of the cart into an array.
global $woocommerce;
$found = false;
foreach ( $woocommerce->cart->get_cart() as $cart_item_key => $values ) {
$_product = $values['data'];
$_product_quantity = $_product->get_stock_quantity();
$_cart_quantity = $values['quantity'];
if (($_product_quantity <= 0) || ($_cart_quantity > $_product_quantity)) {
$found = true;
break;
}
}
return $found;
}
function hide_shipping_based_on_quantity( $available_methods ) {
// use the function check_cart_for_oos() to check the cart for products with 0 stock.
if ( check_cart_for_oos() ) {
// remove the rate you want
unset( $available_methods['table_rate_shipping_next-day'] ); // Replace "table_rate_shipping_next-day" with "table_rate_shipping_your-identifier".
}
// return the available methods without the one you unset.
return $available_methods;
}
From: Remove next day shipping method for out of stock items in Woocommerce
In that answer, it's already mentioned to use your own shipping method identifier. So you just need to replace the shipping method identifier with yours.
// use the function check_cart_for_oos() to check the cart for products with 0 stock.
if ( check_cart_for_oos() ) {
// remove the rate you want
unset( $available_methods['USE_YOUR_OWN_IDENTIFIER_HERE'] ); // Replace "table_rate_shipping_next-day" with "table_rate_shipping_your-identifier".
}
I see when I look at an order it will show the specific item that was refunded if the whole order wasn't.
Is there a way to find using the WC_Order_Item_Product if an item was refunded? Also, is there a way to get the discount amount that shows up below the item in the order view?
I'm currently doing it manually but if there is a function for it already, I would rather use it.
To get refunded orders you could use some dedicated WC_Order methods like the following ones that have Item Id as argument:
$item_qty_refunded = $order->get_qty_refunded_for_item( $item_id ); // Get the refunded amount for a line item.
$item_total_refunded = $order->get_total_refunded_for_item( $item_id ); // Get the refunded amount for a line item.
You can access an array WC_Order_Refund Objects for this order using get_refunds() method:
For each refund, you can use the WC_Order_Refund methods.
For each refund, you can access items using WC_Abstract_Order method get_items() that will give you the refunded items for the current Order refund.
Each refund "line" item is a WC_Order_Item_Product (with negative values in general) see this related answer: Get Order items and WC_Order_Item_Product in WooCommerce 3
So you can use the following example code:
// Get the WC_Order Object instance (from the order ID)
$order = wc_get_order( $order_id );
// Get the Order refunds (array of refunds)
$order_refunds = $order->get_refunds();
// Loop through the order refunds array
foreach( $order_refunds as $refund ){
// Loop through the order refund line items
foreach( $refund->get_items() as $item_id => $item ){
## --- Using WC_Order_Item_Product methods --- ##
$refunded_quantity = $item->get_quantity(); // Quantity: zero or negative integer
$refunded_line_subtotal = $item->get_subtotal(); // line subtotal: zero or negative number
// ... And so on ...
// Get the original refunded item ID
$refunded_item_id = $item->get_meta('_refunded_item_id'); // line subtotal: zero or negative number
}
}
To get the order items discounted values that appear in admin order edit pages you will use the following code:
// Get the WC_Order Object instance (from the order ID)
$order = wc_get_order($order_id);
// Loop through the order refund line items
foreach( $order->get_items() as $item_id => $item ){
$line_subtotal = $item->get_subtotal();
$line_total = $item->get_total();
$line_subtotal_tax = $item->get_subtotal_tax();
$line_total_tax = $item->get_total_tax();
// Get the negative discount values
$line_discount = $line_total - $line_subtotal; // (Negative number)
$line_discount_tax = $line_total_tax - $line_subtotal_tax; // (Negative number)
}
Related Answers:
How to get WooCommerce order details
Get Order items and WC_Order_Item_Product in WooCommerce 3
Woocommerce - Getting the order item price and quantity.
If you are using get_qty_refunded_for_item( $item_id ) or get_total_refunded_for_item( $item_id ) returns 0 use absint().
$item_qty_refunded = $order->get_qty_refunded_for_item( $item_id ); // Get the refunded amount for a line item.
$item_total_refunded = $order->get_total_refunded_for_item( $item_id ); // Get the refunded amount for a line item.
I am on the verge of losing it with this problem. So what I am attempting is to apply Zero rate tax rate when a specific coupon ('12345') is applied to the order. This site also has the plugin WooCommerce Distance Rate Shipping installed which adds its own costs and tax to the order based on billing distance.
While the billing address fields are empty, the following code successfully detects the coupon in question and sets tax rate to 'Zero Rate':
function apply_matched_coupons() {
$coupon_code = '12345';
if ( !is_admin() && WC()->cart->has_discount( $coupon_code ) ) {
$tax_class = 'Zero rate';
}
return $tax_class;
};
add_action( 'woocommerce_product_tax_class','apply_matched_coupons' );
But when an address is typed in the billing fields, the Woocommerce order update AJAX works and the WooCommerce Distance Rate Shipping plugin reads the billing addresss, re-checks its rules, applies its costs and also adds its tax. Hence, overwriting my Zero Tax rate set earlier. Screenshot is attached. I believe a hook like woocommerce_checkout_update_order_review may work which fires each time the order is updated on checkout, but can't figure out exactly how to set it.
The plugin's page here does show an example to set the $rule_cost to 0 with this code:
add_filter( 'woocommerce_distance_rate_shipping_rule_cost_distance_shipping', function( $rule_cost, $rule, $distance, $package ) {
$order_total = $package['contents_cost'];
if ( $order_total > 100 && $distance <= 5 ) {
$rule_cost = 0;
}
return $rule_cost;
}, 10, 4 );
Could this help in any way?
screenshot of extra tax added by the said plugin
Currently my two options for shipping are regular shipping (using the Flexible Shipping plugin) and local pickup. I would like my Completed Order emails to be customized according to the shipping method used for the order (e.g. I'd like the header to say "We've shipped your order!" for regular shipping and "Your order is ready for pickup!" for local pickup).
Here's my first attempt at modifying the text through my child theme's functions.php (code based on LoicTheAztec answer in this thread). Currently, instead of inserting the desired text, it shows the default text that I entered in Woocommerce>Settings>Emails>Order Completed>Completed order>Email heading, regardless of shipping method selected.
add_action( 'woocommerce_email_header ', 'modify_header_by_shipping', 10, 2 );
function modify_header_by_shipping( $email_heading, $email ) {
// Only modify "Customer Completed Order" email notification
if( 'customer_completed_order' != $email->id ) return;
//Initialize variable
$found = false;
// Get $order object from $email
$order = $email->object;
// Iterating through Order shipping methods
foreach($order->get_shipping_methods() as $value){
$rate_id = $value->get_method_id(); // Get the shipping rate ID
if ( 'shipping_method_0_local_pickup5' == $rate_id )
$found = true;
}
if ($found)
echo '<p>'.__("Your order is ready for pickup!","woocommerce").'</p>';
else
echo '<p>'.__("We've shipped your order!","woocommerce").'</p>';
}
Here's my ordering page: https://carrickseeds.ca/checkout/
I have a website where customer chooses a zone in a dropdown in billing fields. These are 4 zones: zone1, zone2 and zone3 as an example.
The idea is that if customer chose zone1 and total cart is less than $1000, customer needs to pay a shipping rate of $100. If more than $1000, customer gets free shipping.
I need to replicate that for the other 2 zones with different cart amounts and shipping rates. Is this possible?
Right now, I have the following working code but customer has to manually choose a flat rate. Problem is, of course, customers... If free shipping is displayed, they will choose that one knowing they dont apply for that option when total amount is less for their zone.
/* Change shipping based on total cost purchased. */
add_filter( 'woocommerce_package_rates', 'bbloomer_woocommerce_tiered_shipping', 10, 2 );
function bbloomer_woocommerce_tiered_shipping( $rates, $package ) {
$threshold_centro = 1000;
$threshold_hasta_el_5 = 1500;
$threshold_arelauquen = 2000;
if ( WC()->cart-> cart_contents_total < $threshold_centro ) {
unset( $rates['free_shipping:9'] );
}
elseif ( WC()->cart-> cart_contents_total < $threshold_hasta_el_5 ) {
unset( $rates['flat_rate:2'] );
}
elseif ( WC()->cart-> cart_contents_total < $threshold_arelauquen ) {
unset( $rates['flat_rate:2'], $rates['flat_rate:3'] );
}
else {
unset( $rates['flat_rate:2'], $rates['flat_rate:3'], $rates['flat_rate:4'], $rates['flat_rate:5'], $rates['flat_rate:6'] );
}
return $rates;
}
and the custom field is this one: billing_wcccf_id_qXZZegrC774HULq
Try to set a WC session when you trigger the selectbox and the use it in your if statement to get the right shipping rate or use a switch
WC->session->set('your session name', 'value')
WC->session->get('your session')