Skip to content

document_template

A document template holds the layout and texts of one document type. The reference app uses two records:

Record Id kind
Delivery note template 00000000-0000-4000-8000-000000000002 delivery_note
Loan template (handover and return protocols) 00000000-0000-4000-8000-000000000003 loan
  • Mutable: the version with the greater hlc wins, unless the stored version is locked (see locked).
  • Every field is optional. A client that finds no record, or a record without a field, uses the defaults below.
  • Never deleted.
Field Type Default Meaning
kind string? delivery_note delivery_note or loan. The reference app identifies templates by id and writes kind for information.
paper string? a4 a4 or letter
language string? device language de or en: language of the printed document. Without it, German when the device language is German, otherwise English. Language and law are separate: see jurisdiction.
show_logo boolean? true Print the company logo
Field Type Default Meaning
title string? "Lieferschein" / "Delivery Note" Printed before the number, as in "Delivery Note LS-2026-3F-0007". Placeholders are not replaced in the title.
intro string? "Wir liefern Ihnen folgende Positionen:" / "We are delivering the following items:" Text above the items
closing string? none Text below the items and the note
footer string? none First footer line on every page, above the company's legal details
columns array of string? position, sku, name, quantity, unit, location Table columns in order. Values: position, sku, name, quantity, unit, location, serials, barcode. Readers ignore unknown values. serials is not a column: it prints the serial numbers below the name. barcode prints items[].barcode.
signatures.recipient boolean? true Print a signature field for the recipient
signatures.issuer boolean? true Print a signature field for the issuer
signatures.recipient_label string? "Ware erhalten" / "Goods received" Caption of the recipient's field
signatures.issuer_label string? "Ausgegeben" / "Issued" Caption of the issuer's field
show_company boolean? true Print the company name and address in the header
numbering.mode string? device device or server, see Number ranges
numbering.format string? LS-{YYYY}-{DEV}-{SEQ:4} Device numbering pattern
numbering.next integer? Reserved; not read or written by the reference app

intro, closing and footer may contain placeholders, replaced when the document is rendered:

Placeholder Replaced by
{number} The note's number
{date} issued_at as a short numeric date in the document language
{recipient} recipient.name of the note

The loan template configures the handover and return protocols. Of the common fields it uses language, paper and show_logo.

Field Type Default Meaning
terms string? standard text for the jurisdiction Conditions for external borrowers (customers and sites). {company} is replaced by the company name. Empty means the standard text.
employee_terms string? standard text for the jurisdiction Conditions for employees. {company} as above.
jurisdiction string? derived Legal system the standard conditions follow: de, at, ch or intl
penalty_enabled boolean? false External loans with a due date carry a contractual penalty for late return
penalty object? suggested scale The penalty scale, same shape as loan penalty
locked boolean? false Set by the organisation on its server; see below

When a loan is created, the device builds the conditions the borrower signs and freezes them in the loan's terms:

  1. Take employee_terms for employees, terms for customers and sites. If that is empty, take the app's standard text for the jurisdiction and kind of borrower, in the template's language.
  2. Replace {company} with the company profile's name ("dem Unternehmen" / "the company" when it is empty).
  3. If a penalty applies (below), append a blank line and a penalty clause generated from the scale, so that text and numbers always agree. For example (en, EUR): "... €5.00 per calendar day from day 3 after the return date and €10.00 per calendar day from day 15 after the return date ..." followed by the cap.

Changing the template later does not change existing loans.

The standard conditions are written for German, Austrian and Swiss law, and in a neutral form (intl) for other countries. Without jurisdiction, the reference app derives it from the company profile's country:

country (trimmed, case-insensitive) Jurisdiction
deutschland, germany, de, d de
österreich, oesterreich, austria, at, a at
schweiz, switzerland, suisse, svizzera, ch ch
any other non-empty value intl
empty from the device region: DE, AT, CH, else intl

A new loan carries the penalty scale in its penalty (and the clause in its terms) only if all of these hold:

  • the borrower is a customer or site, not an employee,
  • the loan has a due date,
  • penalty_enabled is true,
  • the jurisdiction is not intl,
  • the scale has at least one tier with a rate above 0,
  • the reference app additionally requires the Team plan.

When penalty is absent but penalty_enabled is true, the suggested scale applies: 5 per day from the 3rd late day, 10 per day from the 15th, at most 30 percent of the total replacement value:

{ "tiers": [{ "after_days": 2, "per_day": 5 }, { "after_days": 14, "per_day": 10 }], "cap_amount": null, "cap_percent": 30 }

An organisation that runs its own server can set the handover conditions for all devices and lock them. The reference server does this with php artisan lve:loan-terms --lock (see Reference server), which writes the loan template as a server-side change with locked: true.

  • Clients show a locked template read-only and do not write it. The reference app silently skips saving a locked loan template.
  • Servers reject every pushed change to a template whose stored version is locked, with reason locked. Only the server can change or unlock it.
  • Other fields of the template stay as they were; the command changes only the conditions, the jurisdiction and the lock.
Field Rules
kind nullable, one of delivery_note, loan
terms, employee_terms nullable, string
penalty_enabled, locked, show_logo, show_company nullable, boolean
jurisdiction nullable, one of de, at, ch, intl
penalty nullable, object; penalty.tiers required when penalty is present, a list; each tier after_days (required, integer, at least 0) and per_day (required, numeric, at least 0); penalty.cap_amount nullable, numeric, at least 0; penalty.cap_percent nullable, numeric, 0 to 100
paper nullable, one of a4, letter
language nullable, one of de, en
columns nullable, list; each element one of position, sku, name, quantity, unit, location, serials, barcode
signatures nullable, object; recipient and issuer nullable, boolean
numbering nullable, object; mode nullable, one of device, server; next nullable, integer

In addition, the server rejects every change to a locked template with locked.

Note that the server rejects unknown columns values, while readers ignore them. A newer app that adds a column value needs a server that knows it.

Delivery note template:

{
"entity": "document_template",
"id": "00000000-0000-4000-8000-000000000002",
"op": "upsert",
"hlc": "001791792000123-0006-3F2A9C1B",
"data": {
"kind": "delivery_note",
"title": "Delivery note",
"intro": "We deliver to {recipient} as agreed:",
"closing": "Goods received in good order.",
"footer": "Delivery note {number} of {date}",
"paper": "a4",
"language": "en",
"columns": ["position", "sku", "name", "quantity", "unit", "serials"],
"signatures": { "recipient": true, "issuer": true, "recipient_label": "Received by", "issuer_label": "Issued by" },
"numbering": { "mode": "server", "format": "LS-{YYYY}-{DEV}-{SEQ:4}" },
"show_logo": true,
"show_company": true
}
}

Loan template set and locked by the server (node 00000000):

{
"entity": "document_template",
"id": "00000000-0000-4000-8000-000000000003",
"op": "upsert",
"hlc": "001791810000000-0000-00000000",
"data": {
"kind": "loan",
"language": "de",
"terms": "{company} lends the equipment listed above ...",
"employee_terms": null,
"penalty_enabled": true,
"penalty": { "tiers": [{ "after_days": 2, "per_day": 5 }, { "after_days": 14, "per_day": 10 }], "cap_amount": 500, "cap_percent": 30 },
"jurisdiction": "de",
"locked": true
}
}
schemas/document_template.json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Document template",
"description": "Layout and texts of a document type. The reference app uses two records with fixed ids: 00000000-0000-4000-8000-000000000002 for delivery notes and 00000000-0000-4000-8000-000000000003 for loan handover and return protocols. Mutable: the version with the greater hlc wins, unless the stored version is locked (see locked). Every field is optional; the defaults below apply when a field is absent or null.",
"type": "object",
"properties": {
"kind": { "description": "Document type. Absent or null means delivery_note.", "type": ["string", "null"], "enum": ["delivery_note", "loan", null], "default": "delivery_note" },
"title": { "description": "Delivery notes: document title printed before the number. Absent or empty: \"Lieferschein\" or \"Delivery Note\" in the document language. Placeholders are not replaced in the title.", "type": ["string", "null"] },
"intro": { "description": "Delivery notes: text above the items. May contain {number}, {date} and {recipient}. Absent: a standard sentence in the document language.", "type": ["string", "null"] },
"closing": { "description": "Delivery notes: text below the items. May contain {number}, {date} and {recipient}.", "type": ["string", "null"] },
"footer": { "description": "Delivery notes: first footer line on every page. May contain {number}, {date} and {recipient}.", "type": ["string", "null"] },
"paper": { "description": "Paper size. Absent or null means a4.", "type": ["string", "null"], "enum": ["a4", "letter", null], "default": "a4" },
"language": { "description": "Language of the printed documents. Absent or null: German when the device language is German, otherwise English.", "type": ["string", "null"], "enum": ["de", "en", null] },
"columns": {
"description": "Delivery notes: ordered list of the table columns. Absent or null means position, sku, name, quantity, unit, location. Unknown names are ignored. serials is not a column of its own: it prints the serial numbers below the name.",
"type": ["array", "null"],
"items": { "type": "string", "enum": ["position", "sku", "name", "quantity", "unit", "location", "serials", "barcode"] },
"default": ["position", "sku", "name", "quantity", "unit", "location"]
},
"signatures": {
"description": "Delivery notes: which signature fields are printed, and their captions.",
"type": ["object", "null"],
"properties": {
"recipient": { "description": "Print a signature field for the recipient. Absent or null means true.", "type": ["boolean", "null"], "default": true },
"issuer": { "description": "Print a signature field for the issuer. Absent or null means true.", "type": ["boolean", "null"], "default": true },
"recipient_label": { "description": "Caption of the recipient's field; absent or empty uses the standard caption.", "type": ["string", "null"] },
"issuer_label": { "description": "Caption of the issuer's field; absent or empty uses the standard caption.", "type": ["string", "null"] }
},
"additionalProperties": true
},
"numbering": {
"description": "Delivery notes: how numbers are assigned.",
"type": ["object", "null"],
"properties": {
"mode": { "description": "device: each device formats numbers itself. server: numbers come from the server's number range, reserved in blocks; when none are left the device format is used. Absent or null means device.", "type": ["string", "null"], "enum": ["device", "server", null], "default": "device" },
"format": {
"description": "Device numbering pattern. Placeholders: {YYYY}, {YY}, {MM}, {DEV} (device code), {SEQ} or {SEQ:n} (counter padded to n digits). Absent or null means LS-{YYYY}-{DEV}-{SEQ:4}.",
"type": ["string", "null"],
"default": "LS-{YYYY}-{DEV}-{SEQ:4}",
"examples": ["LS-{YYYY}-{DEV}-{SEQ:4}", "LS-{YY}{MM}-{DEV}{SEQ:3}"]
},
"next": { "description": "Reserved. The reference app keeps the device counter on each device and neither reads nor writes this field; it is relayed unchanged.", "type": ["integer", "null"] }
},
"additionalProperties": true
},
"show_logo": { "description": "Print the company logo. Absent or null means true.", "type": ["boolean", "null"], "default": true },
"show_company": { "description": "Delivery notes: print the company name and address in the header. Absent or null means true.", "type": ["boolean", "null"], "default": true },
"terms": { "description": "Loan templates: conditions for external borrowers (customers and sites); {company} is replaced by the company name. Absent or empty: the app's standard text for the jurisdiction.", "type": ["string", "null"] },
"employee_terms": { "description": "Loan templates: conditions for employees; {company} is replaced by the company name. Absent or empty: the app's standard text for the jurisdiction.", "type": ["string", "null"] },
"penalty_enabled": { "description": "Loan templates: external loans with a due date carry a contractual penalty for late return. Absent or null means false.", "type": ["boolean", "null"] },
"penalty": { "description": "Loan templates: the penalty scale. Absent or null while penalty_enabled is true: 5 per day from the third late day, 10 per day from the fifteenth, at most 30 percent of the replacement value.", "$ref": "loan.json#/$defs/penalty" },
"jurisdiction": { "description": "Loan templates: legal system the standard conditions follow. intl means neutral conditions for other countries, without penalty. Absent or null: derived from company_profile.country, else from the device region.", "type": ["string", "null"], "enum": ["de", "at", "ch", "intl", null] },
"locked": { "description": "Set by the organisation on its server. Clients show the template read-only, and servers reject changes pushed by devices with reason locked. Absent or null means false.", "type": ["boolean", "null"] }
},
"additionalProperties": true,
"examples": [
{
"kind": "delivery_note",
"title": "Delivery note",
"intro": "We deliver to {recipient} as agreed:",
"closing": "Goods received in good order.",
"footer": "Delivery note {number} of {date}",
"paper": "a4",
"language": "en",
"columns": ["position", "sku", "name", "quantity", "unit", "serials"],
"signatures": { "recipient": true, "issuer": true, "recipient_label": "Received by", "issuer_label": "Issued by" },
"numbering": { "mode": "server", "format": "LS-{YYYY}-{DEV}-{SEQ:4}" },
"show_logo": true,
"show_company": true
},
{
"kind": "loan",
"language": "de",
"paper": "a4",
"terms": "{company} lends the equipment listed above ...",
"employee_terms": null,
"penalty_enabled": true,
"penalty": { "tiers": [{ "after_days": 2, "per_day": 5 }, { "after_days": 14, "per_day": 10 }], "cap_amount": 500, "cap_percent": 30 },
"jurisdiction": "de",
"locked": true
}
]
}