phpwoocommercefee

How to calculate handling fee based on currency and attribute value of cart items in WooCommerce


I have a code snippet running on a Woocommerce site that calculates and adds a handling fee to the cart total.

There are two parts to the calculation:

  1. If the shipping rate is greater than 0, then add 18% of the shipping rate;
  2. If a product in the cart has a particular attribute, add a set fee dependent on the currency of the cart.

The snippet does all of this fine, however it fails if the cart contains a mixture of products.

That is, if one product has the particular attribute, and another product doesn't have this attribute, then the set fee is not added.

I need the set fee to be added if at least one product in the cart has this attribute. Here is the code:

<?php
// Hook to add a handling fee
add_action( 'woocommerce_cart_calculate_fees','handling_fee' );
function handling_fee($cart_object) {
  global $woocommerce;

  $specialfeeattr = 71; // attribute id for the special fee

  $spfee = 0.00; // initialize special fee
  $percentage = 0.18; // percentage to add to shipping total

  $orderFeeUSD = 3.20; //fee per transaction USD
  $orderFeeCAD = 4.00; //fee per transaction CAD
  $orderFeeEUR = 2.62; //fee per transaction EUR
  $orderFeeGBP = 2.26; //fee per transaction GBP

  $currentCurrency = get_woocommerce_currency();

  if ( is_admin() && ! defined( 'DOING_AJAX' ) )
      return;

      foreach ( $cart_object->cart_contents as $key => $value ) {

          $proid = $value['product_id']; //get the product id from cart

          $product = wc_get_product( $proid );
      $attr =  $product->get_attributes();

          //check if attributes array has fulfillment attribute included
          if (array_key_exists ('pa_fulfillment', $attr )) {
              $attrFulfillment = $attr['pa_fulfillment'];
          }
          if (isset($attrFulfillment)) {
              $attrFulfillmentOptions = $attrFulfillment['options'];
          }

    if ($woocommerce->cart->shipping_total > 0) {
            $spfee = (($woocommerce->cart->shipping_total) * $percentage);
    }
          //only check if fullfillment option is set to warehouse if fulfillment is not null
          if (isset($attrFulfillmentOptions)) {
              //if the product in cart contains attr id 71
              if (in_array($specialfeeattr, $attrFulfillmentOptions )) {
          if($currentCurrency == 'USD'){
           $spfee += $orderFeeUSD;
         } elseif($currentCurrency == 'CAD') {
           $spfee += $orderFeeCAD;
         } elseif($currentCurrency == 'EUR') {
           $spfee += $orderFeeEUR;
         } elseif($currentCurrency == 'GBP') {
           $spfee += $orderFeeGBP;
                  }
              }
          }
      }

          $woocommerce->cart->add_fee( 'Handling', $spfee, true, 'standard' );
      }

?>

Thank you very much for any advice on how to fix this.


Solution

  • You are initializing the $spfee variable with these lines on each iteration:

    if ( $woocommerce->cart->shipping_total > 0 ) {
        $spfee = $woocommerce->cart->shipping_total * $percentage;
    }
    

    In this way you only get the sum of the percentage on the shipping (if this is greater than zero) and the fee on the last iterated cart item.

    Then try to insert the previous lines of code before the start of the loop.

    If you want to apply the standard tax rate, assign a blank value to the fourth parameter of the add_fee method, as reported in the documentation.

    Also there are other optimizations to the code, try this:

    // Hook to add a handling fee
    add_action( 'woocommerce_cart_calculate_fees','handling_fee' );
    function handling_fee( $cart_object ) {
    
        if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
            return;
        }
    
        global $woocommerce;
    
        $specialfeeattr  = 71; // attribute id for the special fee
    
        $spfee           = 0.00; // initialize special fee
        $percentage      = 0.18; // percentage to add to shipping total
    
        $orderFeeUSD     = 3.20; //fee per transaction USD
        $orderFeeCAD     = 4.00; //fee per transaction CAD
        $orderFeeEUR     = 2.62; //fee per transaction EUR
        $orderFeeGBP     = 2.26; //fee per transaction GBP
    
        $currentCurrency = get_woocommerce_currency();
    
        if ( $woocommerce->cart->shipping_total > 0 ) {
            $spfee = $woocommerce->cart->shipping_total * $percentage;
        }
    
        foreach ( $cart_object->cart_contents as $key => $value ) {
    
            $proid   = $value['product_id']; //get the product id from cart
    
            $product = wc_get_product( $proid );
            $attr    = $product->get_attributes();
    
            //check if attributes array has fulfillment attribute included
            if ( array_key_exists ( 'pa_fulfillment', $attr ) ) {
                $attrFulfillment = $attr['pa_fulfillment'];
            }
            if ( isset($attrFulfillment) ) {
                $attrFulfillmentOptions = $attrFulfillment['options'];
            }
    
            // only check if fullfillment option is set to warehouse if fulfillment is not null
            if ( isset($attrFulfillmentOptions) ) {
                // if the product in cart contains attr id 71
                if ( in_array( $specialfeeattr, $attrFulfillmentOptions ) ) {
                    switch ( $currentCurrency ) {
                        case 'USD':
                            $spfee += $orderFeeUSD;
                            break;
                        case 'CAD':
                            $spfee += $orderFeeCAD;
                            break;
                        case 'EUR':
                            $spfee += $orderFeeEUR;
                            break;
                        case 'GBP':
                            $spfee += $orderFeeGBP;
                            break;
                    }
                }
            }
    
        }
    
        $woocommerce->cart->add_fee( 'Handling', $spfee, true, 'standard' );
        
    }
    

    The code should work. Add it to your active theme's functions.php.