I'm fairly new to Freemarker. I'm trying to create an email template that will list invoices, and then the total amount due at the bottom. However, there needs to be a different "Total Amount" based on the currency (some customers might have 3 invoices in EUR, 2 in GBP, etc).
I'm creating an associative array where the keys are the currency and the values are the total amount in that currency. For each invoice, I need to add the amount due to the value of the correct currency. But I'm getting an error that there is an unexpected character.
Here's the gist:
<#assign totalarr = {} />
<#list invoicelist as invoice>
<#assign invcur = invoice.currency />
<#assign invamt = invoice.amountremaining />
<#assign totalarr[invcur] = totalarr[invcur] + invamt />
</#list>
The error is in the second-to-last line, where I'm trying to add the amount to the total value. Any ideas?
Thanks!
-Kristin
To change hash subvariable value you need to use concatenation like this:
<#assign totalarr = {} />
<#list invoicelist as invoice>
<#assign invcur = invoice.currency />
<#assign invamt = invoice.amountremaining />
<#assign sum = totalarr[invcur]!0 />
<#assign totalarr = totalarr + {invcur : sum + invamt} />
</#list>
<#list totalarr?keys as key>
${key} = ${totalarr[key]}
</#list>
Related
Is there a way in freemarker to convert the below html string into plain-text?
Challange: data assign in below tag is dynamic including links and their placement.
<#assign data = '<li>List item 1 with some link</li><li>List item 2 with some link1 and some link2</li>' />
Expected: Need data in plain text format like below:
- List item 1 with some link <https://some-link.com>
- List item 2 with some link1 <https://some-link1.com> and some link2 <https://some-link2.com>
What I have achieved till now:
I am able to remove the complete html code using regex freemarker but it also removes the anchor tag urls which cause unexpected results.
${data?replace("<[^>]*>", "", "r")}
// result
- List item 1 with some link
- List item 2 with some link1 and some link2
Any help would really appreciate!!
Please find the solution below.
<#function htmlListToPlainText param>
<#assign str = param?replace('<ol>','')?replace('</ol>','')?split('</li>')/>
<#assign result>
<#list str as item>
<#if item?contains('<a')>
<#assign anchors = item?matches(r"href='(.*?)'") />
<#if anchors?size == 0>
<#assign anchors = item?matches(r'href="(.*?)"') />
</#if>
<#assign mitem = item?replace('</a>','[0]','f')?replace('</a>','[1]','f')?replace('</a>','[2]','f')?replace("<[^>]*>", "", "r") />
<#list anchors as m>
<#assign mitem = mitem?replace('[${m?index}]',' <${m?groups[1]}>','f') />
</#list>
-${mitem}
<#elseif item?has_next>
-${item?replace("<[^>]*>", "", "r")}
</#if>
</#list>
</#assign>
<#return result>
</#function>
How to Use
${htmlListToPlainText(YoursignalName)}
Limitation:
Handling upto 3 dynamic links in one <li> tag.
I need to iterate through a list made from the "split" built-in yet I haven't succeeded.
I have a two lists of dates that share some of the same dates. using "seq_index_of" as a "Vlookup" i'm able to find the correlation and get the index of the dates that are in both lists. however, "seq_index_of" gives a string, number, boolean or date/time values output and not a sequence (for me to iterate).
I use "split" to turn the string into a sequence. split wont truly do the job, though.
first i use seq_index_of for the two lists:
this will give me the index of correlated dates.
<#assign result>
<#list list1 as L1>
${list2?seq_index_of(L1)}
</#list>
</#assign>
---------------------------------------
next I iterate the result. for this, imagine "array" is the result from above:
ex 1. this does not work. cant iterate
<#assign array = "2020-10-02,2021-10-04,2022-10-04,2023-10-04" />
<#assign mappedArray_string = []>
<#list array?split(",") as item>
<#assign mappedArray_string += [item]>
</#list>
${"is_sequence: " + mappedArray_string?is_sequence?c}
<#--print the list-->
<#list mappedArray_string as m>
<#--i cant do ?string("MM/dd/yyyy") or ant other iteration
${m?string("MM/dd/yyyy")}
<#if m?has_next>${";"}<#else>${"and"}</#if>-->
<#with no iteration, this will work. not what i need-->
${m}
</#list>
+++++++++++++++++++++++++++++++++++++++++++++
ex 2. - this works with a regular numerical list
<#assign array = [100, 200, 300, 400, 500] />
<#assign mappedArray_numbers = []>
<#list array as item>
<#assign mappedArray_numbers += [item]>
</#list>
${"is_sequence: " + mappedArray_numbers?is_sequence?c}
<#--print the list-->
<#list mappedArray_numbers as m>
${m?string("##0.0")}<#-- able to iterate-->
</#list>```
expected:
date1 ; date2 ; date3 and lastdate
error:
For "...(...)" callee: Expected a method, but this has evaluated to a string (wrapper: f.t.SimpleScalar):
==> m?string [in template "preview-template" at line 27, column 3]
----
FTL stack trace ("~" means nesting-related):
- Failed at: ${m?string("MM/dd/yyyy")}
If you want to list the items of list2 that also occur in list1, then do this (requires FreeMarker 2.3.29 because of ?filter):
<#list list2?filter(it -> list1?seq_contains(it)) as d>
${d?string('MM/dd/yyyy')}
</#list>
Before 2.3.29:
<#list list2 as d>
<#if list1?seq_contains(d)>
${d?string('MM/dd/yyyy')}
</#if>
</#list>
I want to make a subtotal of values corresponding to the same tax code in Netsuite Advanced PDF/HTML. Is it even possible? What are other ways to achieve my goal?
This is definitely possible. The templating engine that Netsuite uses under the hood is Apache FreeMarker, and I would highly recommend looking at the documentation there for a full details.
Here is some basic untested code that you can use to start:
<#assign tax1subtotal = 0 >
<#assign tax2subtotal = 0 >
<#assign tax3subtotal = 0 >
<#list record.item as item>
<#if item.taxcode == [taxcode1]>
<#assign tax1subtotal = tax1subtotal + item.tax1amt>
</#if>
<#if item.taxcode == [taxcode2]>
<#assign tax2subtotal = tax2subtotal + item.tax1amt>
</#if>
<#if item.taxcode == [taxcode3]>
<#assign tax3subtotal = tax3subtotal + item.tax1amt>
</#if>
</#list>
Tax Type 1 Subtotal: ${tax1subtotal}<br/>
Tax Type 2 Subtotal: ${tax2subtotal}<br/>
Tax Type 3 Subtotal: ${tax3subtotal}<br/>
I have two sequences of integers. I want to determine if the sequence A contains a values of seuqence B, and then get the values of B that are inside A.
This is what I tried:
<#assign myArticles = [50901,50369] />
<#assign articles = [50369, 50379, 50901] />
<#list myArticles as article>
${articles?seq_contains(article)?string("yes", "no")}
</#list>
And it works (result "yes"), but when I use a List it doesn't work (result "no"):
<#assign articlesList = articleService.getArticles(groupId) />
<#assign articlesArray = [50901,50369] />
<#assign articlesId = []>
<#list articlesList as item>
<#assign articlesId = articlesId + [item.getArticleId()]>
<#-- articlesId gives me this ids: 50369, 50379, 50901 -->
</#list>
<#list articlesArray as article>
${articlesId?seq_contains(article)?string("yes", "no")}
</#list>
Why?
I am working on FreeMarker template for creating chart outputs using HighChart and we have a requirement to do double sorting based on 2 different columns.
Example: Sorting country list using 2 different columns "Region" and "Country"
I checked other forums and found double sorting has been defined within array elements as specified below but not at an individual element level.
current.children?sort_by('type')?sort_by(['properties','cm:name'])
Here is the code snippet
<#--Assign the array values-->
<#assign country = country + [ {
"category":"${level1}",
"value":level1Value?number,
"color": color?string,
"superRegion":"${level3?upper_case}",
"region":"${level2?upper_case}"
} ] >
Sorting the list - The below double sorting doesn't work. Does anyone know how to do this double sorting ?
<#list country?sort_by("superRegion")?reverse?sort_by("region") as countrySorted>
?sort_by can't sort by a composite "key" (as of 2.3.23). Applying ?sort_by twice just re-sorts the whole list.
Usually, lists should be sorted before passing them to the template anyway. I'm not sure if that's feasible in your case. If not, you can still write a TemplateMethodModelEx for it and put that into the data-model etc. (Plus I have made a note that ?sort_by should be able to do this, since it already exists anyway... maybe some contributor will pick it up.)
Resolved case like this. Using sort_by function twicely won't help you. I resolved this way: list, filter, sort_by. Here example.
Can be checked here: https://try.freemarker.apache.org/
Template:
Sorted by gender:
<#list humans?sort_by("gender") as h>
<#if !(genders??)>
<#assign prevGender = h.gender />
<#assign genders = h.gender + "---" />
</#if>
<#if prevGender != h.gender>
<#assign genders = genders + h.gender + "---" />
</#if>
<#assign prevGender = h.gender />
index: ${h.index}, gender: ${h.gender}, name: ${h.name}, age: ${h.age}.
</#list>
Sorted by gender, name:
<#list genders?split("---") as g>
<#if g?? && g?trim!="">gender: ${g}</#if>
<#list humans?filter(x -> x.gender = g)?sort_by("name") as x>
index: ${x.index}, gender: ${x.gender}, name: ${x.name}, age: ${x.age}.
</#list>
</#list>
Data model:
humans=[{"index":0,"gender":"male","name":"Bazil","age":34},{"index":1,"gender":"male","name":"Serhio","age":33},{"index":2,"gender":"male","name":"Andrew","age":32},{"index":3,"gender":"male","name":"Arty","age":34},{"index":4,"gender":"male","name":"Alex","age":33},{"index":5,"gender":"female","name":"July","age":30},{"index":6,"gender":"female","name":"Anna","age":31},{"index":7,"gender":"female","name":"Tatiana","age":32}]
You can overwrite your list:
<#assign mylist = mylist?sort_by("ID") />
<#assign mylist = mylist?sort_by("TYPETRAITEMENT") />
<#assign mylist = mylist?sort_by("DOMAINEMETIER") />