photo credit: Tooting Restart Party via photopin (license)

How to properly calculate FPT/WEEE on configurable products in Magento 2

Recently I stumbled on another nice ‘feature’ of Magento 2. When applying Fixed Product Taxes (FPT) in Magento 2, the functionality seemed a bit … broken. I was trying to apply different FPT’s on the simple children of a configurable product. A client of ours sells refrigerators and their models come in various sizes. We’ve put these up as a configurable product, whereas the size is the configurable option, and each simple product had a different WEEE Tax. I thought this would work out-of-the-box.
It didn’t.
What happened, was that the FPT that was set on the parent product ($1 for example) was used, regardless of what the FPT values of the simple products were. So even if a simple product had a WEEE Tax of $2 or $3, Magento would still use $1, since that was the value of the configurable. I reported this bug to Magento, they confirmed it (they even acknowledged that their implentation of FPT is fairly simple and is not used on meta-products), but when asked when they would implement it, they stated “it’s buried in icebox; unless there’s a massive deluge of interest/need for this it’s likely to stay there.”. Oh irony …
So I figured I would try and resolve this broken feature myself.
And so I did …

Finding the right spot

As many, many things in Magento, there are numerous ways to accomplish the same goal. My first idea was to use the event dispatched by the quote model (sales_quote_save_before ) to manually change the FPT of the configurable product to that of the underlying simple product. I’ve done this trick many times when I need to do some price adjustments when adding a product to the cart. But FPT is different: FPT is not just calculated when a product is added to the cart, but it is calculated on many more occasions. For example, when you list the products in your minicart, or when calculating the totals of the complete order. In those occasions, the Quote Item is not asked for it’s WEEE Tax; it’s re-calculated. So I couldn’t use the event-approach.
So I decided to go straight to the core. I had to find the place in the Magento code where the FPT was is calculated. If I could hook into that, all other places in Magento that needed the FPT would have my modified value. After a time of spitting through the code, I finally came by the method Magento\Weee\Model\Tax::getProductWeeeAttributes() , which is responsible for taking a product as a parameter, and returning an array with FPT information as a result. Bingo! If I could just manipulate the output of that method I would be there! And lucky me, Magento offers plugins to do so in an easy manner.
So the first thing I did was register a plugin to wrap my logic around the getProductWeeeAttributes() -method. In etc/di.xml , I added:

And I created the the corresponding plugin:

Let the headaches begin …

The next hours were pure pain and struggle. I thought I found the right spot, but I just couldn’t get it to work! I had the product, so all I had to do was perform the $callable()-method with an instance of the child product. You know, this part in the above example:

This part drove me mad! In order to get the proper ID of the simple product that was used I needed to know what product the customer had selected. To know that, I needed to have access to the current quote. But I didn’t have a quote! I only had a product! I figured out that I could use $billing  to get the quote (by using $billing->getQuote() ). Now I would have to iterate through all the items in the quote to find the item with the matching product ID, check which other quote item used it as a parent ID and get the product ID from that. You know, something like this:

Yeah, I know it looks ugly but it worked.
Unless …

Unless what?

Unless you add a configurable product to the cart more than once (you know, with different configurations). In that case, the $parentItem  would become a false positive, resulting in a wrong $simpleId , and the FPT would just be a mess.
At this point I was completely bumped. Now what? I tried all kind of ways to get correct $simpleId , but the code would just become messier and the resulting FPT would still be incorrect. The big problem was just that I could not get the proper Quote Item if I would only have a product and a total quote to work with (which makes sense of course).

A bright moment

Then I had a bright moment. I figured that it was not up to me to figure out what was the correct quote item to use, it was information that could easily be provided to me higher up the chain. So I started tracing it back where that call comes from. I know that a Quote has a collectTotals() -method that iterates through all the Tax Models to get the proper tax values. So that was where I started. From there I came to Magento\Weee\Model\Total\Quote\Weee::collect() , that has a foreach() -loop in it that iterates through the quote items. Inside this loop the method Magento\Weee\Model\Total\Quote\Weee::process()  is executed which – as a first thing to do – executes the Magento\Weee\Model\Tax::getProductWeeeAttributes() -method, around which my plugin is wrapped. Bingo! If I could just manipulate the product model in this method so it would know if it’s quote item the link would be complete.
Unlucky for me, the process() -method is a protected method so I couldn’t hook into this with a plugin. So I had to write a rewrite for this one. In etc/di.xml, I added the following:

This way, my class would be used instead of that of Magento. In Vendor\Module\Rewrite\Magento\Weee\Model\Total\Quote\Weee  I added the following code:

The only interesting thing of this script is:

Which adds a reference to the quote item to my product model.
Now, with this set up, all I had to do was change the following code in my plugin:

That’s it! Now when I add a configurable product to the cart, or change the cart/quote on any given location, the FPT of the simple product is used, and not that of the configurable.

In conclusion

Well, I hope this article might help someone one day who encountered the same problem (if you’re that someone, a little ‘thank you’ in the comments would be nice ;-)). But the main message of this article is that it took me hours of figuring out and a lot of trial and error to eventually come to a solution that’s effectively around 15 lines of code (if you don’t count comments and constructors and closing brackets and stuff). It’s a fine example of how time spent with programming is not always translated to the amount of lines you write (as some managers might think). Au contrair: the less code you write, the better!

Visitors give this article an average rating of 4.1 out of 5.

How would you rate this article?

One thought on “How to properly calculate FPT/WEEE on configurable products in Magento 2”

  1. John Park says:

    Hi Do you have this plugin as a zip? Thanks

Leave a Reply