Create a Report
Goal
Turn a designed template into a report users can run: printed for one record ("print this invoice") or over a whole list, from the command bar, in the app they already have open.
What a report is made of
Two halves, and they meet at a name.
- The template — the page: its bands, its tables, its logo, its watermark. Designed in the graphic designer and exported as JSON, which you paste in here.
- The datasets — the rows that fill it. Each names a saved query or carries a query of its own, under a name the template refers to.
A report carries no data of its own. Everything it prints is a query, run when somebody presses Print, under their permissions. Two people printing the same report see the records each of them is allowed to see — a report is a layout over your data, never a way around who may read it.
Prerequisites
- You are signed in as a maker, with a solution and an environment selected.
- The saved queries your report will use already exist (or you will write the query inline).
- You have a template exported from the designer.
Steps
- Open your solution and choose Reports.
- Select New, and give it a display name — the logical name fills itself in.
- Fill in the datasets. For each one:
- Name — what the template calls these rows. The template's repeater names it, so the two must match exactly.
- Saved query — the question that fills it, chosen from the environment's saved queries rather than typed. Each option says which table the query reads, because a display name rarely distinguishes two of them. Write the question first — see Save a Query.
- Record field — optional; see below.
- Paste the template JSON.
- Select Save. The platform checks the report is well formed, that each query is a valid query, and that each one would actually run here — every field, join and measure against this environment's real tables. A misspelled field is refused now, rather than discovered by whoever presses Print.
The first dataset is the primary one. Its table is what the report is about: it decides which records and which lists offer the report on their command bar, and it is the one a list's current filter is handed to.
Printing one record
Set the record field on a dataset and the report can be printed for a single record.
- On the primary dataset it is usually
id— "this invoice". - On a child dataset it is the lookup back to the parent —
par_invoiceidon the lines, so printing invoice 42 prints invoice 42's lines and no others. - Leave it empty and the dataset is unaffected by which record you printed from. That is how one report can print a single invoice beside a table of this month's exchange rates.
Running one
A report is reached deliberately — nothing puts a button on a table just because a report names it. Three ways in:
- From the app's navigation, if a maker placed the report there. It opens over the whole table.
- From a command a maker drew on a form or a view, with Run a report as its behaviour. Pressed on an open record it prints that record; pressed on a list it is over the whole table. See Make a Button Do Something.
- From a dashboard widget that names it.
Either way the report opens as its own screen, with the app's navigation still beside it and its address in the address bar — so a report you are looking at can be refreshed, bookmarked or sent to a colleague. A report a command opened carries a ← button at the start of the viewer's toolbar, which goes back to the screen the button was pressed on.
A list's search and filter do not travel with it. A report is addressed by its own name, and a filter is not something an address can carry; to report on a narrower set of records, give the filter to the report.
Reading a report on screen
The viewer opens on one page at a time, at a zoom that fits a whole page, with Print at the end. Four controls change what you are looking at:
- Pages — One page with a pager (how you read a report) or All pages, scrolling (how you check one before printing it). In All pages the pager goes, because it would be a second answer to where you are.
- Zoom — Fit page, Fit width, or Actual size. Fit means fit, in both directions: a page narrower than your window is enlarged to fill it, because that is what the command is named for. Actual size is the answer when you want 1:1 instead.
- Fullscreen — fills the window with the report. Esc leaves it. The report stays inside the app, so messages and the rest of the product still work while it is open.
- Print.
Printing produces every page at full size, not the one on screen and not at the zoom you were reading it at, and none of the viewer's own buttons. The paper is the size the report was designed for — an A4 report prints on A4, edge to edge, with no browser header, footer, URL or page number in the margin, because the sheet has no margin for them to print in.
If you have switched headers and footers on in your browser's own print dialog, you will still get them — that setting is yours, and the platform does not override it. Turn "Headers and footers" off there.
A report is text, not a picture of one
You can select any text in a report and copy it, and your browser's Find (Ctrl+F / ⌘F) searches the page you are on. A screen reader reads the report aloud rather than announcing an image. None of this is a setting: a report page is ordinary text on the page, which is also what makes Persian and Arabic lines render correctly when they contain a Latin product code or a price — the browser orders the mixed run itself.
Find searches the pages that are on screen. In One page that is the page you are reading; switch to All pages to search the whole report at once.
Dates, numbers and digits
A report prints dates in the same calendar and pattern as the grid beside it, and numbers with the same digits — Persian digits and Jalali dates for a Persian tenant. You do not configure this per report: it comes from Settings → Calendar and the tenant's locale, so a report cannot drift away from the rest of the product.
Moving it between environments
A report is a solution component, filed under the table it is about. Add it to your solution from that table's Manage components list and it exports and imports with everything else — including its template, which travels inside the report rather than beside it.
Its saved queries travel too, and are imported before it: a report is refused if it names a query the target environment does not have, which is the honest failure rather than a report that imports and then prints nothing.
Troubleshooting
- "The template is not valid JSON." — the paste is truncated or has a stray character. The box does not reformat what you type, so what you see is what was sent.
- "A report needs at least one dataset." — a dataset row needs both a name and a saved query to count; a half-filled row is ignored.
- "Dataset 'rows' must name exactly one of 'savedQuery' or 'query'." — you have given both, or neither.
- "…names the saved query 'x', which does not exist in this environment." — check the spelling, or add the query to the solution so it travels.
- "Unknown field …" — the query names a field that is not on its table. Fix the query, not the report.
- The report prints but the page is empty. — the template's repeater names a dataset that the report does not define. The names have to match exactly.
- "This report's template could not be laid out." — the engine could not make pages of it. Re-export from the designer; a template edited by hand is easy to break.
- No report command appears on the record. — nothing generates one. Draw a button and give it Run a report, or place the report in the app's navigation.
- The report opened but shows every record. — it was run from a list, or from a record that has never been saved. Only a saved, open record narrows a report.