Custom Coupon type woocommerce wordpress - php

I need to create custom coupon type. Because i had custom calculation for custom coupon. Anyone out there help me.
Coupon calculation:
the cart has one product and value is 5000, then custom coupon code applied. The cart total need to change 2000. Similarly the cart has 2 product and value is 7000. If the custom coupon code applied, then cart total need to be 4000.
So the coupon need to make the cart total as flat 2000 for one product

In newer versions of WooCommerce, you will need to
Register a custom coupon type
Validate the coupon
Calculate & Apply the discount
Register custom coupon type
add_filter( 'woocommerce_coupon_discount_types', 'custom_coupon_type',10, 1);
function custom_coupon_type( $discount_types ) {
$discount_types['my_type'] =__( 'My New Coupon Type', 'woocommerce' );
return $discount_types;
}
Validate the coupon
add_filter('woocommerce_coupon_is_valid_for_product', 'validate_custom_coupon', 10, 4);
function validate_custom_coupon($valid, $product, $coupon, $values){
if ( ! $coupon->is_type( array( 'my_type' ) ) ) {
return $valid;
}
$product_cats = wp_get_post_terms( $product->id, 'product_cat', array( "fields" => "ids" ) );
// SPECIFIC PRODUCTS ARE DISCOUNTED
if ( sizeof( $coupon->product_ids ) > 0 ) {
if ( in_array( $product->id, $coupon->product_ids ) || ( isset( $product->variation_id ) && in_array( $product->variation_id, $coupon->product_ids ) ) || in_array( $product->get_parent(), $coupon->product_ids ) ) {
$valid = true;
}
}
// CATEGORY DISCOUNTS
if ( sizeof( $coupon->product_categories ) > 0 ) {
if ( sizeof( array_intersect( $product_cats, $coupon->product_categories ) ) > 0 ) {
$valid = true;
}
}
// IF ALL ITEMS ARE DISCOUNTED
if ( ! sizeof( $coupon->product_ids ) && ! sizeof( $coupon->product_categories ) ) {
$valid = true;
}
// SPECIFIC PRODUCT IDs EXLCUDED FROM DISCOUNT
if ( sizeof( $coupon->exclude_product_ids ) > 0 ) {
if ( in_array( $product->id, $coupon->exclude_product_ids ) || ( isset( $product->variation_id ) && in_array( $product->variation_id, $coupon->exclude_product_ids ) ) || in_array( $product->get_parent(), $coupon->exclude_product_ids ) ) {
$valid = false;
}
}
// SPECIFIC CATEGORIES EXLCUDED FROM THE DISCOUNT
if ( sizeof( $coupon->exclude_product_categories ) > 0 ) {
if ( sizeof( array_intersect( $product_cats, $coupon->exclude_product_categories ) ) > 0 ) {
$valid = false;
}
}
// SALE ITEMS EXCLUDED FROM DISCOUNT
if ( $coupon->exclude_sale_items == 'yes' ) {
$product_ids_on_sale = wc_get_product_ids_on_sale();
if ( isset( $product->variation_id ) ) {
if ( in_array( $product->variation_id, $product_ids_on_sale, true ) ) {
$valid = false;
}
} elseif ( in_array( $product->id, $product_ids_on_sale, true ) ) {
$valid = false;
}
}
return $valid;
}
Calculate & Apply Discount
add_filter('woocommerce_coupon_get_discount_amount', 'wc_cpn_disc', 10, 5);
function wc_cpn_disc($discount, $discounting_amount, $cart_item, $single, $coupon) {
// IF TYPE MATCHES PERFORM CUSTOM CALCULATION
if ($coupon->type == 'my_type')
$discount = $cart_item['quantity'] * 2000;
return $discount;
}
Sources:
#raju_odi's answer on this exact question
#Chris Morris's answer to Woocommerce custom coupon type with custom discount always return 0

Please use below code in your active theme's function.php
function custom_discount_type( $discount_types ) {
$discount_types['custom_discount'] =__( 'custom discount', 'woocommerce' );
return $discount_types;
}
// add the hooks
add_filter( 'woocommerce_coupon_discount_types', 'custom_discount_type',10, 1);
//function to get coupon amount for "custom_discount"
function woocommerce_coupon_get_discount_amount($discount, $discounting_amount, $cart_item, $single, $coupon) {
if ($coupon->code == 'custom'){
//echo "yes custom discount"; //if $coupon->type == 'fixed_cart' or 'percent' or 'fixed_product' or 'percent_product' The code Works
$discount = $cart_item['quantity'] * 2000;
return $discount;
} else {
return $discount;
}
}
//add hook to coupon amount hook
add_filter('woocommerce_coupon_get_discount_amount', 'woocommerce_coupon_get_discount_amount', 10, 5);
Note : Please add your added coupon name here on this line if ($coupon->code == 'your_added_coupon_here')
Working fine and tested.

Related

WooCommerce | Add notification in shipping rate table based on the total weight of the cart

I am trying to add a notification to the shipping totals row on the WooCommerce shopping cart page as soon as two conditions are met.
Shipping method local_pickup is available
AND
the total weight of the products in my cart are above 14999 grams.
I've been playing around with the code uderneath, which already hides certain shipping methods (succesfully) but I'm unable to add a simple notification when the conditions are met.
add_filter( 'woocommerce_package_rates', 'woocommerce_shipping_notification', 9999, 2 );
function woocommerce_shipping_notification( $rates, $package ) {
if ( WC()->cart->get_cart_contents_weight() > 14999 ) {
if ( isset( $rates['local_pickup:1'] ) )
unset( $rates['flat_rate:2'], $rates['local_pickup:7'] );
echo "My notification";
}
return $rates;
}
if ( !class_exists( 'WooCommerce' ) ) return;
add_action('wp', function() {
if ( is_cart() && !WC()->cart->is_empty() ) {
$cart = WC()->cart;
$products = $cart->get_cart_contents();
$cat_ids = array('20', '22');
$product_categories = array();
$product_categories_ids = array();
foreach($cat_ids as $key => $cat_id) {
$product_categories = array_merge(
$product_categories,
get_categories(array(
'taxonomy' => 'product_cat',
'child_of' => $cat_id,
))
);
}
foreach($product_categories as $key => $category) {
$product_categories_ids[] = $category->term_id;
}
$categories_included = function() use ( $products, $product_categories_ids ) {
foreach( $products as $key => $product ) {
$terms = wp_get_object_terms(array($product['product_id']), 'product_cat');
foreach( $terms as $_key => $term ) {
if ( in_array( $term->term_id, $product_categories_ids) )
return true;
}
}
return false;
};
$only_pick_up = $categories_included() || $cart->get_cart_contents_weight() > 15000;
if ( $only_pick_up ) {
add_action( 'woocommerce_before_cart', function() {
echo
'<div>
<h3>Please contact us to discuss the delivery options</h3>
</div>';
});
add_action('woocommerce_cart_totals_before_shipping', function() {
foreach(WC()->shipping()->packages[0]['rates'] as $key => $rate) {
if ( $rate->__get('instance_id') != 18 ) {
unset(WC()->shipping()->packages[0]['rates'][$key]);
}
}
});
}
}
});

Show certain countries on WooCommerce checkout for particular products in cart

I am trying to show only limited countries in WooComerce checkout for particular products only.
I am successfully able to get the cart product id but i cannot get it outside the function.
// hide countries
function get_cart_product_id() {
foreach ( WC()->cart->get_cart() as $cart_item ) {
$product = $cart_item['data'];
if(!empty($product)){
$live_pro = $product->get_id();
}
}
return $live_pro;
global $live_pro;
}
global $live_pro;
echo $live_pro; // Donesn't echo anything here. Below if also not working
if ($live_pro == 435925 || $live_pro == 435929 || $live_pro == 435930 || $live_pro == 435931 || $live_pro == 435932 ) {
add_filter( 'woocommerce_countries', 'bbloomer_custom_woocommerce_countries' );
}
function bbloomer_custom_woocommerce_countries( $country ) {
$country = array(
'ES' => 'Spain',
'PT' => 'Portugal',
'FR' => 'France',
);
return $country;
}
Any advice to help me find the solution?
To show/hide certain countries on WooCommerce checkout for particular products in cart, you can use the woocommerce_countries_allowed_countries filter hook.
Either you indicate which country (codes) you want to remove:
function filter_woocommerce_countries_allowed_countries( $countries ) {
// Cart or checkout page
if ( is_cart() || is_checkout() ) {
// The targeted product ids
$targeted_ids = array( 30, 815 );
// Flag
$found = false;
if ( WC()->cart ) {
// Loop through cart items
foreach ( WC()->cart->get_cart() as $cart_item ) {
if ( array_intersect( $targeted_ids, array( $cart_item['product_id'], $cart_item['variation_id'] ) ) ) {
$found = true;
break;
}
}
}
// True
if ( $found ) {
// Remove
unset( $countries[ 'NL' ] );
unset( $countries[ 'FR' ] );
}
}
// Return
return $countries;
}
add_filter( 'woocommerce_countries_allowed_countries', 'filter_woocommerce_countries_allowed_countries', 10, 1 );
OR
The reverse, and indicate which country (codes) you want to keep
function filter_woocommerce_countries_allowed_countries( $countries ) {
// Cart or checkout page
if ( is_cart() || is_checkout() ) {
// The targeted product ids
$targeted_ids = array( 30, 815 );
// Country codes you want to show
$show_countries = array( 'BE', 'NL', 'FR', 'ES' );
// Flag
$found = false;
if ( WC()->cart ) {
// Loop through cart items
foreach ( WC()->cart->get_cart() as $cart_item ) {
if ( array_intersect( $targeted_ids, array( $cart_item['product_id'], $cart_item['variation_id'] ) ) ) {
$found = true;
break;
}
}
}
// True
if ( $found ) {
// Loop through country codes
foreach ( $countries as $key => $country ) {
// NOT found
if ( ! in_array( $key, $show_countries ) ) {
// Remove
unset( $countries[$key] );
}
}
}
}
// Return
return $countries;
}
add_filter( 'woocommerce_countries_allowed_countries', 'filter_woocommerce_countries_allowed_countries', 10, 1 );

Allow customer to set the product price and add to cart with certain validations in WooCommerce

I'm working on a giftcard product whereof I need the customer to be able to set the price as long as it is 100 or more. Problem is, I'm not sure how to create the value check.
The customer should then be able to add to cart as normal and to checkout as usual.
I've included a remove_action for the product price (which for some reason does not work) if the product is assigned to the giftcard category.
The field input has been created and the data should be carried over to the cart and checkout and into the order -- but it does not work for some reason.
The next step is to set the product price into whatever the customer submits as the giftcard value (as long as it is 100 or more) and to display that as the product price on cart and checkout.
If anyone can review and help me out, that would be awesome.
add_action( 'woocommerce_before_add_to_cart_form', 'giftcard_price_field' );
function giftcard_price_field() {
global $product;
if( has_term('giftcard', 'product_cat', $product->get_id() ) ) {
// if the product is assigned to the giftcard category, remove the product price
remove_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_price', 10 );
// add a new input field for the price, allowing the customer to set the price
echo '<div class="giftcard-product-price">
<label for="giftcard-product-price">Giftcard value: </label>
<input type="text" id="giftcard-product-price" name="giftcard-product-price" placeholder="Giftcard value" maxlength="1000">
</div>';
}
}
add_filter( 'woocommerce_add_cart_item_data', 'giftcard_price_field_cart_data', 10, 3 );
function giftcard_price_field_cart_data( $cart_item_data, $product_id, $variation_id ) {
if( ! empty ( $_POST[ 'giftcard-product-price' ] ) ) {
// need to check that the value is NOT below 100 and if so, create a wc_notice warning
$cart_item_data['giftcard-product-price'] = sanitize_text_field( $_POST['giftcard-product-price']);
}
return $cart_item_data;
}
add_filter( 'woocommerce_get_item_data', 'giftcard_price_field_display_data', 10, 2 );
function giftcard_price_field_display_data( $item_data, $cart_item ) {
if( ! empty ( $cart_item[ 'giftcard-product-price' ] ) ) {
$item_data[] = array (
'key' => 'Giftcard value',
'value' => $cart_item['giftcard-product-price'],
'display' => '',
);
}
return $item_data;
}
add_action( 'woocommerce_checkout_create_order_line_item', 'giftcard_price_field_order_data', 10, 4 );
function giftcard_price_field_order_data( $item, $cart_item_key, $values, $order ) {
if( ! empty ( $values[ 'giftcard-product-price' ] ) ) {
$item->add_meta_data( 'Giftcard value', $values['giftcard-product-price'] );
}
}
Add a new field 'giftcard_product_price' on single product page if has_term()
Removes the original product price on the single product page
Various validations have been added and are possible
The price of the product (giftcard) is adjusted to the price entered by the customer
function giftcard_price_field() {
global $product;
// Instanceof
if ( $product instanceof WC_Product ) {
// Set category(ies)
$cats = array ( 'giftcard' );
// True
if ( has_term( $cats, 'product_cat', $product->get_id() ) ) {
// add a new input field for the price, allowing the customer to set the price
echo '<div class="giftcard-product-price">
<label for="giftcard-product-price">Giftcard value: </label>
<input type="text" id="giftcard_product_price" name="giftcard_product_price" placeholder="Giftcard value" maxlength="1000">
</div>';
}
}
}
add_action( 'woocommerce_before_add_to_cart_button', 'giftcard_price_field', 10, 0 );
// Remove price
function action_woocommerce_single_product_summary() {
global $product;
// Instanceof
if ( $product instanceof WC_Product ) {
// Set category(ies)
$cats = array ( 'giftcard' );
// True
if ( has_term( $cats, 'product_cat', $product->get_id() ) ) {
remove_action('woocommerce_single_product_summary','woocommerce_template_single_price', 10 );
}
}
}
add_action( 'woocommerce_single_product_summary', 'action_woocommerce_single_product_summary', 5 );
// Validate
function filter_woocommerce_add_to_cart_validation( $passed, $product_id, $quantity, $variation_id = null, $variations = null ) {
// Isset
if ( isset ( $_POST['giftcard_product_price'] ) ) {
$giftcard_product_price = $_POST['giftcard_product_price'];
// Error = empty, not numeric or less than 100
if ( empty ( $giftcard_product_price ) ) {
wc_add_notice( __( 'Field is empty', 'woocommerce' ), 'error' );
$passed = false;
} elseif ( ! is_numeric ( $giftcard_product_price ) ) {
wc_add_notice( __( 'NOT a number or a numeric string', 'woocommerce' ), 'error' );
$passed = false;
} elseif ( $giftcard_product_price < 100 ) {
wc_add_notice( __( 'Less than 100', 'woocommerce' ), 'error' );
$passed = false;
}
}
return $passed;
}
add_filter( 'woocommerce_add_to_cart_validation', 'filter_woocommerce_add_to_cart_validation', 10, 5 );
function filter_add_cart_item_data( $cart_item_data, $product_id, $variation_id ) {
if ( isset ( $_POST['giftcard_product_price'] ) ) {
$cart_item_data['giftcard_product_price'] = sanitize_text_field( $_POST['giftcard_product_price'] );
}
return $cart_item_data;
}
add_filter( 'woocommerce_add_cart_item_data', 'filter_add_cart_item_data', 10, 3 );
// Set price
function action_woocommerce_before_calculate_totals( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Loop through cart items
foreach ( $cart->get_cart() as $cart_item ) {
if ( isset ( $cart_item['giftcard_product_price'] ) ) {
$cart_item['data']->set_price( $cart_item['giftcard_product_price'] );
}
}
}
add_action( 'woocommerce_before_calculate_totals', 'action_woocommerce_before_calculate_totals', 10, 1 );

Change product prices based on a custom rate in Woocommerce

In Woocommerce I am using woocommerce_product_get_price like to alter my product prices and it is working fine… But when I add to cart a product, the cart total and cart item are zero.
That is my code:
add_filter( 'woocommerce_add_cart_item', 'set_custom_cart_item_prices', 20, 2 );
function set_custom_cart_item_prices( $cart_data, $cart_item_key ) {
$new_price = $cart_data['data']->get_price() * 2;
$cart_data['data']->set_price( $new_price );
$cart_data['new_price'] = $new_price;
return $cart_data;
}
add_filter( 'woocommerce_get_cart_item_from_session', 'set_custom_cart_item_prices_from_session', 20, 3 );
function set_custom_cart_item_prices_from_session( $session_data, $values, $key ) {
if ( ! isset( $session_data['new_price'] ) || empty ( $session_data['new_price'] ) )
return $session_data;
$session_data['data']->set_price( $session_data['new_price'] );
return $session_data;
}
Its doesn't work. I have also tried to use woocommerce_before_calculate_totals without success
How to avoid zero prices on cart? Any help is appreciated.
Updated: You are not using the correct hooks and the correct way. The following will replace all your code. The price changes will be done on simple products, variable products and product variations.
The 1st function is the rate to be applied to your product prices (so you will define in it your rate calculation).
The 2nd function will change all products displayed price on all product pages instead of using your custom function hooked in woocommerce_product_get_price filter hook.
The 3rd Function will change the products price in cart items, on cart and checkout pages and in orders items too.
// The price rate to be applied
function get_product_price_rate() {
// HERE define the price rate to be applied
return 1.25; // +25%
}
// Change the product displayed price on product pages
add_filter( 'woocommerce_get_price_html', 'custom_price_html', 10, 2 );
function custom_price_html( $price, $product ){
$rate = get_product_price_rate();
// Simple products and product variations
if( in_array( $product->get_type() , array( 'simple' , 'variation' ) ) ) {
$regular_price = wc_get_price_to_display( $product, array( 'price' => ( $product->get_regular_price() * $rate ) ) );
$active_price = wc_get_price_to_display( $product, array( 'price' => ( $product->get_price() * $rate ) ) );
if ( '' === $product->get_price() ) {
$price = apply_filters( 'woocommerce_empty_price_html', '', $product );
} elseif ( $product->is_on_sale() ) {
$price = wc_format_sale_price( $regular_price, $active_price ) . $product->get_price_suffix();
} else {
$price = wc_price( $active_price ) . $product->get_price_suffix();
}
}
// Variable products
elseif ( 'variable' === $product->get_type() ) {
$prices = $product->get_variation_prices( true );
if ( empty( $prices['price'] ) ) {
$price = apply_filters( 'woocommerce_variable_empty_price_html', '', $this );
} else {
$min_price = current( $prices['price'] ) * $rate;
$max_price = end( $prices['price'] ) * $rate;
$min_reg_price = current( $prices['regular_price'] ) * $rate;
$max_reg_price = end( $prices['regular_price'] ) * $rate;
if ( $min_price !== $max_price ) {
$price = wc_format_price_range( $min_price, $max_price );
} elseif ( $this->is_on_sale() && $min_reg_price === $max_reg_price ) {
$price = wc_format_sale_price( wc_price( $max_reg_price ), wc_price( $min_price ) );
} else {
$price = wc_price( $min_price );
}
$price .= $product->get_price_suffix();
}
}
return $price;
}
// Change cart items prices (and order items prices)
add_action('woocommerce_before_calculate_totals', 'increase_cart_item_prices', 100, 1 );
function increase_cart_item_prices( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Loop Through cart items
foreach ( $cart->get_cart() as $cart_item ) {
$rate = get_product_price_rate();
$cart_item['data']->set_price( $cart_item['data']->get_price() * $rate );
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.

Buy one get one in woocommerce with out coupon code

I want implement functionality on my site when user buy a x product then user will get a y product as free.
In my site i have variable products.If customer buy 1kg pack of X product then customer want to get Free 30ml of Y product.
I added below code in my function.php but its working the problem is when refreshing the page gift product count is increasing.
my updated code.
add_action( 'woocommerce_before_cart', 'bbloomer_apply_matched_coupons' );
function bbloomer_apply_matched_coupons() {
global $woocommerce;
$cat_in_cart = false;
foreach ( $woocommerce->cart->cart_contents as $key => $values ) {
// this is your product ID
$autocoupon = array( 123411 );
if( in_array( $values['variation_id'], $autocoupon ) ) {
$cat_in_cart = true;
break;
}
}
if ( $cat_in_cart ) {
$product_id = 2044;
$quantity = 1;
$variation_id = 2046;
$variation = array(
'pa_size' => '30ml'
);
$woocommerce->cart->add_to_cart( $product_id, $quantity, $variation_id, $variation );
}
}
Add this to start of 'functions.php'.
ob_start();
session_start();
Then add this code.
add_action( 'woocommerce_before_cart', 'bbloomer_apply_matched_coupons' );
function bbloomer_apply_matched_coupons() {
if(!$_SESSION['GiftAdded']) {
global $woocommerce;
$cat_in_cart = false;
$coupon_in_cart = false;
$autocoupon = array( 123411 ); // variation ids of products that offers gifts
$freecoupon = array( 2046 ); // variation ids of products that are gift coupons
foreach ( $woocommerce->cart->cart_contents as $key => $values ) {
if( in_array( $values['variation_id'], $autocoupon ) ) {
$cat_in_cart = true;
}
if( in_array( $values['variation_id'], $freecoupon) ) {
$coupon_in_cart = true;
}
}
if ( $cat_in_cart && !$coupon_in_cart ) {
$product_id = 2044;
$quantity = 1;
$variation_id = 2046;
$variation = array( 'pa_size' => '30ml' );
$woocommerce->cart->add_to_cart( $product_id, $quantity, $variation_id, $variation );
$_SESSION['GiftAdded']=true;
}
}
}
The $_SESSION['GiftAdded'] will prevent the gift product added again when its deleted manually.
There is an other alternative based on quantity using another hook… It handles when quantities are changed or products removed from cart and set the price of the free product to zero:
add_action( 'woocommerce_before_calculate_totals', 'add_free_product_to_cart', 20, 1 );
function add_free_product_to_cart( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
$products_ids = array( 123411 ); // Products IDs to check
$free_var_id = 2046; // free product (variation ID)
$free_prod_id = 2044; // free product (product ID)
$free_attr = array( 'pa_size' => 'medium' ); // Free product attribute
$free_price = 0;
$targeted = $free = array('qty' => 0 ); // Initializing
// Loop through cart items
foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
if ( in_array( $cart_item['data']->get_id(), $products_ids ) ) {
$targeted['qty'] += $cart_item['quantity'];
} elseif ( $cart_item['data']->get_id() == $free_var_id ) {
$cart_item['data']->set_price($free_price);
$free['qty'] += $cart_item['quantity'];
$free['key'] = $cart_item_key;
}
}
// We exit (No changes are needed)
if( $targeted['qty'] == $free['qty'] )
return;
if( $targeted['qty'] > 0 && $free['qty'] == 0 )
{
// Add the free product
$cart->add_to_cart( $free_prod_id, $targeted['qty'], $free_var_id, $free_attr );
}
elseif( $targeted['qty'] > 0 && $free['qty'] > 0 && $targeted['qty'] != $free['qty'] )
{
// Adjust free product quantity
$cart->set_quantity( $free['key'], $targeted['qty'] );
}
elseif( $targeted['qty'] == 0 && $free['qty'] > 0)
{
// Remove free product
$cart->remove_cart_item( $free['key'] );
}
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.

Categories