Zum Hauptinhalt springen

Material & Verleih

Vier Bereiche rund um Ausrüstung: Material (der Bestand), Fahrzeuge, Packlisten (was fährt wann mit) und Verleih (wer bekommt was wann).

Alle Pfade sind relativ zu https://<domain>/api/v1.


Material

Bereich material. Drei Begriffe, wie in der Anwendung:

BegriffBedeutung
Exemplarein konkretes Stück mit eigener Material-Nummer
Artikelmehrere gleichartige Exemplare, gruppiert
Seteine benannte Zusammenstellung aus Exemplaren und Artikeln

Kategorien und Lagerorte liegen im selben Bereich: Ein Werkzeug, das Material lesen darf, soll nicht zusätzlich um Kategorien bitten müssen – ohne sie ist die Liste kaum zu deuten.

MethodePfadBereichBeschreibung
GET/materialmaterial:readExemplare (Liste)
POST/materialmaterial:writeExemplar anlegen
GET/material/{id}material:readExemplar lesen
PUT/material/{id}material:writeExemplar ändern
DELETE/material/{id}material:writeExemplar löschen
GET/material/code/{code}material:readExemplar über QR-/Barcode finden
GET/material/artikelmaterial:readArtikel (Liste)
POST/material/artikelmaterial:writeArtikel anlegen
POST/material/artikel/{id}/aufloesenmaterial:writeArtikel auflösen
GET/material/setsmaterial:readSets (Liste)
POST/material/setsmaterial:writeSet anlegen
GET/material/sets/{id}material:readSet samt Positionen
PUT/material/sets/{id}material:writeSet ändern
DELETE/material/sets/{id}material:writeSet löschen
POST/material/sets/{id}/positionenmaterial:writePosition anlegen oder Menge ändern
DELETE/material/sets/{id}/positionen/{positionId}material:writePosition entfernen
GET/material/kategorienmaterial:readKategorien (Liste)
POST/material/kategorienmaterial:writeKategorie anlegen
PUT/material/kategorien/{id}material:writeKategorie ändern
DELETE/material/kategorien/{id}material:writeKategorie löschen
GET/material/lagerortematerial:readLagerorte (Liste)
POST/material/lagerortematerial:writeLagerort anlegen
PUT/material/lagerorte/{id}material:writeLagerort ändern
DELETE/material/lagerorte/{id}material:writeLagerort löschen

Liste filtern

ParameterBedeutung
qVolltextsuche
kategorie_id, lagerort_idKategorie bzw. Lagerort
zustandZustand
statusverfuegbar, reserviert, defekt, reparatur, ausgemustert
fremdtrue = nur fremdes Material
nutzbartrue = nur einsetzbares
archivierttrue = auch archivierte

Sortierbar: title (Standard), material_id, weight_grams, condition, created_at, updated_at, status. Auch diese Liste blättert in der Datenbank.

Felder eines Exemplars

FeldTypAnmerkung
titleTextPflicht
material_idTextdie sichtbare Material-Nummer
category_id, location_idUUIDaus /material/kategorien bzw. /material/lagerorte
content_descriptionTextInhalt eines Behälters
weight_gramsGanzzahl ≥ 0
quantityGanzzahl ≥ 0Stückzahl bei Schüttgut
color_code, dimensions, shelfText
manufacturer, serial_numberText
purchase_date, repair_dateDatum
purchase_value, replacement_valueZahl ≥ 0Anschaffung / Wiederbeschaffung
conditionTextZustand
statusverfuegbar | reserviert | defekt | reparatur | ausgemustert
defect_reasonText
is_external, owner_name, owner_contactfremdes Material
tagsListe von Texten
notesText
is_archivedWahrheitswert
Die Pflichtfelder des Vereins gelten auch hier

Was unter Einstellungen → Pflichtfelder konfiguriert ist, wird beim Anlegen und beim Ändern geprüft (400 bad_request, die Meldung nennt die fehlenden Felder). Sonst könnte ein Werkzeug Datensätze erzeugen, die die Oberfläche anschließend als unvollständig anmeckert.

Artikel und Sets

POST /material/artikel erwartet nur {"name":"…"}. Auflösen entfernt den Artikel, die Exemplare bleiben und stehen danach wieder einzeln – bewusst kein DELETE, weil „Artikel löschen“ nahelegen würde, dass auch das Material weg ist.

Eine Set-Position benennt entweder ein Exemplar oder einen Artikel:

{ "material_id": "<uuid>", "quantity": 2 }
{ "group_id": "<uuid>", "quantity": 4 }

Beim Packen wird aus einem Artikel das konkrete Stück gewählt (Set-Substitution). Ein erneutes POST mit derselben Kennung ändert die Menge.

Kategorien und Lagerorte haben je name (Pflicht), sort_order und is_active.


Fahrzeuge

Bereich fahrzeuge – eigener Bereich und nicht Teil von „Material“: Fahrzeuge haben in der Anwendung eine eigene Rechtelage, und ein Werkzeug, das den Materialbestand abgleicht, hat am Bulli nichts zu suchen.

MethodePfadBereichBeschreibung
GET/fahrzeugefahrzeuge:readListe
POST/fahrzeugefahrzeuge:writeAnlegen
GET/fahrzeuge/{id}fahrzeuge:readEinzeln lesen
PUT/fahrzeuge/{id}fahrzeuge:writeÄndern
DELETE/fahrzeuge/{id}fahrzeuge:writeLöschen

Filter: q (Name, Kennzeichen, Typ), archiviert (true = auch ausgemusterte). Sortierbar: name (Standard), license_plate, seats, max_payload_kg, created_at.

FeldTypAnmerkung
nameTextPflicht
license_plateTextKennzeichen
vehicle_typeText
seatsGanzzahl ≥ 0
max_payload_kgZahl ≥ 0Zuladung
cargo_dimensionsTextLaderaum
notesText
is_archivedWahrheitswertausgemustert

Packlisten

Bereich packlisten. Start- und Enddatum sind Pflicht – das ist keine Formalie: nur mit Zeitraum lässt sich erkennen, ob Material auf zwei Listen gleichzeitig steht, also genau die Prüfung, die eine Packliste nützlich macht.

MethodePfadBereichBeschreibung
GET/packlistenpacklisten:readListe
POST/packlistenpacklisten:writeAnlegen
GET/packlisten/{id}packlisten:readListe samt Positionen und Fahrzeugen
PUT/packlisten/{id}packlisten:writeÄndern
DELETE/packlisten/{id}packlisten:writeIn den Papierkorb
GET/packlisten/{id}/positionenpacklisten:readPositionen (Liste)
POST/packlisten/{id}/positionenpacklisten:writePosition hinzufügen
PUT/packlisten/{id}/positionen/{positionId}packlisten:writePosition ändern
DELETE/packlisten/{id}/positionen/{positionId}packlisten:writePosition entfernen
GET/packlisten/{id}/fahrzeugepacklisten:readFahrzeuge (Liste)
POST/packlisten/{id}/fahrzeugepacklisten:writeFahrzeug zuordnen
DELETE/packlisten/{id}/fahrzeuge/{fahrzeugId}packlisten:writeFahrzeug entfernen

Filter: q, archiviert. Sortierbar: start_date (Standard), name, end_date, status, created_at.

FeldTypAnmerkung
nameTextPflicht
descriptionText
start_date, end_dateJJJJ-MM-TTbeide Pflicht; Ende nicht vor Beginn
statusdraft | planned | packed | done

Position: material_id (Pflicht), quantity (Standard 1), comment. Beim Ändern zusätzlich is_packed. Fahrzeug zuordnen: {"vehicle_id":"<uuid>"}.

Positionen werden nicht auf Verfügbarkeit geprüft

In der Oberfläche ist das Einplanen ein interaktiver Vorgang mit Rückfrage. Über die Schnittstelle soll ein Abgleich eine Liste zusammenstellen können; was dabei doppelt verplant wird, zeigt die Anwendung anschließend an der Liste selbst.

DELETE legt die Liste in den Papierkorb – die Antwort nennt die Frist in papierkorb_tage. Zurückholen geht nur in der Anwendung.


Verleih

Bereich verleih. Die Verfügbarkeitsprüfung kommt aus derselben Quelle wie Oberfläche und öffentliches Formular und ist hier nicht optional: Eine über die Schnittstelle angelegte Ausleihe, die den Bulli ein zweites Mal vergibt, fiele erst am Abfahrtstag auf.

MethodePfadBereichBeschreibung
GET/verleihverleih:readListe
POST/verleihverleih:writeAusleihe anlegen
GET/verleih/{id}verleih:readEinzeln lesen
PUT/verleih/{id}verleih:writeÄndern
DELETE/verleih/{id}verleih:writeLöschen
POST/verleih/{id}/statusverleih:writeStatuswechsel
GET/verleih/belegungverleih:readBelegte Zeiträume für eine Kalenderansicht
GET/verleih/verleihbarverleih:readWas überhaupt verliehen werden darf
POST/verleih/pruefenverleih:readZeitraum prüfen, ohne etwas anzulegen

Filter der Liste: q, art (material oder fahrzeug), status (kommagetrennt), offen (true = nur laufende), ueberfaellig, mitglied_id. Sortierbar: start_date (Standard), end_date, borrower_name, status, created_at.

Felder

FeldTypAnmerkung
kindmaterial | fahrzeugStandard material
borrower_nameTextPflicht
contact_idUUIDwenn die Person im Bestand steht
borrower_email, borrower_phoneText
start_date, end_dateJJJJ-MM-TTPflicht; Ende nicht vor Beginn
purpose, notesText
itemsListemindestens eine Position

Jede Position benennt genau eines von beiden – ein Material oder ein Fahrzeug – plus quantity:

{ "items": [ { "vehicle_id": "<uuid>", "quantity": 1 } ] }

Anlegen und Konflikte

curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"borrower_name":"Maja Beispiel","start_date":"2026-08-14","end_date":"2026-08-16",
"items":[{"vehicle_id":"<uuid>","quantity":1}]}' \
"$BASIS/verleih"

Ist im Zeitraum etwas vergeben, kommt 409 not_available mit den Gründen in details.konflikte. Geprüft wird gegen andere Ausleihen und Packlisten.

Eine über die Schnittstelle angelegte Ausleihe startet direkt auf bestaetigt: Wer einen Schlüssel mit Schreibrecht hat, hat sie damit entschieden. Wer einen Antrags-Workflow will, nutzt das öffentliche Ausleihformular.

Was ein PUT nicht ändert

Status und Protokollfelder (status, source, decided_at, handed_out_at, returned_at …) ändert ausschließlich der Statuswechsel. Mitgeschickte Werte werden verworfen. Werden items mitgeschickt, ersetzen sie die Positionen vollständig – die eigene Buchung bleibt bei der Konfliktprüfung ausgeklammert.

Statusfluss

angefragt ──bestaetigen──> bestaetigt ──ausgeben──> ausgegeben ──zurueck──> zurueck
│ │
└──ablehnen──> abgelehnt └──stornieren──> storniert

POST /verleih/{id}/status:

FeldTypAnmerkung
aktionbestaetigen | ablehnen | ausgeben | zurueck | stornierenPflicht
noteTextwird an die vorhandenen Notizen angehängt
itemsListe aus item_id, returned_ok, damage_noteZustandsprotokoll bei der Rücknahme

Die erlaubten Übergänge sind fest verdrahtet; ein unpassender Wechsel antwortet mit 409 conflict. Beim Bestätigen wird noch einmal auf Konflikte geprüft – zwischen Anfrage und Bestätigung kann jemand anderes gebucht haben.

Verfügbarkeit vorab prüfen

POST /verleih/pruefen legt nichts an und braucht deshalb nur verleih:read:

{
"start_date": "2026-08-14",
"end_date": "2026-08-16",
"items": [ { "vehicle_id": "<uuid>", "quantity": 1 } ],
"exclude_loan_id": null
}

Antwort:

{ "data": { "frei": false, "konflikte": ["Bulli ist vom 14.08. bis 16.08. bereits vergeben."] } }

Damit kann ein Anmeldetool schon im Formular sagen, dass der Zeitraum nicht frei ist. exclude_loan_id klammert eine bestehende Buchung aus – nötig, wenn man einen vorhandenen Zeitraum verschieben will.

GET /verleih/belegung liefert die belegten Zeiträume für eine Kalenderansicht (art, ab), GET /verleih/verleihbar die Freigaben aus den Einstellungen, getrennt nach material und fahrzeuge.

Hat dies deine Frage beantwortet?