Creating Report Templates in upmesh

Reports
Company Settings

In upmesh, you can create reports in several places: as a daily report, ticket report, or project report. For daily reports and ticket reports, standard reports are already available to you. As a company admin, you can also upload your own report templates in the company administration.

To create your own reports, you'll need:

  • MS Office or another program that can create .doc or .docx files

  • The documentation and variables from the info dialog at https://app.upmesh.de/company/reportTemplates

  • Knowledge of how to apply this information. That's exactly what we'll cover in this guide.

How do the variables work?

In the "Ticket Report", "Daily Report", and "Project Report" tabs, you'll find the variables available for the respective report. They're sorted by category and each shows a description, a data type, and how to write the variable.

The data type tells you whether it's a string (a piece of text) that can be inserted directly into the document, or whether it needs special handling. Under "Variable," you'll see the path you use to reference that variable.

Note: if this path contains [], it means you're dealing with an array (a list). For example, to output the title of a ticket, you first need to specify which ticket should be output, or use a loop to go through all tickets and print the title for each one. More on this in the "For Loops" section.

Data types and how to use them

Let's start with the data types, assuming the variable doesn't have [] in its path.

Strings are text and can be inserted without any further preparation. You use them by placing them inside double curly braces. The project name might look like this: {{project.title}}. The braces indicate that this is a code snippet that will be replaced during report generation.

Number variables can be inserted just like strings.

You can insert a Date just like a string too. The result will look like this though: 2026-07-07T11:41:41.928Z. It looks nicer if you use the code examples from the first tab of the info dialog. These show various ways to format it:

  • Date including time: {{ new Date( DATE-FIELD ).toLocaleString('de-DE') }}

  • Only the time: {{ new Date( DATE-FIELD ).toLocaleTimeString('de-DE') }}

  • Only the day: {{ new Date( DATE-FIELD ).toLocaleDateString('de-DE') }}

  • Today's date: {{ new Date(new Date()).toLocaleDateString('de-DE') }}

Note: depending on the language setting, MS Word replaces the quotation marks you type in the last bracket with different ones. These don't work. You can recognize them because they're shaped differently, with the opening quotation mark sitting low instead of high. It's best to copy and paste the quotation marks instead of typing them.

"Null" means the type can be empty. The type "Date | null" means either a date or empty. Important: use an IF statement to make sure the variable exists before you use it.

Booleans can only have one of two values: true or false. If you use them like a string, exactly that value gets printed. With an IF statement, you can change how it's displayed, for example {{IF $t.archived}}archived{{END-IF}} prints "archived" if t.archived is true.

Objects are indicated either as "object" or as curly braces containing further variable names. The tags in the ticket report, for example, are an object array: {tagName: string; groupName: string; color: string }[].

An object always contains further information. If you output it like a string, it's displayed in the same format as the data type, but with the actual data filled in. A tag might look like this: {tagName: Tag, groupName: Group, color: #ffffff }. This display can help you when testing and troubleshooting. Note: not all properties of an object are always shown if they aren't filled in.

To display the tag name in this example, you can write {{ $t.tagName}} if you previously defined t as a single tag (see the "For Loops" and "Variables & Calculations" sections).

IF & END-IF

IF statements tell upmesh: "If X, then do Y." They always start with IF and end with END-IF. In the first curly brace after the IF is the condition (if X), and between the curly braces of IF and END-IF is what should happen (then do Y).

If you forget the END-IF, report generation will fail. It's best to write the END-IF right after the IF, before you do anything else. That way you won't forget it.

Example:

{{IF project.title === 'upmesh'}}
{{project.title}}
{{END-IF}}

This means: "If the project name is 'upmesh', print the project name at this point in the report."

The opposite is possible too:

{{IF project.title !== 'upmesh'}}
{{project.title}}
{{END-IF}}

This means: "If the project name is not 'upmesh', print the project name at this point in the report."

In lists, this can look like this:

{{IF Array(custom.Projekte).toString().includes('upmesh')}}Yes{{END-IF}}
{{IF Array(custom.Projekte).toString().includes('upmesh')== false}}No{{END-IF}}

If the custom list field "Projekte" contains the value "upmesh", upmesh prints "Yes" at this point in the report. If the field doesn't contain the value "upmesh", upmesh prints "No".

IF statements are absolutely necessary for variables that can be null or missing, otherwise report generation will fail:

{{IF tickets[0]}}
{{tickets[0]}}
{{END-IF}}

This means: "If at least one ticket exists, print it."

{{IF tickets[0] && tickets[0].assignee}}
{{tickets[0].assignee}}
{{END-IF}}

This means: "If at least one ticket exists and it has an assignee, print that assignee."

Since a new report is tested in the background with a small test project when you save it, a forgotten IF statement can already cause error messages when saving. But this isn't always the case, especially nested IF statements often only show up as problems later. When in doubt, it's better to add one IF statement too many than too few.

For Loops

For loops let you go through lists. They tell upmesh: "For each element in this list, do X."

{{FOR ticket IN tickets}}
#{{$ticket.ticketNumber}} {{$ticket.title}}
{{END-FOR ticket}}

This loop goes through the tickets and creates a new variable called ticket, which holds the current list element. Since it's a separate variable, you need to add a $ before the name when you use it. In this example, the ticket number and title are printed.

The loop ends with END-FOR. Specifying ticket in the END-FOR is optional, but it makes things clearer when several FOR loops are nested inside each other, for example when you create another loop inside a ticket loop to go through that ticket's tags.

If the END-FOR is missing, the report will fail. It's best to write the END-FOR right after FOR x IN y, and only then decide what should happen with the elements.

Variables & Calculations

You can also create and run your own JavaScript snippets in a report. These aren't printed, but are used to define functions or constants that you can use elsewhere in the report.

It works like this:

{{EXEC
MY_CONSTANT = 3;
}}

Everything between {{EXEC and }} becomes part of the code snippet. You can also use this to perform calculations, a good example is scaling images, covered in the next section.

Inserting Images

Images are objects in the format { width: number; height: number; data: string | ArrayBuffer; extension: string}. width and height specify the width and height of the image. If you change these, the image will be printed in a different size. data contains the image data, and extension specifies the file extension.

You insert images by writing IMAGE inside the curly braces before the variable name, for example {{ IMAGE $ticket.plan.large}} (assuming you previously defined ticket as a specific ticket, see the "For Loops" section). This prints the plan at full size.

If you want the image to be smaller, you can copy the object into a variable beforehand and then adjust plan.large.height and plan.large.width. Here's an example calculation:

{{FOR ticket IN tickets}}
{{IF $ticket.plan.large}} 
{{EXEC 
targetHeight = 7;
scaled = targetHeight / $ticket.plan.large.height;
targetWidth = $ticket.plan.large.width * scaled;
newImage = { width:targetWidth,height:targetHeight,data:$ticket.plan.large.data, extension:$ticket.plan.large.extension }; 
}}
{{IMAGE newImage}} 
{{END-IF}} 
{{END-FOR ticket}}

Combining Elements

You can now combine all these elements to create a report. You can nest multiple IF statements inside each other, nest multiple For loops inside each other, or combine IF and FOR.

It's a good idea to build the report step by step and test it repeatedly along the way. This makes troubleshooting easier, since you can better narrow down where an error might be if you know what's changed since the last test.

The formatting of variables, meaning text color, text size, and font, carries over into the completed report. Now it's up to you to create your own report and format it exactly the way you need it.

Was this article helpful?

No upmesh account yet?

Try upmesh for free: construction documentation, defects, plans and site diary in one app — in the browser, on your phone and offline.