We continue our series of interesting support questions to help you customize your EvolutivoFW application.

GenDoc: join a list of related records

The question is that we want to create a list of names from the related contacts of an account, but we want them to be all together, in one line.

The first approach is to use a direct {foreach}

{paracada Contacts}
{Contacts.salutationtype} {Contacts.firstname} {Contacts.lastname}, con DNI: {Contacts.phone} y
{/paracada}
en nombre y representación de {Accounts.accountname}, con CIF {Accounts.siccode} y con domicilio en {Accounts.bill_street}. {Accounts.bill_code} - {Accounts.bill_city}  ({Accounts.bill_state}),

There are a few problems with this solution:

--None-- Lina Schwiebert, con DNI: 03-3608-5660 y
--None-- Felix Hirpara, con DNI: 717-491-5643 y
Bernardine Rodefer, con DNI: 901-901-4726 y
Eleni Vanscoik, con DNI: 01208-373467 y
--None-- Tonette Wenner, con DNI: 516-968-6051 y
--None-- Joanna Leinenbach, con DNI: 561-470-4574 y
nuevo uno, con DNI: y
en nombre y representación de Chemex Labs Ltd, con CIF y con domicilio en C/ Joan Fuster 12. 03779 - Els Poblets  (Alicante), 

As we can see, each contact is in its own line, not together, and we have an extra "and" (y) in the last row. The first problem cannot be solved with {foreach} because, by definition, each iteration is a new paragraph. For the second problem, we can use a workflow expression to show or not the "and":

{paracada Contacts}
{Contacts.salutationtype} {Contacts.firstname} {Contacts.lastname}, con DNI: {Contacts.phone} {expresionif '~esultimaiteracion¬' == 'false' then 'y' else '' end}
{/paracada}
en nombre y representación de {Accounts.accountname}, con CIF {Accounts.siccode} y con domicilio en {Accounts.bill_street}. {Accounts.bill_code} - {Accounts.bill_city}  ({Accounts.bill_state}),

We use the expression {expresionif '~islastiteration¬' == 'false' then 'y' else '' end}. The GenDoc directive islastiteration returns the string "true" for the last iteration of the {foreach} and "false" for all the other iterations. This permits us to set or not the connector.

Now that we have opened the VERY powerful workflow expression can of worms, we will continue to use it to solve the problem. We are going to create a workflow expression that will return the concatenated string that we need. This looks like this:

{expresionsubstring(aggregation_fields_operation('group_concat', 'Contacts', 'concat(salutation," ",firstname," ",lastname,", con DNI: ",vtiger_contactdetails.phone," y") separator " "'), 0, -2)} en nombre y representación de {Accounts.accountname}, con CIF {Accounts.siccode} y con domicilio en {Accounts.bill_street}. {Accounts.bill_code} - {Accounts.bill_city}  ({Accounts.bill_state}),

Here we use the workflow expression aggregation_fields_operation to launch a group_concat of the string we need using a preset separator. Then we use the expression substring to eliminate the last connector.

--None-- Lina Schwiebert, con DNI: 03-3608-5660 y --None-- Felix Hirpara, con DNI: 717-491-5643 y Bernardine Rodefer, con DNI: 901-901-4726 y Eleni Vanscoik, con DNI: 01208-373467 y --None-- Tonette Wenner, con DNI: 516-968-6051 y --None-- Joanna Leinenbach, con DNI: 561-470-4574 y nuevo uno , con DNI: en nombre y representación de Chemex Labs Ltd, con CIF y con domicilio en C/ Joan Fuster 12. 03779 - Els Poblets  (Alicante),

We have a very resolutive workflow expression language. The learning curve is steep, but very powerful nonetheless.

I share here the template and merged result for reference.

GenDoc: Typical counter "x of y" for a foreach

Constructing on the previous question, I asked myself, what if I needed to create the typical "x of y" for each iteration in the {foreach}?

I know we have both the {iteration} and {numiterations} directives, so it should be as easy as adding them inside the loop. I created this GenDoc

{paracada Contacts}
{repeticion}/{numiteraciones}   {Contacts.salutationtype} {Contacts.firstname} {Contacts.lastname}, con DNI: {Contacts.phone} {expresionif '~esultimaiteracion¬' == 'false' then 'y' else '' end}
{/paracada}
en nombre y representación de {Accounts.accountname}, con CIF {Accounts.siccode} y con domicilio en {Accounts.bill_street}. {Accounts.bill_code} - {Accounts.bill_city}  ({Accounts.bill_state}),

Which looks like this:

1/7 --None-- Lina Schwiebert,   con DNI: 03-3608-5660 y
2/7 --None-- Felix Hirpara, con DNI: 717-491-5643 y
3/7  Bernardine Rodefer,    con DNI: 901-901-4726 y
4/7  Eleni Vanscoik,    con DNI: 01208-373467 y
5/7 --None-- Tonette Wenner,    con DNI: 516-968-6051 y
6/7 --None-- Joanna Leinenbach, con DNI: 561-470-4574 y
7/7  nuevo uno, con DNI: 
en nombre y representación de Chemex Labs Ltd, con CIF y con domicilio en C/ Joan Fuster 12. 03779 - Els Poblets  (Alicante),

Awesome!

I share here the template and merged result for reference.

How can I get the last comment created for a record?

This question has an easy answer because we have a workflow meta variable named comments that does this for us. You can read all about it on the workflow email documentation page.

The exact syntax to get only the last comment in raw format is:

$(general : (__VtigerMeta__) comments_1d_text_comment)

Return only one comment, in descending order, in text format, in other words; the last one.

This works for any module that has comments activated, but it gets a little more interesting. This meta variable only works in our templating language, which is used mostly for emails. What if we need to have that value as a workflow result to save in a field, for example? In fact, one of the side requests of this question was to be able to add the last comment to a report or as a GenDoc paragraph. In this case, we need to be able to execute that meta variable as a workflow expression to save in a field for reporting or to be executed as an expression in GenDoc.

There are various ways to get this last comment using the workflow language but I am going to explain the easiest and most extensible: Condition Expression Business Actions.

The condition expression business map supports 3 types of expressions:

  • Expression: a workflow expression
  • Function: an internal PHP call to any existing function in the code
  • Template: a merge of a piece of text using our templating language

So, we can easily generate a large piece of text using our templating language by putting it inside a template type condition expression. For example, we could use this to generate generative AI prompts. In this case, we just want the last comment so our business action looks like this:

Template Condition Expression

Once we have that map we can get the result from the workflow expression like this:

template expression workflow evaluation

This is a very easy way to generate long sentences or paragraphs in the workflow system, we usually do those with complex concat expressions where balancing the variables, commas, and quotes gets messy and hard to maintain very quickly. With the usage of a template type condition expression that gets easy.

Powerful and customizable!

Photo by Neil Thomas on Unsplash

Previous Post