General best practices

In this section you will find the most common practices for a wide variety of situation while using Liquid.

Account number

There are three different methods to get an account number out of the account drop:

  • .mapped_number: returns the mapped account number and should always be used when defining ranges
  • .number: returns the mapped OR the original account number based on the option chosen in advanced settings on firm level. It should only be used for printing the account number.
  • .original_number: returns the original account number and should only be used when you explicitly want to print the original account number.

❗️

As .number can change based on your settings, never use it in the name of your custom variables like this custom.[account.number].some_value.
A better approach would be to make use of the account drop to create a custom variable, e.g. account.custom.some_value.

Attributes

When assigning variables don’t use datatype attributes like as:integer or as:currency. You can better use this when printing (export), unless you need rounding figures before doing further calculations.

{% assign gross_profit = 100 %}
{{ gross_profit | currency }}

Make sure to use a datatype attribute for the input of a custom variable (e.g. as:integer, as:currency, as:date), if you know what type of input is required.

Registers

Max 40 registers to be taken into account $0 - $39. If this amount is not sufficient, you can reset the register by assigning it equal to zero or you might rethink the way you are coding, since 40 registers is already quite a number!

Account collection

You can never check whether an account_collection is blank. You first have to use it on period.accounts assigned to a new local variable. You can now check if that local variable is blank.

{% input custom.accounts.ranges as:account_collection range:"6,7" %}

Don´t do:
{% if custom.accounts.ranges == blank %}{% endif %}

Do:
{% assign accounts = period.accounts | range:custom.accounts.ranges %}
{% if accounts == blank %}{% endif %}

Select

Always use option_values for dropdown variables and make sure option_values are never the same as options since you will probably want to make your options translatable while you do not want your option_values to be changing based on your language!

Make sure that option values have logical names.

Fori

If you want to make an item within a forloop that is linked to the iteration you are in, use key instead of forloop.index. Keys give a unique return, the return from a forloop.index is reused.

Use category.key:

{% fori category in custom.categories %}
  {% input category.name %}
  {% capture items %}items_{{ *category**.**key* }}{% endcapture %}
  {% for item in custom.[items] %}
    {% input item.name %}
  {% endfor %}
{% endfori %}

Do NOT use forloop.index:

{% fori category in custom.categories %}
  {% input category.name %}
  {% capture items %}items_{{ *forloop**.**index* }}{% endcapture %}
  {% for item in custom.[items] %}
    {% input item.name %}
  {% endfor %}
{% endfori %}

Use of blank

Always use blank, except for a collection; there you need to use empty.

{% fori item in custom.offices %}
  {% input item.office_city %}
{% endfori %}

{% if custom.offices == empty %}
  There are no offices in this collection
{% endif %}

Be aware that if you check if something == 0, it will still be true if it's blank too.

{% input custom.some.suffix as:select options:"Option 0|Option 1" option_values:"0|1" %}

{% if custom.some.suffix == 0 %}
  CHECK
{% endif %}

In your code you can bypass this problem using the logic of one of these solutions.

{% if custom.some.suffix == 0 and custom.some.suffix != blank %}
  CHECK
{% endif %}

OR

{% if custom.some.suffix.string_value == "0" %}
  CHECK
{% endif %}

Numbers starting with 0

When custom variables for phone numbers, VAT numbers, ... start with zero, the zero is not shown automatically when you printed. By using .string_value the zero will be taken into account.

{% input custom.cellphone.number %}

{{ custom.cellphone.number.string_value }}

Results

When custom variables have a default value, you may want to create a result tag to transfer the value of the custom variable (either the default value or the value that is entered manually).

{% input custom.profit.value as:integer default:100 %}
{% assign profit = custom.profit.value | default:100 %}
{% result 'profit' profit %}

However if the custom variable doesn’t have a default, it is coding wise better to directly call the custom variable using period.reconciliations.[handle].custom.profit.value.

When using custom variables from another template, make sure to check whether the template exist. This can be done by verifying whether the template is blank.

{% if period.reconciliation.tax_calculation == blank %}
...
{% else %}
 ...
{% endif %}

Unreconciled

When using unreconciled tags, it is always better to perform the calculations before the unreconciled tag and assign the result to a local variable. You can use that local variable within your unreconciled tag.

{% assign diff = total-$1 %}
{% unreconciled diff as:indicator %}

However, never add the unreconciled tag or the local variable to an ifi statement or fori-loop. The reconciled status for reconciliation templates is always calculated in input mode, so this could cause issues and discrepancies. Instead, you could work with a local variable created outside of the fori-loop (read more information on that here).

Placeholders and defaults

Make use of placeholders and defaults where necessary, and do not forget to make translations of these as well.

{% t= "Add license plate" nl:"Voeg nummerplaat toe" %}
{% input custom.car.licenseplate placeholder:"Add license plate" %}

If-statements

Consider what the best statement is for the logic you want to implement, e.g. do not use unnecessary else-statements:

Don´t do                                 Do

{% if var == x %}                        {% unless var == x %}                             
{% else %}                                   {{ content }}                                            
    {{ content }}                        {% endunless %}                    
{% endif %}
or
{% if var == x %}                        {% if var == x %}
    {{ content }}                            {{ content }}
{% else %}                               {% endif %}    
{% endif %}

{% if var == x and var > x %}            {% if var >= X %}
    {{ content }}                            {{ content }}
{% endif %}                              {% endif %}

Consider the use of variables when use both "AND" and "OR" within a single if-statement.

Don't do:

{% if var1 == true or var2 == false and var3 == true or var4 == true %}
	{% assign result = "OK" %}
{% else %}
	{% assign result = "NOK" %}
{% endif %}

Do:

{% if var1 == true or var2 == false %}
	{% assign check_1 = true %}
{% endif %}

{% if var3 == true or var4 == false %}
	{% assign check_2 = true %}
{% endif %}

{% if check_1 and check_2 %}
	{% assign result = "OK" %}
{% else %}
	{% assign result = "NOK" %}
{% endif %}

Export

In 99% of the cases, the templates created should be made available in export as well. Therefore always keep this in mind when coding that certain items should not be visible and therefore smart use of ifi and ic and nic is required.

  • Assign, unreconciled and rollforward tags can't be between ic or ifi
  • Booleans will appear as V in export when nic is not used.

Naming of parts

When using different parts, we recommend only using small letters, numbers and underscores for part names.

Please also make sure that the name of the part is different from that of any other variable.

Handles

Handles should be descriptive or should be the abbreviation of the template’s name. Furthermore it has to be unique, may not contain “.” and can only exist out of small letters and underscores.

Virtual account numbers

Virtual account number are mandatory for export purposes. Despite the fact that ‘number’ is mentioned, these virtual account numbers are not limited to numbers only. They can also contain text in them, but the main condition is that they should always start with a number.

Silverfin structures their virtual account numbers in line with the country’s chart of accounts. For Belgium, this means that all templates related to e.g. fixed assets, will start with virtual account number 2. Templates that cannot be linked to the chart of accounts start with number 8 or 9, or with letters.

❗️

Dots (.) are not allowed in virtual accounts numbers.

Mandatory fields

Mandatory fields can be used to indicate a certain input is required to be completed. The condition for the field to be required should always be assigned to a variable.

Don't do:

{% input detail.custom.category as:select
options:"Increase|Decrease|New|Unaltered" option_values:"1|2|3|4"
required:detail.custom.provision_form_2043 == true %}

Do:

{% if detail.custom.provision_form_2043 == true %}
	{% assign required_category = true %}
{% endif %}

{% input detail.custom.category as:select
options:"Increase|Decrease|New|Unaltered" option_values:"1|2|3|4" 
required:required_category %}

Impact on other templates

When the code you are working on in one template also has impact in another template, it is a good practice to leave a comment in your code referring to that situation. So, eventually, if someone else needs to make an update on that code, they would be aware that it will not only affect the current template and they can take all the considerations needed.

{% comment %}below logic has impact on the working of template handle_name{% endcomment %}