If you use advanced templates and item groups, you got problems! Although Freemarker is a wonderful templating language, it is not a programming language. Here are some tips and tricks I learned when dealing with item groups and advanced templates.
Rule #1: The scope of variables in Freemarker is somewhat illusive. I’ll spare you the details, but recommend using unique variable names in all functions. You’ll see that in my examples.
Rule #2: Freemarker supports the creation of objects with properties. However, it will not let you update an object after it’s been instantiated.
Rule #3: If you are going to work with numbers, make double sure that whatever you’re working with is a number, variable!0 does not accomplish this.
Below is code taken from an advance template applied to sales orders. This section is where I check to make sure there are items and then looping through them to print each one on the PDFed output.
As I’m looping through all items, you see on line 503 I pass the index of the current item to the getItemDetails() function that returns an object with properties taken from the current line.
Here is where you hit a problem with “Item Group” items. Item group items consist of a “Group” line with a quantity but no unit price or extended price, followed by items of varying types, each with a quantity and unit price, followed by a “Group End” item, again with no quantity or unit price. In order to show the group item correctly with the correct totals, you need to read all items in the group keeping track of the totals yourself. Here is the twist. If the quantity on the group is not 1 and quantities of items in the group are not 1, you’re hosed! In order to get your unit price, you need to calculate the extended price of each line. When done, take the total and divide by the quantity on the original group item. This is unbelievable to me, but the quantities of items in the group already reflect the quantity of the group, BUT can be modified.
As an example, let’s say you have a group of dinnerware with quantity of 2. The items in the group are 4 plates and 4 cups. The group item will show quantity of 2. The quantity of plates in the group will be 8 and the quantity of cups will be 8. The extended price of each line must be calculated as quantity * price. If for some reason, and this is definitely possible once the group is initially added to the order, someone edits the line associated with plates and changes the quantity back to 4, the group’s total unit price and extended price can no longer be calculated by summing the unit prices of all lines and multiplying by the quantity on the group. I hope that’s not too convoluted, but it’s still true! Try it yourself. Groups, unlike Kits, can be modified independent of their definition.
So keep reading…
In line 185, you see a call to getLineDetails, which returns an object. Then lines 192 through 215 “read ahead” looking for the end of the group, keeping track of each line’s extended price (quantity * unit price). When the end of the group is located, the original line, the “Group” line, gets the total extended price. The group’s unit price is then calculated as the extended price divided by the quantity on the group. This “fixes” all the changes made to unit prices and quantities of items in the group after the group was added to the order. Inconceivable (Princess Bride)!
From before, where we called getItemDetails, you see it returns an object with modified properties. It also skips over items inside a group item, calling getGroupDetails to accumulate all details associated with the group into one object with the correct properties to print in that line.
And finally, you’ll see references to updateLine(). You’ll find that you can create objects, but you cannot modify properties of an object once it’s been instantiated. So…. you need to clone the object with the updated property and move forward with the replica. You’ll see how that is done here.
I am unable to give you the code in a form that can be copied. You may have already noticed the missing line numbers and lines through my screenshots. That is because I have removed references to the “secret sauce.” My wife just finished a 9 week cooking class taught be a master chef. She told me she learned that many of the great chefs tweak their recipes ever so slightly in cookbooks, as they don’t want anyone copying them with the result being as good as the original. I don’t consider myself a proud programmer and I’m not trying to water down the lesson, but there are some trade secrets here that you’ll just need to discover for yourself.
Best of luck, and I hope this enlightens both you and me (as I often return to these blog posts as notes). Freemarker is quirky, but it’s all we got!