Change Woocomerce shipping methods output order - woocommerce

There are 4 methods, and you need to do the last one first. Which I just haven't tried. Hooks don't help.
Checout view
This code selects the delivery method, and I need to move it, that is, make it the first:
add_action( 'woocommerce_before_checkout_form', 'auto_select_free_shipping_by_default' );
function auto_select_free_shipping_by_default() {
if ( ! WC()->session->has_session() )
WC()->session->set_customer_session_cookie( true );
// Check if "free shipping" is already set
if ( strpos( WC()->session->get('chosen_shipping_methods')[0], 'nova_poshta_shipping_method' ) !== false )
return;
// Loop through shipping methods
foreach( WC()->session->get('shipping_for_package_0')['rates'] as $key => $rate ){
if( $rate->method_id === 'nova_poshta_shipping_method' ){
// Set "Free shipping" method
WC()->session->set( 'chosen_shipping_methods', array($rate->id) );
return;
}
}
}
The value of the method to move: 'nova_poshta_shipping_method'.

Related

Remove Woocommerce shipping option for one particular city

Need some help on this...Please :)
I have this site that is using the Woocommerce's shipping option for "Starken por pagar", that works globally for all cities
And right after that is "Envío Gratis" that is send it for my actual shipping plugin, that calculate free shipping "Envío Gratis" when one particular city is selected.
My idea is that I need to remove "Starken por pagar" when the user select one particular city (one only - the same that is used by my shipping plugin) and left only the "Envío Gratis" for that city.
Because now I dont need the users need to pay for some service that is free. And as you may know, some people barely read the things in front of ther eyes :P
I tried with this (Im not an expert, sorry)
function filter_woocommerce_package_rates( $rates, $package ) {
// Loop trough
foreach ( $rates as $rate_id => $rate ) {
// Checks if a value exists in an array, multiple can be added, separated by a comma
if ( in_array( $rate->method_id, array( 'local_pickup', 'free_shipping' ) ) ) {
unset( $rates['flat_rate:28'] );
}
}
return $rates;
}
add_filter( 'woocommerce_package_rates', 'filter_woocommerce_package_rates', 10, 2 );
Or
// Hide shipping in cart page.
add_filter( 'woocommerce_product_needs_shipping', 'disable_shipping_in_cart_page', 10, 2 );
function disable_shipping_in_cart_page( $needs_shipping, $product ){
if ( is_cart() )
$needs_shipping = false;
return $needs_shipping;
}
// Conditionally remove Flat Rate which qualifies for free shipping
add_filter( 'woocommerce_package_rates', 'hide_flat_rate_when_free_is_available', 10, 3 );
function hide_flat_rate_when_free_is_available( $rates, $package ) {
$free = array();
$free_available = false;
foreach ( $rates as $rate_key => $rate ) {
if ( 'free_shipping' === $rate->method_id ){
$free_available = true;
break;
}
}
foreach ( $rates as $rate_key => $rate ) {
if ( $free_available && 'flat_rate' === $rate->method_id )
unset($rates[$rate_key]);
}
return $rates;
}
Thank you :)

woocommerce conditional email template if product type in order

I try to send a customized email template when a customer has a ticket (custom product type) in cart.
I have the following:
function bc_customer_completed_order_template($template, $template_name, $template_path)
{
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$product = wc_get_product( $cart_item['product_id'] );
$type = get_class($product);
if ( $type == 'WC_Product_Tickets' && 'customer-completed-order.php' === basename($template) ) {
$template = trailingslashit(plugin_dir_path( __FILE__ )) . 'templates/customer-completed-order.php';
}
}
return $template;
}
add_filter('woocommerce_locate_template', 'bc_customer_completed_order_template', 10, 3);
The conditionals are working (on cart and checkout page for example), but when the order is placed, the new template is not used.
Anybody?
Your email template will look like this:
Code snippets:
// Suppose orders don't have ticket products.
$has_tickets = false;
// Loop order items.
foreach ( $order->get_items() as $item_id => $item ) {
// Get product object from order item.
$_product = $item->get_product();
// Check if the product object is valid and the class is `WC_Product_Tickets`
if ( $_product && 'WC_Product_Tickets' === get_class( $_product ) ) {
// Change the flag.
$has_tickets = true;
// Break the loop as we alreay have true flag.
break;
}
}
// Check if order have tickets items.
if ( $has_tickets ) {
// Load custom email template.
wc_get_template( 'templates/custom-customer-completed-order.php' );
// Return as we don't need the below code.
return;
}
It turned out, although the above solution is correct in its idea, in reality one cannot load a template and make use of the $order without extending the woocommerce email class.
Therefore i loaded the function inside the email template itself and made an if - else statement so for situation A the layout is different then for situation b.
Like so:
$has_tickets = false;
// Loop order items.
foreach ( $order->get_items() as $item_id => $item ) {
// Get product object from order item.
$_product = $item->get_product();
// Check if the product object is valid and the class is `WC_Product_Tickets`
if ( $_product && 'WC_Product_Tickets' === get_class( $_product ) ) {
// Change the flag.
$has_tickets = true;
// Break the loop as we alreay have true flag.
break;
}
}
// Check if order have tickets items.
if ( $has_tickets ) {
do_action( 'woocommerce_email_header', $email_heading, $email ); ?>
//custom email layout here//
}
else{
//Regular email template here
}

Disable specific payment methods depending on Woocommerce order status syntax error, unexpected 'elseif' (T_ELSEIF) [duplicate]

This question already has answers here:
Disable specific payment methods depending on Woocommerce order status
(2 answers)
Closed 2 years ago.
help i get this error
I am doing the same function Disable specific payment methods depending on Woocommerce order status I get
Copy and paste the same code and it didn't work
syntax error, unexpected 'elseif' (T_ELSEIF)
add_filter( 'woocommerce_available_payment_gateways', 'conditionally_hide_payment_gateways', 100, 1 );
function conditionally_hide_payment_gateways( $available_gateways ) {
// 1. On Order Pay page
if( is_wc_endpoint_url( 'order-pay' ) ) {
// Get an instance of the WC_Order Object
$order = wc_get_order( get_query_var('order-pay') );
// Loop through payment gateways 'pending', 'on-hold', 'processing'
foreach( $available_gateways as $gateways_id => $gateways ){
// Keep paypal only for "pending" order status
elseif ($gateways_id !== 'paypal' && $order->has_status('pending') ) {
unset($available_gateways[$gateways_id]);
}
}
}
// 2. On Checkout page
elseif( is_checkout() && ! is_wc_endpoint_url() ) {
// Disable paypal
if( isset($available_gateways['paypal']) ) {
unset($available_gateways['paypal']);
}
}
return $available_gateways;
}
enter image description here
The elseif inside the foreach doesn't have a preceding if, so that's your error.
add_filter( 'woocommerce_available_payment_gateways', 'conditionally_hide_payment_gateways', 100, 1 );
function conditionally_hide_payment_gateways( $available_gateways ) {
// 1. On Order Pay page
if( is_wc_endpoint_url( 'order-pay' ) ) {
// Get an instance of the WC_Order Object
$order = wc_get_order( get_query_var('order-pay') );
// Loop through payment gateways 'pending', 'on-hold', 'processing'
foreach( $available_gateways as $gateways_id => $gateways ){
// Keep paypal only for "pending" order status
if ($gateways_id !== 'paypal' && $order->has_status('pending') ) {
unset($available_gateways[$gateways_id]);
}
}
}
// 2. On Checkout page
elseif( is_checkout() && ! is_wc_endpoint_url() ) {
// Disable paypal
if( isset($available_gateways['paypal']) ) {
unset($available_gateways['paypal']);
}
}
return $available_gateways;
}

Hide shipping and payment methods in WooCommerce

I'm building a WooCommerce e-shop and I need to tweak my checkout page by doing the following:
Hide a certain shipping method (only one) if the order total > 100€.
Hide the cash on delivery payment method if local pickup is selected.
Does anyone know how to do that? I have the Code Snippets plugin so I can easily add any custom code.
To hide specific shipping method based on the cart total, you can use below code snippet. You need to update your shipping method name in the code.
Disable shipping method as per cart total
Add this snippet in your theme's functions.php file or custom plugin file.
add_filter( 'woocommerce_package_rates', 'shipping_based_on_price', 10, 2 );
function shipping_based_on_price( $rates, $package ) {
$total = WC()->cart->cart_contents_total;
//echo $total;
if ( $total > 100 ) {
unset( $rates['local_delivery'] ); // Unset your shipping method
}
return $rates;
}
Disable Payment Gateway For Specific Shipping Method
Use below code snippet. Update code as per your payment method & shipping method.
add_filter( 'woocommerce_available_payment_gateways', 'x34fg_gateway_disable_shipping' );
function x34fg_gateway_disable_shipping( $available_gateways ) {
global $woocommerce;
if ( !is_admin() ) {
$chosen_methods = WC()->session->get( 'chosen_shipping_methods' );
$chosen_shipping = $chosen_methods[0];
if ( isset( $available_gateways['cod'] ) && 0 === strpos( $chosen_shipping, 'local_pickup' ) ) {
unset( $available_gateways['cod'] );
}
}
return $available_gateways;
}
There are a number of plugins that will do this for you, take a look at this one WooCommerce Conditional Shipping and Payments
You'll want to tie in to the "woocommerce_payment_gateways" action
Something along these lines:
function alter_payment_gateways( $gateways ){
$chosen_rates = ( isset( WC()->session ) ) ? WC()->session->get( 'chosen_shipping_methods' ) : array();
if( in_array( 'local-pickup:6', $chosen_rates ) ) {
$array_diff = array('cod');
$list = array_diff( $list, $array_diff );
}
return $list;
}
add_action('woocommerce_payment_gateways', 'alter_payment_gateways', 50, 1);
The number on the end of 'local-pickup' on line 4 will depend on your woocommerce setup. You can find the string you need to put in here by adding something to a basket, going to the checkout, right clicking on the "Local Pickup" option in the delivery methods and looking at the value attribute.

Woocommerce custom coupon type with custom discount always return 0;

what I am trying to do is creating custom coupon type with custom programmatically discount in woocommerce using hooks. This what my code looks like
//add new coupon type called "custom_discount"
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->type == 'custom_discount'){ //if $coupon->type == 'fixed_cart' or 'percent' or 'fixed_product' or 'percent_product' The code Works
$discount = 85;
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);
the coupon always return with 0(Zero) discount amount. Am I missing something.
Thanks for helping =)
Ok so this topic lead me on the road to making my own coupon type so I thought I should answer it to help others.
I tried out your code and was wondering why any normal coupon showed "-£xx" before the remove button, but the custom coupon type just was lank, no minus, no pound sign jut empty. Turns out it's an issue with how WooCommerce validates the coupon on cart products. It will become active if the coupon is valid regardless if the item in the cart are.
Your coupon needs to be either valid and valid for a product, or valid for the entire cart. However when Woocommerce checks to see if your product is valid for the product it ONLY checks if the coupon type is fixed_product or 'percent_product'. If it's anything else it runs the woocommerce_coupon_is_valid_for_product hook with the existing data but defaulting to false, so your woocommerce_coupon_get_discount_amount hook will not fire.
So you need to hook into woocommerce_coupon_is_valid_for_product and validate the product to make sure it's valid. In my case I just copied the default valid function and changes $this to $coupon.
// Check if coupon is valid for product
add_filter('woocommerce_coupon_is_valid_for_product', 'woocommerce_coupon_is_valid_for_product', 10, 4);
function woocommerce_coupon_is_valid_for_product($valid, $product, $coupon, $values){
if ( ! $coupon->is_type( array( 'bogof' ) ) ) {
return $valid;
}
$product_cats = wp_get_post_terms( $product->id, 'product_cat', array( "fields" => "ids" ) );
var_dump( $coupon->product_ids );
// Specific products get the discount
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 ( ! sizeof( $coupon->product_ids ) && ! sizeof( $coupon->product_categories ) ) {
// No product ids - all items discounted
$valid = true;
}
// Specific product ID's excluded from the 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 excluded 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;
}
This question may be a little old, but there are two main coupon types, and you should add another filter based on the coupon you're adding. The filter for woocommerce_cart_coupon_types is for cart discounts, while woocommerce_product_coupon_types is for product discounts.
What you want to use is the second one woocommerce_product_coupon_types when you want WooCommerce to automatically validate the product/category_in and product/category_exclude fields etc.
You can still use the answer submitted by Chris for any manual validation, without using the above filters as required, however, please note that there are two validation functions to use/filter: $coupon->is_valid_for_cart() and $coupon->is_valid_for_product().

Resources