I am trying to make shipping costs with different cites in Georgia country.
I found this code:
function ace_change_city_to_dropdown( $fields ) {
$cities = array(
'Tbilisi',
'city2',
// etc …
);
$city_args = wp_parse_args( array(
'type' => 'select',
'options' => array_combine( $cities, $cities ),
), $fields['shipping']['shipping_city'] );
$fields['shipping']['shipping_city'] = $city_args;
$fields['billing']['billing_city'] = $city_args;
return $fields;
}
add_filter( 'woocommerce_checkout_fields', 'ace_change_city_to_dropdown' );
And I found woocommerce city rate shipping Where I can change city names with my desired cities. it's working fine.
But when there shipping costs (for example 5$) I want to hide free shipping checkbox.
How to hide free shipping when there shipping costs in WooCommerce?
Try the following, that will hide Free shipping if any shipping method with a cost is available:
add_filter( 'woocommerce_package_rates', 'hide_free_shipping_for_available_rate_costs', 100, 2 );
function hide_free_shipping_for_available_rate_costs( $rates, $package ) {
$has_cost = false;
foreach ( $rates as $rate_key => $rate ) {
if( $rate-cost > 0 ) {
$has_cost = true;
}
if ( 'free_shipping' === $rate->method_id ) {
$free_rate_key = $rate_key;
}
}
if ( $has_cost && isset($free_rate_key) ){
unset($rates[$free_rate_key]);
}
return $rates;
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
Refresh the shipping caches:
This code is already saved on your function.php file.
In a shipping zone settings, disable / save any shipping method, then enable back / save.
You are done and you can test it.
Related: WooCommerce - Hide other shipping methods when FREE SHIPPING is available
Related
someone gives me a suggestion to understand how to correctly write the shipping function in woocommerce to get this result:
a) Shipping costs are € 10 and are free for orders over € 100
b) I have two categories A and B
c) if in the cart there are products of ONLY CATEGORY B, the rule of free shipping for orders over € 100 must not be applied. So the shipping must be 10 €.
For point c) I have entered this function in function.php, but there is something wrong:
// Disable Woocommerce shipping methods based on specific category
add_filter( 'woocommerce_package_rates', 'hide_shipping_for_categories', 10, 2 );
function hide_shipping_for_categories( $rates, $package ) {
// Add your own Woocommerce categories here (either category ID, slug or name)
$terms = array( 'I nostri sottoli' );
$taxonomy = 'product_cat'; // If you need to hide shipping based on tags, then change it to product_tag
// Add shipping methods to be removed (like "local_pickup:8")
$method_instances_ids = array('free_shipping:4');
$found = false;
// Loop through cart items checking for defined product IDs
foreach( $package['contents'] as $cart_item ) {
if ( has_term( $terms, $taxonomy, $cart_item['product_id'] ) ){
$found = true;
break;
}
}
if ( ! $found ) return $rates; // If not found we exit
// Loop through your active shipping methods
foreach( $rates as $rate_id => $rate ) {
// Remove all other shipping methods other than your defined shipping method
if ( in_array( $rate_id, $method_instances_ids ) ){
unset( $rates[$rate_id] );
}
}
return $rates;
}
I have the following scenario:
If a customer is outside Canada, I need to show a checkbox that verifying he is out of the country for tax purposes, and to hide this checkbox and remove its requirement when the customer has selected Canada as a country.
Looked for many solutions, I found this one useful and working, if I select Canada, the check box gets hidden, however the requirement still there, and the customer can't proceed further.
Also, if he is a returning customer, and the system pulled Canada automatically, the checkbox shows unless he changes the country and goes back to Canada.
**What's missing from the code? **
Remove checkbox requirement when Canada is selected
Hide the checkbox on page load and only show after country selection if it was not Canada.
May you please help with this?
(If other country is selected)
(If Canada is selected)
add_action( 'woocommerce_review_order_before_submit', 'bt_add_tax_verification', 10 );
/**
* Add WooCommerce additional Checkbox checkout field
*/
function bt_add_tax_verification() {
woocommerce_form_field( 'tax_verification', array( // CSS ID
'type' => 'checkbox',
'class' => array('form-row mycheckbox'), // CSS Class
'label_class' => array('woocommerce-form__label woocommerce-form__label-for-checkbox checkbox'),
'input_class' => array('woocommerce-form__input woocommerce-form__input-checkbox input-checkbox'),
'required' => true, // Mandatory or Optional
'label' => 'I certify that I am not resident in Canada for purposes of the Excise Tax Act and I am not registered under that Act. Where applicable, I agree to advise RxCourse Institute Inc, 838 Silverthorn Mill Ave, Mississauga ON L5W 1B1 in the event there is any change to my residence status or should I become registered for the purposes of the Excise Tax Act', // Label and Link
));
}
add_action( 'woocommerce_checkout_process', 'bt_add_tax_verification_warning' );
/**
* Alert if checkbox not checked
*/
function bt_add_tax_verification_warning() {
if ( ! (int) isset( $_POST['tax_verification'] ) ) {
wc_add_notice( __( 'Please verify that you are not a Canadian resident by selecting the checkbox' ), 'error' );
}
}
add_action( 'woocommerce_checkout_update_order_meta', 'bt_checkout_field_order_meta_db' );
/**
* Add custom field as order meta with field value to database
*/
function bt_checkout_field_order_meta_db( $order_id ) {
if ( ! empty( $_POST['tax_verification'] ) ) {
update_post_meta( $order_id, 'tax_verification', sanitize_text_field( $_POST['tax_verification'] ) );
}
}
add_action( 'woocommerce_admin_order_data_after_billing_address', 'bt_checkout_field_display_admin_order_meta', 10, 1 );
/**
* Display field value on the backend WooCOmmerce order
*/
function bt_checkout_field_display_admin_order_meta($order){
echo '<p><strong>'.__('Checkout Checkbox Label').':</strong> ' . get_post_meta( $order->get_id(), 'tax_verification', true ) . '<p>';
}
add_filter('woocommerce_email_order_meta_keys', 'bt_custom_order_meta_email');
function bt_custom_order_meta_email( $keys ) {
$keys[] = 'tax_verification'; // This will look for a custom field called 'tax_verification' and add it to WooCommerce emails
return $keys;
}
add_filter( 'woocommerce_email_order_meta_fields', 'bt_woocommerce_email_order_meta_fields', 10, 3 );
/**
* Show hide base on country selection
*/
add_action( 'woocommerce_after_checkout_form', 'woo_conditionally_hide_show_checkout_field', 9999 );
function woo_conditionally_hide_show_checkout_field() {
wc_enqueue_js( "
// On page load
jQuery(document).ready(function() {
// var country_code = jQuery(this).val();
var country_name = jQuery('#billing_country option:selected').text().toLowerCase();
show_hide_fields(country_name);
});
// On change country dropdown
jQuery(document).on('change','#billing_country',function() {
var country_name = jQuery('option:selected',this).text().toLowerCase();
show_hide_fields(country_name);
});
function show_hide_fields(country){
if (country == 'canada') {
jQuery('#tax_verification_field').hide();
} else {
jQuery('#tax_verification_field').show();
}
}
");
}
add_action( 'woocommerce_checkout_process', 'bt_add_tax_verification_warning' );
/**
* Alert if checkbox not checked
*/
function bt_add_tax_verification_warning() {
if( isset($_POST['billing_country']) && 'CA' !== $_POST['billing_country'] ){
if ( ! (int) isset( $_POST['tax_verification'] ) ) {
wc_add_notice( __( 'Please verify that you are not a Canadian resident by selecting the checkbox' ), 'error' );
}
}
}
Modify the validation function to check if Country is CA
I have created a product on WooCommerce, and added two options on product detail page using the hook woocommerce_before_add_to_cart_button. Now when customers add product to cart from product detail page they have two options their. They can choose one option from these two options.
Then I have stored the user selected value in cart meta using the woocommerce hook woocommerce_add_cart_item_data.
I am using the code from this answer: Save product custom field radio button value in cart and display it on Cart page
This is my code:
// single Product Page options
add_action("woocommerce_before_add_to_cart_button", "options_on_single_product");
function options_on_single_product(){
$dp_product_id = get_the_ID();
$product_url = get_permalink($dp_product_id);
?>
<input type="radio" name="custom_options" checked="checked" value="option1"> option1<br />
<input type="radio" name="custom_options" value="option2"> option2
<?php
}
//Store the custom field
add_filter( 'woocommerce_add_cart_item_data', 'save_custom_data_with_add_to_cart', 10, 2 );
function save_custom_data_with_add_to_cart( $cart_item_meta, $product_id ) {
global $woocommerce;
$cart_item_meta['custom_options'] = $_POST['custom_options'];
return $cart_item_meta;
}
And this is what I have tried:
add_action( 'woocommerce_before_calculate_totals', 'add_custom_price', 10, 1);
function add_custom_price( $cart_obj ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
foreach ( $cart_obj->get_cart() as $key => $value ) {
$product_id = $value['product_id'];
$custom_options = $value['custom_options'];
$coupon_code = $value['coupon_code'];
if($custom_options == 'option2')
{
if($coupon_code !='')
{
global $woocommerce;
if ( WC()->cart->has_discount( $coupon_code ) ) return;
(WC()->cart->add_discount( $coupon_code ))
//code for second discount
}
else{
$percentage = get_post_meta( $product_id , 'percentage', true );
//print_r($value);
$old_price = $value['data']->regular_price;
$new_price = ($percentage / 100) * $old_price;
$value['data']->set_price( $new_price );
}
}
}
}
Now what I am trying to get with that last snippet is:
If Option1 is selected by the customer then woocommerce regular process is run.
If Option2 is selected then firstly coupon code applied to cart (if code entered by the customer) and then the price is divide by some percentage (stored in product meta) is applied afterward.
But it’s not working as expected because the changed product price is maid before and coupon discount is applied after on this changed price.
What I would like is that the coupon discount will be applied first on the product regular price and then after change this price with my custom product discount.
Is this possible? How can I achieve that?
Thanks.
This is not really possible … Why? … Because (the logic):
You have the product price
Then the coupon discount is applied to that price (afterwards)
==> if you change the product price, the coupon is will be applied to that changed price
What you can do instead:
You don't change product price
if entered the coupon is applied and …
If "option2" product is added to cart:
Apply a custom discount (a negative fee) based on the product price added after using WC_cart add_fee() method…
For this last case you will have to fine tune your additional discount.
If the coupon has not been applied or it's removed there is no additional discount.
Your custom function will be hooked in woocommerce_cart_calculate_fees action hook instead:
add_action( 'woocommerce_cart_calculate_fees', 'option2_additional_discount', 10, 1 );
function option2_additional_discount( $cart_obj ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
$discount = 0;
$applied_coupons = $cart_obj->get_applied_coupons();
foreach ( $cart_obj->get_cart() as $item_values ) {
if( 'option2' == $item_values['custom_options'] && !empty($applied_coupons) ){
$product_id = $item_values['product_id'];
$percentage = get_post_meta( $product_id , 'percentage', true );
$quantity = $item_values['quantity'];
$product_reg_price = $item_values['data']->regular_price;
$line_total = $item_values['line_total'];
$line_subtotal = $item_values['line_subtotal'];
$percentage = 90;
## ----- CALCULATIONS (To Fine tune) ----- ##
$item_discounted_price = ($percentage / 100) * $product_reg_price * $item_values['quantity'];
// Or Besed on line item subtotal
$discounted_price = ($percentage / 100) * $line_subtotal;
$discount += $product_reg_price - $item_discounted_price;
}
}
if($discount != 0)
$cart_obj->add_fee( __( 'Option2 discount', 'woocommerce' ) , - $discount );
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
This code is tested and works.
Adding a negative fee using the WC_Cart->add_fee() method wasn't working for me. When i check the WC Cart class, it even states you are not allowed to use a negative ammount.
See the docs.
I did the following:
create a placeholder coupon with a 'secure' code, e.g. custom_discount_fjgndfl28. Set a discount ammount of 0, so when somebody (somehow) uses this coupon outside your program the discount is still 0.
Use filter woocommerce_get_shop_coupon_data, and set all the coupon data you want for that coupon/session.
Hook into woocommerce_before_calculate_totals and set your custom coupon to the cart.
At this point the Cart should calculate everything correctly. And when it becomes an order, it also has the correct discount ammount.
Note: the coupon code is also used as a label in some templates. Use filter woocommerce_cart_totals_coupon_label to change it.
Example functions:
/**
* NOTE: All the hooks and filters below have to be called from your own
* does_it_need_custom_discount() function. I used the 'wp' hook for mine.
* Do not copy/paste this to your functions.php.
**/
add_filter('woocommerce_get_shop_coupon_data', 'addVirtualCoupon', 10, 2);
function addVirtualCoupon($unknown_param, $curr_coupon_code) {
if($curr_coupon_code == 'custom_discount_fjgndfl28') {
// possible types are: 'fixed_cart', 'percent', 'fixed_product' or 'percent_product.
$discount_type = 'fixed_cart';
// how you calculate the ammount and where you get the data from is totally up to you.
$amount = $get_or_calculate_the_coupon_ammount;
if(!$discount_type || !$amount) return false;
$coupon = array(
'id' => 9999999999 . rand(2,9),
'amount' => $amount,
'individual_use' => false,
'product_ids' => array(),
'exclude_product_ids' => array(),
'usage_limit' => '',
'usage_limit_per_user' => '',
'limit_usage_to_x_items' => '',
'usage_count' => '',
'expiry_date' => '',
'apply_before_tax' => 'yes',
'free_shipping' => false,
'product_categories' => array(),
'exclude_product_categories' => array(),
'exclude_sale_items' => false,
'minimum_amount' => '',
'maximum_amount' => '',
'customer_email' => '',
'discount_type' => $discount_type,
);
return $coupon;
}
}
add_action('woocommerce_before_calculate_totals', 'applyFakeCoupons');
function applyFakeCoupons() {
global $woocommerce;
// $woocommerce->cart->remove_coupons(); remove existing coupons if needed.
$woocommerce->cart->applied_coupons[] = $this->coupon_code;
}
add_filter( 'woocommerce_cart_totals_coupon_label', 'cart_totals_coupon_label', 100, 2 );
function cart_totals_coupon_label($label, $coupon) {
if($coupon) {
$code = $coupon->get_code();
if($code == 'custom_discount_fjgndfl28') {
return 'Your custom coupon label';
}
}
return $label;
}
Please Note: i copied these functions out of a class that handles much more, it's only to help you get going.
I have a 3 shipping methods in my Woocommerce store and I want to disable the tax rate when customer choose the 3rd shipping method.
How can I do that?
As settings doesn't seem to be working to remove taxes from specific shipping methods, try the following code that will set zero taxes to your specific defined shipping methods:
add_filter('woocommerce_package_rates', 'null_specific_shipping_method_taxes', 12, 2);
function null_specific_shipping_method_taxes( $rates, $package ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return $rates;
// HERE define your targeted shipping methods IDs (in this array)
$shipping_methods_ids = array( 'flat_rate:2', 'flat_rate:12', 'flat_rate:3', 'shipping_by_rules:5' );
// Loop through shipping rates
foreach ( $rates as $rate_key => $rate ){
$has_taxes = false;
// Only for your defined Shipping method IDs
if( in_array( $rate->id, $shipping_methods_ids ) ){
$taxes = [];
// Loop through the shipping taxes array (as they can be many)
foreach ($rates[$rate_key]->taxes as $key => $tax){
if( $rates[$rate_key]->taxes[$key] > 0 ){
// Set each tax cost to zero
$taxes[$key] = 0;
$has_taxes = true;
}
}
// Set the new taxes array
if( $has_taxes )
$rates[$rate_key]->taxes = $taxes;
}
}
return $rates;
}
This code goes on function.php file of your active child theme (or theme). Tested and works (if settings are made in a correct way, regarding related shipping methods and taxes in Woocommerce)…
I have a woocommerce website in which there are 2 products, one product has free shipping but other has paid shipping if they are added separately in cart. Now I have a scenario that if some customer adds both free and paid shipping products together in shopping cart then shipping should become free for that whole order. How can I achieve this?
Thanks,
Mohammad
try this... just paste this in your theme's functions.php and replace the id in $products_to_look = array( 34 ); below.
add_filter( 'woocommerce_package_rates', 'woocommerce_package_rates' );
function woocommerce_package_rates( $rates ) {
if ( ! WC()->cart->is_empty() ) {
$products_to_look = array( 34 ); // ids of products separated by comma.
$found = false;
foreach ( WC()->cart->get_cart() as $cart_item ) {
if ( $found = in_array( $cart_item['product_id'], $products_to_look ) ) {
break;
}
}
}
if ( $found ) {
foreach($rates as $key => $rate ) {
$rates[$key]->label = 'Free shipping'; // change label to Free shipping...
$rates[$key]->cost = 0; // cost is set to 0.
}
}
return $rates;
}
Further readings: WooCommerce shipping fee that changes when condition is met