PDF Letterhead Templates¶
Manages the PDF templates calServer places underneath a report as stationery — Administration > Report management > PDF templates.
This page described something else until 2026-08
It listed "built-in reports" (standard calibration certificate, device
overview, annual summary …), a variable mapping and an endpoint
POST /api/v2/reports/generate. None of that exists. calServer ships
no report templates, and that is deliberate — see
Where report templates come from.
What the menu entry actually manages is described here.
What a PDF template is¶
An uploaded PDF carrying the company layout — letterhead, footer, frame —
which ReportOverlayService places underneath the report pages when
rendering. The report itself remains the JasperReports template; the PDF only
supplies the paper.
It is stored in master_report_template, the file itself lives versioned in
the DMS. Each template carries:
| Field | Meaning |
|---|---|
template_name |
display name in the picker |
total_page |
page count of the uploaded PDF |
first_page |
which PDF page goes underneath the first report page |
other_page |
which one goes underneath all following pages |
sort_order |
order in the picker |
The first_page / other_page split is the entire point of the construction:
stationery typically carries a full letterhead on page 1 and a leaner
continuation sheet after that.
Assigning it to a report¶
In the report under Stationery:
| Setting | use_template |
Effect |
|---|---|---|
| No letterhead | null |
the report renders without any underlay |
| Global letterhead | html |
the centrally maintained details (address, phone, accreditation number) are rendered onto every page |
| PDF template | pdf |
the PDF managed here is placed underneath (template_uID) |
Recommendation: the global letterhead. It is maintained in one place and applies to every report; a PDF template has to be regenerated and re-uploaded whenever the address or accreditation number changes. The reasoning is in ADR-037.
PDF templates stay the right choice when the paper carries more than text — watermarks, perforation marks, a designed layout that cannot be derived from a header and a footer.
Replacing a template¶
New stationery replaces the file of the existing template — row menu Datei
austauschen, POST /admin/report-templates/{id}/file. The template keeps its
uID, every template_uID in the report settings stays valid, and the new PDF
becomes the next DMS version.
Uploading through PDF-Vorlage hochladen (POST /admin/report-templates)
creates a second template instead — and the old one cannot be deleted while a
report still uses it as stationery (409, naming the reports). That is the wrong
route for an exchange.
If the new PDF has fewer pages than the old one, first_page and other_page
are clamped to the last page that exists rather than pointing past the end.
Versioning¶
Every upload creates a new DMS version; the previous one stays retrievable:
GET /admin/report-templates/{id}/versions— version listPOST /admin/report-templates/{id}/versions/{versionId}/activate— re-activate an older versionGET /admin/report-templates/{id}/versions/{versionId}/download— download a version
This is the same mechanism the report packages themselves use. An accidental upload is therefore one click back, not a restore case.
Where report templates come from¶
The actual JasperReports templates (.jrxml) are not part of the
application. They are maintained, versioned and reviewed in
calhelp/calServer-reports and
published as packages at
calhelp.github.io/calServer-reports/downloads
— see Downloading report packages.
This is a deliberate decision, not a gap: a template shipped inside the
application image would fork the copy maintained over there. That is why
calServer creates a row for each of the three
system reports but leaves it empty, naming the
package that belongs in it right in the report management
(LOCATION-JSON-SAMPLE, PRICE-LIST-JSON-SAMPLE, ORDER-JSON-SAMPLE).
New templates should use the JSON data source instead of embedded SQL: Authoring V2 reports with a JSON data source.
Generating a report¶
Reports are generated at their entity, not through a central generation endpoint:
GET /api/v2/calibrations/{ctag}/reports # available reports
POST /api/v2/calibrations/{ctag}/reports/generate # generate
GET /api/v2/inventories/{mtag}/reports
POST /api/v2/inventories/{mtag}/reports/generate
GET /api/v2/bookings/{bookingId}/reports
POST /api/v2/bookings/{bookingId}/reports/generate
Rendering happens in the report-runner container. Its state:
docker ps --filter name=report-runner --format '{{.Names}}\t{{.Status}}'
docker logs report-runner --tail 50
If it is not running, every report fails with "Report runner unavailable".