-
Notifications
You must be signed in to change notification settings - Fork 20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix/issue 2562 - Add Retail Delivery Fee for Colorado #2834
base: trunk
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest to create separate file that have custom fees, maybe even folder to store logic per each case in separate file (function) that will return fee data or false.
In main hooked method check if $custom_fees
contain item with $customer->get_shipping_country() . ':" . $customer->get_shipping_state()
.
$custom_fees = array(
'US:CO' => 'custom_function_name'
);
If matched include custom fee logic file and run custom_function_name
to check if fee should be applied, if should be applied return fee data.
array(
'value' => 0.27 // can be calculated
'label' => __( 'Retail Delivery Fee', 'woocommerce_services' ),
);
If fee should not be applied return false
.
if ( count( array_intersect( wc_get_chosen_shipping_method_ids(), apply_filters( 'woocommerce_local_pickup_methods', array( 'legacy_local_pickup', 'local_pickup' ) ) ) ) > 0 ) { | ||
return; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should check if all chosen methods wc_get_chosen_shipping_method_ids()
are local_pickup, if anyone of them is not fee should be applied.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @bartech , I'm not sure if I understand this. Can you explain a bit more on this?
This code is checking if there is any non "local pickup" method is being chosen or not. If it's a local pickup then the fee should not be applied. That's why it has return
there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As wc_get_chosen_shipping_method_ids
is an array it can have multiple shipping methods picked (in case there are multiple shipping packages). Now code checks if any method is local pickup and we have to check if all of them are local pickup not just one of them. Even if part of the order is sent via carrier or delivered you apply the flat fee 27c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh ok, I got it now and have made the changes.
* | ||
* @param WC_Cart $cart WooCommerce Cart object. | ||
*/ | ||
public function add_custom_state_surcharge_fee( $cart ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have to check if Colorado state Sales or Use Tax is charged on the items (see if we already have those taxes applied) as usually when items are exempt from state Sales or Use Tax they are exempt from Retail Delivery Fee.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a nice catch. Thanks! I did try to dig into this and based on this article, and it said this : "If the entire retail sale is exempt from sales tax, the sale is also exempt from the retail delivery fee. If one item in the retail sale is exempt, but another item is subject to sales tax, the retailer should collect the retail delivery fee.". From WooCommerce point of view, it seems we don't need to apply this fee if either the sales tax is zero, or all products is non-taxable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, if there is no tax on whole cart level no need to apply the fee.
if ( $this->maybe_all_products_are_virtual( $cart ) ) { | ||
return; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be part of custom method. There might be a fee or custom tax that should be applied to virtual products.
/** Do not apply the fee if all shipping methods use Local Pickup */ | ||
if ( 0 === count( array_diff( wc_get_chosen_shipping_method_ids(), apply_filters( 'woocommerce_local_pickup_methods', array( 'legacy_local_pickup', 'local_pickup' ) ) ) ) ) { | ||
return; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be in custom method. It's specific to Colorado fee.
|
||
$customer = WC()->customer; | ||
|
||
foreach ( $this->get_custom_surcharges() as $key => $custom_info ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of looping over $this->get_custom_surcharges()
better to get $custom_surcharges
array and check if key exists. There can be only one item with same index anyway.
$custom_surcharges = $this->get_custom_surcharges();
$key = $customer->get_shipping_country() . ':' . $customer->get_shipping_state();
if ( ! isset( $custom_surcharges[ $key ] ) {
return;
}
If we want to apply multiple rules (fees) for same location we have to allow item to be array of methods.
'US:CO' => array( 'method_one', 'method_two' ),
IMHO it's better to use :
as delimiter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Multiple rules seems like a great idea for scalability.
// Making sure all info is not empty. | ||
if ( ! isset( $custom_info['function'] ) || ! isset( $custom_info['fee'] ) || ! isset( $custom_info['fee_text'] ) || ! is_float( $custom_info['fee'] ) || ! is_callable( $custom_info['function'] ) ) { | ||
continue; | ||
} | ||
|
||
$eligible_to_add_fee = call_user_func( $custom_info['function'], $cart ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better if custom method returned the fee. This way fee can be defined dynamically by custom method. Return fee as array 'value' => 123, 'label' => 'Fee name'
or false if fee should not be applied. And here check if method is callable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I agree on this. Since we're applying multiple rules, I think we should set the fee in the custom method.
// Add custom state surcharge. | ||
add_action( 'woocommerce_cart_calculate_fees', array( $this, 'add_custom_state_surcharge_fee' ), 10 ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's rename to maybe_add_custom_surcharge_fees
it doesn't have to be state specific.
No need for comment as method name say it all.
if ( ! is_array( $this->get_custom_surcharges() ) ) { | ||
return; | ||
} | ||
|
||
$custom_surcharges = $this->get_custom_surcharges(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move the $custom_surcharges = $this->get_custom_surcharges();
above the sanity check, no need to call method twice. Also suggest to rename to just $surcharges
*/ | ||
$fee_info = apply_filters( 'wc_services_apply_custom_surcharge_fee', $fee_info, $key, $function_rule, $cart ); | ||
|
||
if ( isset( $fee_info['fee_text'] ) && isset( $fee_info['fee'] ) && is_float( $fee_info['fee'] ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would check if $fee_info['fee']
is_numeric
and cast it to float. Mostly for case when it's an int.
* @param array Custom surcharge function or method. | ||
* @param WC_Cart WooCommerce cart object. | ||
*/ | ||
$fee_info = apply_filters( 'wc_services_apply_custom_surcharge_fee', $fee_info, $key, $function_rule, $cart ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can call it just $fee
.
Why we pass $function_rule
in filter, how is this useful for the developer?
$has_taxable_product = false; | ||
|
||
foreach ( $cart->get_cart() as $cart_item ) { | ||
if ( $cart_item['data']->is_taxable() ) { | ||
$has_taxable_product = true; | ||
break; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Think we don't have to check if products are taxable. $cart->get_cart_contents_taxes()
check is enough, if products are not taxable get_cart_contents_taxes
will return empty array. Maybe even check if 0 < $cart->get_cart_contents_tax()
will be enough.
// Do not apply the fee if all products are virtual. | ||
if ( WC_Connect_Utils::is_all_products_are_virtual( $cart ) ) { | ||
return $fee_info; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better replace with $cart->needs_shipping()
. It's a loop over items in cart that check if each product $product->needs_shipping()
which is ! product->is_virtual()
|
||
$content_taxes = $cart->get_cart_contents_taxes(); | ||
|
||
if ( $has_taxable_product && ! empty( $content_taxes ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better to reverse the check and return early.
/** | ||
* Get US Colorado custom surcharge eligibility. | ||
* | ||
* @param WC_Cart|null $cart Cart object or null. | ||
* | ||
* @return boolean. | ||
*/ | ||
public static function get_us_co_eligibility( $cart = null ) { | ||
$fee_info = array(); | ||
|
||
if ( false === ( $cart instanceof WC_Cart ) ) { | ||
return $fee_info; | ||
} | ||
|
||
// Do not apply the fee if all products are virtual. | ||
if ( WC_Connect_Utils::is_all_products_are_virtual( $cart ) ) { | ||
return $fee_info; | ||
} | ||
|
||
// Do not apply the fee if all shipping methods use Local Pickup. | ||
if ( 0 === count( array_diff( wc_get_chosen_shipping_method_ids(), apply_filters( 'woocommerce_local_pickup_methods', array( 'legacy_local_pickup', 'local_pickup' ) ) ) ) ) { | ||
return $fee_info; | ||
} | ||
|
||
$has_taxable_product = false; | ||
|
||
foreach ( $cart->get_cart() as $cart_item ) { | ||
if ( $cart_item['data']->is_taxable() ) { | ||
$has_taxable_product = true; | ||
break; | ||
} | ||
} | ||
|
||
$content_taxes = $cart->get_cart_contents_taxes(); | ||
|
||
if ( $has_taxable_product && ! empty( $content_taxes ) ) { | ||
$fee_info = array( | ||
'fee' => 0.27, | ||
'fee_text' => __( 'Retail Delivery Fee', 'woocommerce_services' ), | ||
); | ||
} | ||
|
||
return $fee_info; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/** | |
* Get US Colorado custom surcharge eligibility. | |
* | |
* @param WC_Cart|null $cart Cart object or null. | |
* | |
* @return boolean. | |
*/ | |
public static function get_us_co_eligibility( $cart = null ) { | |
$fee_info = array(); | |
if ( false === ( $cart instanceof WC_Cart ) ) { | |
return $fee_info; | |
} | |
// Do not apply the fee if all products are virtual. | |
if ( WC_Connect_Utils::is_all_products_are_virtual( $cart ) ) { | |
return $fee_info; | |
} | |
// Do not apply the fee if all shipping methods use Local Pickup. | |
if ( 0 === count( array_diff( wc_get_chosen_shipping_method_ids(), apply_filters( 'woocommerce_local_pickup_methods', array( 'legacy_local_pickup', 'local_pickup' ) ) ) ) ) { | |
return $fee_info; | |
} | |
$has_taxable_product = false; | |
foreach ( $cart->get_cart() as $cart_item ) { | |
if ( $cart_item['data']->is_taxable() ) { | |
$has_taxable_product = true; | |
break; | |
} | |
} | |
$content_taxes = $cart->get_cart_contents_taxes(); | |
if ( $has_taxable_product && ! empty( $content_taxes ) ) { | |
$fee_info = array( | |
'fee' => 0.27, | |
'fee_text' => __( 'Retail Delivery Fee', 'woocommerce_services' ), | |
); | |
} | |
return $fee_info; | |
} | |
/** | |
* Add US Colorado "Retail Delivery Fee" Tax. | |
* | |
* @param WC_Cart|null $cart Cart object or null. | |
* | |
* @return array|false. | |
*/ | |
public static function add_us_co_rdf( $cart = null ) { | |
if ( false === ( $cart instanceof WC_Cart ) ) { | |
return false; | |
} | |
// Retail Delivery Fee is only applicable if any product in order is taxable. | |
if ( 0 === $cart->get_cart_contents_tax() ) { | |
return false; | |
} | |
// Retail Delivery Fee is only applicable when order is shipped. | |
if ( ! $cart->needs_shipping() ) { | |
return false; | |
} | |
// Do not apply Retail Delivery Fee if all selected shipping methods are Local Pickup. | |
if ( 0 === count( array_diff( wc_get_chosen_shipping_method_ids(), apply_filters( 'woocommerce_local_pickup_methods', array( 'legacy_local_pickup', 'local_pickup' ) ) ) ) ) { | |
return false; | |
} | |
return array( | |
'value' => 0.29, | |
'label' => __( 'Retail Delivery Fee', 'woocommerce_services' ), | |
); | |
} |
I renamed the method please adjust callable name in custom surcharges.
Adjusted fee to 29c based on this
Description
Adding a function for custom surcharge ( retail delivery fee ) for Colorado state. This changes also introduce a new filter hook that the user can use to disable the custom surcharge addition.
Related issue(s)
Fixes #2562
Steps to reproduce & screenshots/GIFs
Checklist
changelog.txt
entry addedreadme.txt
entry added