Loans
A loan hands equipment to a borrower who brings it back later: an employee, a customer or a construction site. This page follows one loan from handover to closing. The same data is in test-vectors/loan-state.json, so an implementation can check its derived values against it.
The example. On 12 October 2026 the Northring construction site borrows:
| Position | Article | Quantity | Serials | Replacement value | Accessories |
|---|---|---|---|---|---|
| 1 | Cordless drill 18 V | 1 | DR-1001 |
189.90 | Charger, Battery 4 Ah, Case |
| 2 | Cable drum 25 m | 3 | 40.00 |
Due on 19 October (due_at 2026-10-18T22:00:00Z, Berlin). The loan template has the penalty switched on with the suggested scale.
Handover
Section titled “Handover”sequenceDiagram
autonumber
actor B as Borrower
actor I as Issuer
participant D as Device
participant S as Server
I->>D: pick borrower, scan items, values, condition, photos, accessories, due date
Note over D: check stock line by line,<br/>customers and sites need a due date
D->>B: show conditions (terms + penalty clause)
B->>D: signs (required)
I->>D: signs (optional)
Note over D: number UE-2026-3F-0001 (device counter)
loop each item
Note over D: write movement issue,<br/>loan_id, note = number
end
Note over D: store photos, signatures, handover PDF
Note over D: write loan (items, terms, penalty, signatures, pdf)
D->>S: PUT attachments, push [movements..., loan]
S-->>D: applied
- Checks. At least one item; a due date for customers and sites; the borrower's signature; every line passes the stock checks of an issue against the stock left by the lines before.
- Number. From the device's loan counter:
UE-{YYYY}-{DEV}-{SEQ:4}. - Penalty. For an external borrower with a due date, the template's scale if a penalty applies, else none.
- Conditions. The template's text for the kind of borrower (or the standard text for the jurisdiction),
{company}replaced, the penalty clause appended. Frozen interms. - Movements. One
issueper item withloan_id, the loan number innote, the item's serials. - Loan record with copies of the borrower, every item with
movement_id, the signatures,terms,penaltyand the handover protocolpdf.
Records written (shortened to the fields that matter here):
[ { "entity": "movement", "id": "A3B5C7D9-2E4F-4061-8A3C-5E7F9B1D3F52", "op": "upsert", "hlc": "001791802800000-0000-3F2A9C1B", "data": { "type": "issue", "article_id": "4A7C2E91-0B3D-4F6A-8E15-7C9B2D4F6A80", "from_location_id": "2B0E6C1A-7D44-4F1B-9C3E-5A8D2F6B1C07", "quantity": 1, "serials": ["DR-1001"], "note": "UE-2026-3F-0001", "loan_id": "E2C4A6B8-1D3F-4A5C-9E7B-0F2D4B6A8C91", "booked_at": "2026-10-12T11:00:00Z", "booked_by": "Erika Mustermann" } }, { "entity": "movement", "id": "B4C6D8E0-3F5A-4172-9B4D-6F8A0C2E4A63", "op": "upsert", "hlc": "001791802800000-0001-3F2A9C1B", "data": { "type": "issue", "article_id": "0E2A4C6E-8B1D-4F3A-A5C7-9E1B3D5F7A24", "from_location_id": "2B0E6C1A-7D44-4F1B-9C3E-5A8D2F6B1C07", "quantity": 3, "note": "UE-2026-3F-0001", "loan_id": "E2C4A6B8-1D3F-4A5C-9E7B-0F2D4B6A8C91", "booked_at": "2026-10-12T11:00:00Z", "booked_by": "Erika Mustermann" } }, { "entity": "loan", "id": "E2C4A6B8-1D3F-4A5C-9E7B-0F2D4B6A8C91", "op": "upsert", "hlc": "001791802800000-0002-3F2A9C1B", "data": { "number": "UE-2026-3F-0001", "borrower_id": "5E2A8C14-9D3B-4A7F-B6C0-1E4D7F2A9B35", "borrower": { "kind": "site", "name": "Northring construction site", "city": "Berlin" }, "borrower_kind": "site", "issued_at": "2026-10-12T11:00:00Z", "issued_by": "Erika Mustermann", "due_at": "2026-10-18T22:00:00Z", "items": [ { "position": 1, "article_id": "4A7C2E91-0B3D-4F6A-8E15-7C9B2D4F6A80", "sku": "200310", "name": "Cordless drill 18 V", "unit": "Stk", "quantity": 1, "serials": ["DR-1001"], "from_location_id": "2B0E6C1A-7D44-4F1B-9C3E-5A8D2F6B1C07", "location_code": "N-A01", "replacement_value": 189.9, "condition": "new", "accessories": ["Charger", "Battery 4 Ah", "Case"], "movement_id": "A3B5C7D9-2E4F-4061-8A3C-5E7F9B1D3F52" }, { "position": 2, "article_id": "0E2A4C6E-8B1D-4F3A-A5C7-9E1B3D5F7A24", "sku": "300120", "name": "Cable drum 25 m", "unit": "Stk", "quantity": 3, "serials": [], "from_location_id": "2B0E6C1A-7D44-4F1B-9C3E-5A8D2F6B1C07", "location_code": "N-A01", "replacement_value": 40, "accessories": [], "movement_id": "B4C6D8E0-3F5A-4172-9B4D-6F8A0C2E4A63" } ], "signatures": [ { "role": "recipient", "name": "Max Mustermann", "signed_at": "2026-10-12T11:02:00Z", "image": "acc6bcb1913432eed9c60781607713d63c802ff23893d35b88b1903ed04c2f50" } ], "returns": [], "terms": "... conditions as signed, with the penalty clause ...", "penalty": { "tiers": [{ "after_days": 2, "per_day": 5 }, { "after_days": 14, "per_day": 10 }], "cap_amount": null, "cap_percent": 30 }, "pdf": "28b405d5ad469ff43888136a56c515bc0762856c8052bd09830433634d8997c0" } }]State: open, nothing returned; total value 189.90 + 3 × 40.00 = 309.90. Stock at N-A01: drill −1, cable drums −3. DR-1001 is issued and shown as on loan to the Northring site, due 19 October.
Partial return
Section titled “Partial return”On 20 October at 09:30 (Berlin), one day late, the site brings back the drill (damaged) with the charger and the case, and two of the three cable drums.
sequenceDiagram
autonumber
actor B as Borrower
actor I as Receiver
participant D as Device
participant S as Server
I->>D: select lines: quantity, serials, condition, location, photos, accessories
Note over D: check against what is outstanding
B->>D: signs (required)
I->>D: signs (required)
loop each line with quantity > 0 and condition ok or damaged
Note over D: write movement receipt,<br/>loan_id, to_location_id, serials
end
Note over D: append return (lines, signatures, return PDF)<br/>write loan version n+1
D->>S: PUT attachments, push [movements..., loan]
S-->>D: applied
Nothing is booked until both signatures are there. Then:
- a
receiptmovement for each line that brings units back (okordamaged), withloan_id, the loan number innote,to_location_idand the returned serials, - a new version of the loan with one more entry in
returns.
The return appended to the loan:
{ "id": "F1A3C5E7-9B2D-4F46-8A0C-2E4B6D8F0A17", "returned_at": "2026-10-20T07:30:00Z", "received_by": "Erika Mustermann", "items": [ { "position": 1, "quantity": 1, "serials": ["DR-1001"], "condition": "damaged", "note": "Chuck wobbles", "photos": ["84358bf14527752ea15b31417c718a0e6a7a92ad904b2e1f856574695b9bb611"], "accessories": ["charger", "Case"], "lost_accessories": [], "to_location_id": "2B0E6C1A-7D44-4F1B-9C3E-5A8D2F6B1C07", "movement_id": "C7E9A1B3-5D7F-4291-B3D5-7F9A1C3E5B64" }, { "position": 2, "quantity": 2, "serials": [], "condition": "ok", "photos": [], "accessories": [], "lost_accessories": [], "to_location_id": "2B0E6C1A-7D44-4F1B-9C3E-5A8D2F6B1C07", "movement_id": "D8FA0B2C-6E8A-43A2-8C4E-0A2C4E6A8B75" } ], "signatures": [ { "role": "recipient", "name": "Max Mustermann", "signed_at": "2026-10-20T07:31:00Z", "image": "f4ea7a3453ab1b8fdeeaf7ee8d9fd4104e43956b95855a61c1ef9f3e180d6f6d" }, { "role": "issuer", "name": "Erika Mustermann", "signed_at": "2026-10-20T07:31:30Z", "image": "be7d9df48d637d1f1e1684b07569c572999cc50eca2862a73f7dc25798f2b082" } ], "pdf": "e65034da1a35513b1e77bb281f4bcb7fbc39abd8712d54ddfea59aaa974a2db8"}Accessory names are compared case-insensitively, so charger closes "Charger".
State after the return (20 October, 10:00 Berlin):
| Position | Outstanding quantity | Outstanding serials | Outstanding accessories |
|---|---|---|---|
| 1 | 0 | none | Battery 4 Ah |
| 2 | 1 | none | none |
The loan is overdue (1 overdue day). The penalty after 1 day is 0 (2 days of grace). On 25 October, still with these items out, it is 6 overdue days and 4 × 5.00 = 20.00. The return protocol states that the return was 1 day late; it shows a penalty only when the amount for those days is above 0.
Accessory-only return and items reported lost
Section titled “Accessory-only return and items reported lost”On 25 October at 11:00 the site reports that the battery and the third cable drum were lost on the site. A return line may have quantity 0 and carry only accessories:
{ "id": "0B2D4F61-8A3C-4E5F-9B7D-1C3E5A7B9D02", "returned_at": "2026-10-25T10:00:00Z", "received_by": "Erika Mustermann", "items": [ { "position": 1, "quantity": 0, "serials": [], "condition": "ok", "photos": [], "accessories": [], "lost_accessories": ["Battery 4 Ah"] }, { "position": 2, "quantity": 1, "serials": [], "condition": "lost", "note": "Not found on site", "photos": [], "accessories": [], "lost_accessories": [] } ], "signatures": [ { "role": "recipient", "name": "Max Mustermann", "signed_at": "2026-10-25T10:01:00Z", "image": "9d1c3e5a7b9d1f3a5c7e9b1d3f5a7c9e1b3d5f7a9c1e3b5d7f9a1c3e5b7d9f1a" }, { "role": "issuer", "name": "Erika Mustermann", "signed_at": "2026-10-25T10:01:30Z", "image": "7b9d1f3a5c7e9b1d3f5a7c9e1b3d5f7a9c1e3b5d7f9a1c3e5b7d9f1a3c5e7b9d" } ]}- No movement is written: a lost unit does not come back into stock, and accessories are not stock. Stock of cable drums at
N-A01stays 1 lower than before the loan; that is the loss. - The return protocol lists the lost drum with its replacement value (40.00) and states that the usual replacement value applies to the battery, which has no stated value.
An item reported lost as a whole writes off its accessories with it: had the drill itself been reported lost (quantity: 1, condition: lost), the charger, battery and case would no longer be outstanding without listing them.
Closing
Section titled “Closing”After the second return nothing is outstanding: quantities 0, no serials, no accessories. The loan is closed. There is no separate "close" record; status is derived. A closed loan has no accrued penalty.
| Step | Status | Overdue days | Accrued penalty |
|---|---|---|---|
| 19 Oct 23:59 Berlin, before any return | open | 0 | none |
| 20 Oct 10:00, after the partial return | overdue | 1 | none (0) |
| 25 Oct 10:00, before the second return | overdue | 6 | 20.00 |
| 25 Oct 11:05, after the second return | closed | 6 | none |
Employees
Section titled “Employees”Loans to employees work the same way with these differences: the due date is optional, employee_terms (or the standard employee conditions) apply, and no penalty is ever agreed. Without a due date the loan is never overdue.
Concurrency
Section titled “Concurrency”All returns of a loan are entries in one record. If two devices record returns of the same loan offline, both write a new version with their own return appended; the later version wins and the other return disappears from the loan, while its receipt movements stay (movements are never lost). The device whose return was lost sees the winning version on its next pull. To avoid this, a loan should be returned on one device at a time; a client MAY warn when it holds an unsent return of a loan for which it pulls a newer version.