Apply a fee per item based on product attribute size in WooCommerce

Apply a fee per item based on product attribute size in WooCommerce

The goal is to use the cart item attribute size to calculate a fee per item.

Here is my code attempt:

add_action( 'woocommerce_cart_calculate_fees',
'wpf_wc_add_cart_fees_by_product_attribute' );

if ( ! function_exists( 'wpf_wc_add_cart_fees_by_product_attribute' ) )
{
     function wpf_wc_add_cart_fees_by_product_attribute( $cart ) {
         if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;

         // Attribute slug => fee per item
         $fees = array(
             '50ml'   => 0.05,
             '100ml'    => 0.05,
             '200ml'  => 0.05,
             '750ml'     => 0.10,
             '375ml'     => 0.05,
             '9pk'     => 0.45,
   //          '12pk'    => 0.60,
             '15pk'    => 0.75,
             '18pk'    => 0.90,
             '24pk'    => 1.20,
             '30pk'    => 1.50,  
             'crvbox'     => 0.25,
         );

         foreach ( $cart->get_cart() as $cart_item ) {
             $product      = $cart_item['data'];
             $product_id   = $cart_item['product_id'];
  //           $quantity     = $cart_item['quantity'];

             // Adjust this attribute name to match what you use, e.g., 'pa_size'
             $attribute_taxonomy = 'pa_size';
             $terms = wc_get_product_terms( $product_id,
$attribute_taxonomy, array( 'fields' => 'slugs' ) );

             if ( ! empty( $terms ) ) {
                 foreach ( $terms as $term_slug ) {
                     if ( isset( $fees[ $term_slug ] ) ) {
                         $fee = $fees[ $term_slug ];
      //                   $fee_per_item = $fees[ $term_slug ];
     //                    $total_fee = $fee_per_item * $quantity;

                         $cart->add_fee( 'CRV Fee - ' .
$product->get_name(), $fee, true );
                         break; // Prevent multiple fees if multiple matching attributes exist
                     }
                 }
             }
         }
     }
}

This works acually only for my single products.

But ite doesn't work for variable products as it takes the first variation size. For example, I have product with the following variations 12pk, 18pk, 30pk, 6pk. Regardless of the pack size in cart, the fee is always calculated for the first size 12pk. If I comment out the 12pk line in my code, it uses the next 18pk.

How to make the code work for simple and variable products in the correct way, taking into account the size assigned to the product (or the variation for variable products)?

Answer

For variable products you need to use the product variation object or ID, wich is not the case in your current code. Also your code can be simplified and optimized.

Try the following instead that should work for all products including variations:

add_action( 'woocommerce_cart_calculate_fees', 'add_fees_by_product_size' );

if ( ! function_exists( 'add_fees_by_product_size' ) )
{
    function add_fees_by_product_size( $cart ) {
        if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;

        $taxonomy = 'pa_size'; // Attribute taxonomy
        $fees     = array( // Attribute slug => fee per item
            '50ml'    => 0.05,
            '100ml'   => 0.05,
            '200ml'   => 0.05,
            '750ml'   => 0.10,
            '375ml'   => 0.05,
            '9pk'     => 0.45,
            '12pk'    => 0.60,
            '15pk'    => 0.75,
            '18pk'    => 0.90,
            '24pk'    => 1.20,
            '30pk'    => 1.50,  
            'crvbox'  => 0.25,
        );

        // Loop through cart items
        foreach ( $cart->get_cart() as $item ) {
            $product    = $item['data']; // Get the product object
            $quantity   = $item['quantity']; // Get the quantity
            $attributes = $product->get_attributes(); // get the product attributes

            if ( isset($attributes[$taxonomy]) ) {
                // Ensure to get the correct term slug from simple product or product variation
                $term_slug = is_object($attributes[$taxonomy]) ? current($attributes[$taxonomy]->get_slugs()) : $attributes[$taxonomy];

                if ( isset($fees[$term_slug]) ) {
                    $cart->add_fee( sprintf( esc_html__('CRV Fee - %s'), $product->get_name()), $fees[$term_slug], true );
                }
            }
        }
    }
}

It should work.

Enjoyed this article?

Check out more content on our blog or follow us on social media.

Browse more articles