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
hlcwins, 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.
Common fields
Section titled “Common fields”| 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 |
Delivery note fields
Section titled “Delivery note fields”| 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 |
Loan fields
Section titled “Loan fields”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 |
Conditions on a loan
Section titled “Conditions on a loan”When a loan is created, the device builds the conditions the borrower signs and freezes them in the loan's terms:
- Take
employee_termsfor employees,termsfor 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. - Replace
{company}with the company profile'sname("dem Unternehmen" / "the company" when it is empty). - 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.
Jurisdiction
Section titled “Jurisdiction”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 |
When a penalty applies
Section titled “When a penalty applies”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_enabledistrue,- 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 }locked
Section titled “locked”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.
Server validation
Section titled “Server validation”| 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.
Examples
Section titled “Examples”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 }}Schema
Section titled “Schema”{ "$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 } ]}