Systemdokumentation
Wie MatManager funktioniert
MatManager ist eine Webanwendung zur Materialverwaltung und Ausleihplanung für Jugendorganisationen. Diese Seite dokumentiert die Architektur, den Technologie-Stack, die zentralen Algorithmen und alle Features des Systems.
Technologie-Stack
Cloudflare R2
VercelWeitere Libraries: FullCalendar, Recharts, SheetJS (xlsx), Sharp, Lucide Icons, bcryptjs, qrcode.react
Multi-Tenant Architektur
Jede Cevi-Abteilung arbeitet in einem isolierten Mandanten. Benutzer, Materialien, Lagerorte und Bestellungen sind vollständig durch die Department-Zuordnung getrennt. Abteilungen können eigene Materialkategorien als JSON-Array definieren und individuelle Vorlaufzeiten (leadTimeWarningHours,leadTimeBlockHours) sowie Pufferzeiten für Aufbereitung (prepTimeHours) und Wiedereinlagerung (restockTimeHours) konfigurieren -- jeweils separat für interne und externe Anfragen.
Innerhalb einer Abteilung organisieren sich Mitglieder in Gruppen. Administratoren erstellen Gruppen, weisen Rollen zu und verwalten Beitrittsanfragen. Das Rollensystem unterscheidet vier Stufen: USER,GRUPPENLEITER, ADMIN und SUPERADMIN.
Inventarsystem: Material & Exemplare
Das Inventar trennt konsequent zwischen dem Materialtyp und dem physischen Exemplar. Ein Material beschreibt die Gattung (z.B. "Blachenzelt 4x4m") mit Stammdaten wie Gewicht, Abmessungen, Kategorie, Lieferant, Kaufpreis, Garantiedatum und bis zu drei Fotos. Jedes einzelne Objekt im Lager wird als eigenständigesMaterialItem mit einer automatisch generierten 8-stelligen Alphanumerischen ID (z.B. 8A3F9B2C) nachverfolgt.
Dynamische Zustandsbewertung
Statt fester Notensysteme definieren Abteilungen eigene Bewertungskriterien pro Material. Ein Zelt kann z.B. nach "Zustand", "Sauberkeit" und "Wasserdichtigkeit" bewertet werden, ein Werkzeug nur nach "Zustand". Die Kriterien werden als JSON im Feld customConditionFields gespeichert, die Werte pro Exemplar in conditions:
// Material: Definiert die Kriterien
customConditionFields: ["Zustand", "Sauberkeit", "Wasserdichtigkeit"]
// MaterialItem: Speichert die Werte pro Exemplar
conditions: { "Zustand": 8, "Sauberkeit": 6, "Wasserdichtigkeit": 10 }Bundles & Baugruppen
Einfache Bündelungen werden über bundleSize und bundleUnitabgebildet (z.B. "10 Heringe = 1 Bund"). Für komplexe Sets existiert das Baugruppen-System: Ein Material mit isBaugruppe: true verknüpft über das Modell BaugruppeComponent mehrere Materialien mit festgelegten Mengen. Zusätzlich können MaterialItem-Exemplare über Parent-Child-Relationen physisch verschachtelt werden.
Standortverwaltung
Lagerorte folgen einer zweistufigen Hierarchie: Ein SITE beschreibt den Standort (Gebäude, Adresse), ein SPOT den konkreten Platz (Regal, Kammer). Jedes Material kann sowohl einem Standort als auch einem Spot zugewiesen werden.
Wartungshistorie & Lückenlose Nachverfolgung
Jedes Exemplar führt eine vollständige Historie. Das Modell MaintenanceLogdokumentiert Reparaturen, Kontrollen und Anschaffungen mit Freitextnotizen. Parallel dazu zeichnet MaterialItemHistory jeden Zustandswechsel auf -- inklusive alter und neuer Zustandswerte, der auslösenden Aktion (ORDER_RETURNED,MANUAL_EDIT, MAINTENANCE) und dem verantwortlichen Benutzer.
Verfügbarkeitsberechnung
Die zentrale Herausforderung: Sicherstellen, dass Material nicht doppelt verliehen wird, wenn sich Ausleihzeiträume überschneiden. Die Dateilib/checkAvailability.ts löst das mit einer zeitraumbasierten Kollisionsprüfung.
// Kernlogik: Zwei Zeiträume überschneiden sich genau dann,
// wenn startA < endB UND endA > startB.
const totalItems = await db.materialItem.count({
where: { materialId, status: { notIn: ["MAINTENANCE", "LOST"] } }
});
const overlapping = await db.orderItem.aggregate({
_sum: { quantity: true },
where: {
materialId,
order: {
status: { in: ["REQUESTED", "APPROVED", "READY", "ACTIVE"] },
AND: [
{ startDateTime: { lt: requestedEnd } },
{ endDateTime: { gt: requestedStart } }
]
}
}
});
const reserved = overlapping._sum.quantity ?? 0;
const available = Math.max(0, totalItems - reserved);Nur Bestellungen in aktiven Zuständen (REQUESTED, APPROVED,READY, ACTIVE) blockieren Kapazität. Abgeschlossene, stornierte oder abgelehnte Bestellungen werden ignoriert. Zusätzlich berücksichtigt der Algorithmus abteilungsspezifische Pufferzeiten: Nach einer Rückgabe bleibt Material für die konfigurierte restockTimeHours gesperrt, bevor es für neue Anfragen freigegeben wird.
Bestell- & Ausleih-Pipeline
Der Lebenszyklus einer Ausleihe durchläuft sechs Zustände:
- REQUESTED -- Der Benutzer wählt Material im Shop, legt einen Zeitraum fest und sendet die Anfrage ab. Die Verfügbarkeit wird in Echtzeit geprüft. E-Mail-Benachrichtigung an zuständige Admins.
- APPROVED -- Der Admin bestätigt die Anfrage. Optional: Sofortige Reservierung ohne Bestätigung (
reserveOnRequestInternal). - READY -- Der Mat-Chef stellt das Material zusammen. Dabei werden konkrete Exemplare (
MaterialItem) den einzelnen Positionen zugewiesen. Optional: Bereitstellungsfoto, Notizen für den Abholer. Der Besteller erhält eine E-Mail. - ACTIVE -- Material wurde übergeben. Optionales Übergabefoto zur Dokumentation.
- RETURNED -- Material zurückgebracht. Pro zugewiesenem Exemplar werden Rückgabezustand, Sauberkeit und eventuelle Schadensnotizen erfasst. Optionales Rückgabefoto.
- COMPLETED -- Rückgabe kontrolliert, Exemplare wieder im Bestand verfügbar.
Zusätzlich existieren die Zustände REJECTED und CANCELLEDmit optionalem Ablehnungsgrund bzw. Stornierungsdatum. Für externe Anfragen unterstützt das System Gast-Bestellungen (isGuestOrder): Dabei werden Name, E-Mail und Bemerkungen erfasst, ohne dass ein Benutzerkonto nötig ist.
E-Mail-System
Transaktionale E-Mails
Über die Resend API (lib/mail.ts) versendet MatManager automatisierte HTML-E-Mails mit einheitlichem Template-Design. Das System enthält über 10 spezialisierte E-Mail-Funktionen:
- Verifizierungscode bei Registrierung
- Passwort-Reset-Link
- Benachrichtigung an Admins bei neuen Bestellungen
- Statusänderungen an Besteller (bestätigt, abgelehnt, bereitgestellt)
- Rückgabe-Erinnerungen bei überfälligen Ausleihen
- Wartungserinnerungen bei abgelaufener Lebensdauer
- Einladungslinks für neue Mitglieder
- Beitrittsanfrage-Benachrichtigungen
- Bestätigung und Bereitstellungsinfo für Gast-Bestellungen
- Kontaktformular-Weiterleitung
Jeder Benutzer kann granular steuern, welche E-Mails er erhalten möchte (emailOrderUpdates, emailNewOrders,emailReturnReminder, emailMaintenance, etc.).
Admin-Werkzeuge
QR-Code-Generierung
Für jedes Exemplar können QR-Codes mit konfigurierbarem Inhalt generiert werden. Pro Material lässt sich festlegen, ob Name, ID, Datum und Gewicht im Code enthalten sein sollen (qrIncludeName, qrIncludeId, etc.). Die Codes werden clientseitig mit qrcode.react gerendert und können als druckbares PDF-Label exportiert werden.
Excel Import & Export
Über SheetJS (xlsx) können Materialbestände als .xlsx-Dateien exportiert werden. Der Import unterstützt Spaltenzuordnung über ein Mapping-Modal, sodass bestehende Inventarlisten aus anderen Systemen übernommen werden können.
Statistiken & Auswertungen
Das Admin-Dashboard zeigt Auswertungen über Recharts-Diagramme: Bestellvolumen über Zeit, populäre Materialien, Zustandsentwicklung der Bestände und Benutzeraktivität.
Cron-Jobs
Zeitgesteuerte API-Endpunkte (/api/cron/returns,/api/cron/maintenance) prüfen automatisch auf überfällige Rückgaben und abgelaufene Wartungsintervalle. Die Endpunkte sind mit einemCRON_SECRET Token abgesichert.
Bild-Pipeline & Speicherung
Serverseitiges Sharp-Optimierungs-System
Fotos werden beim Upload serverseitig über Sharp (lib/imageCompressor.ts) verarbeitet. Bilder werden automatisch auf maximal 1920x1920 Pixel skaliert und als WebP komprimiert. Zusätzlich generiert das System Thumbnails (300x300 Pixel).
Clientseitige Kompression & Zuschneiden
Vor dem Upload werden Bilder im Browser via Canvas komprimiert (lib/clientImageCompressor.ts). Das Zuschneiden erfolgt interaktiv mit ImageCropper.tsx.
Cloudflare R2 Bucket
Bilder werden in einem Cloudflare R2 Bucket gespeichert (lib/uploadHelper.ts). Die URLs werden in der Datenbank hinterlegt.
Sicherheit & Infrastruktur
Authentifizierung & Passwörter
Passwörter werden mit bcryptjs (10 Rounds) gehasht. Das System unterstützt klassischen E-Mail/Passwort-Login sowie Google OAuth (Single Sign-On).
Datenschutz & DSGVO / revDSG
Das System entspricht dem Schweizer revDSG. Personenbezogene Daten beschränken sich auf Name, E-Mail, Telefonnummer und Ausleihhistorie. Benutzer können ihr Konto und ihre Daten jederzeit löschen lassen.
Hosting & Deployment
MatManager ist auf Vercel gehostet. Die PostgreSQL-Datenbank wird über Supabase oder Neon betrieben und über den Prisma ORM angesprochen. Medien-Assets (Fotos) liegen in einem Cloudflare R2 Bucket und werden über eine öffentliche CDN-URL ausgeliefert. Transaktionale E-Mails laufen über die Resend API. Alle Verbindungen sind TLS-verschlüsselt.
Zeitgesteuerte Aufgaben (Rückgabe-Prüfung, Wartungserinnerungen) werden als Cron-Endpunkte bereitgestellt und über einen externen Scheduler aufgerufen, abgesichert durch einen gemeinsamen CRON_SECRET.