Compatibility Checks Algorithm
Compatibility Checks Algorithm
Compatibility Checks sind automatische Prüfungen, die beim Zuweisen von Schichten durchgeführt werden. Sie helfen Admins, sichere und passende Zuweisungen zu treffen.
Für Admins
Was ist eine Kompatibilitätsprüfung?
Wenn du einen Mitarbeiter zu einer Schicht zuordnen möchtest, prüft das System automatisch, ob es Probleme gibt:
- Overlapping Shifts — Hat der Mitarbeiter an diesem Datum bereits eine andere Schicht?
- Position Mismatch — Passt die Position des Mitarbeiters zur Schicht?
- Required Custom Fields — Hat der Mitarbeiter alle erforderlichen Felder ausgefüllt?
- Rest Time — Genug Ruhezeiten zwischen Schichten?
- Working Hours — Überschreitet die tägliche Arbeitszeit?
- Availability — Ist der Mitarbeiter für diesen Zeitraum verfügbar?
Warnung vs. Blocker
Die Kompatibilitätsprüfung hat zwei Modi:
1. Warnung-Modus (Standard — Strict Mode OFF)
⚠️ Warnung: Position "Koch" erforderlich, Mitarbeiter hat "Aushilfe"
→ [Trotzdem zuweisen] [Abbrechen]- Admin KANN trotzdem zuweisen
- Das System zeigt nur eine Warnung
- Sinnvoll für flexible Pläne oder Notfälle
2. Blocker-Modus (Strict Mode ON)
❌ Fehler: Position "Koch" erforderlich, Mitarbeiter hat "Aushilfe"
→ [Abbrechen]- Admin KANN NICHT zuweisen
- Das System blockiert die Zuweisung
- Sinnvoll für strikte Anforderungen (Lizenz, Sicherheit)
Wie Strict Mode die Kompatibilität beeinflusst
Strict Mode OFF (Standard):
- Position Mismatch → Warnung (Admin kann ignorieren)
- Required Custom Fields missing → Warnung
- Overlap with other shift → Warnung
- Working Hours too high → Warnung
Strict Mode ON:
- Position Mismatch → Blocker (Zuweisung nicht möglich)
- Required Custom Fields missing → Blocker
- Overlap with other shift → Blocker
- Working Hours too high → Warnung (bleibt Warnung)
Kritische Probleme (immer Blocker, unabhängig von Strict Mode):
- Mitarbeiter ist bereits zu dieser Schicht zugewiesen
- Mitarbeiter Vertrag endet vor der Schicht
- Externe API-Anforderung fehlgeschlagen
Kompatibilitätsprüfungen im Detail
1. Überlappende Schichten (Blocking — immer)
Problem: Der Mitarbeiter hat bereits eine andere Schicht zu dieser Zeit
Bestehendes Shift: Montag 09:00 - 17:00
Neue Schicht: Montag 16:00 - 22:00
^^^^^^ Überlappung!Aktion: System blockiert (egal welcher Modus)
Lösung: Wähle einen anderen Mitarbeiter oder ändere die neue Schicht
2. Position Mismatch (Warning or Blocker)
Problem: Mitarbeiter hat die erforderliche Position nicht
Schicht erforderlich: Position "Koch"
Mitarbeiter hat: Position "Aushilfe"Strict Mode OFF: ⚠️ Warnung Strict Mode ON: ❌ Blocker
Lösung:
- Position zum Mitarbeiter hinzufügen, ODER
- Andere Schicht/Mitarbeiter wählen
3. Erforderliche Custom Fields (Warning or Blocker)
Problem: Schicht erfordert ein Custom Field, Mitarbeiter hat es nicht
Schicht erfordert: "Gabelstaplerführerschein" = true
Mitarbeiter hat: "Gabelstaplerführerschein" = false/leerStrict Mode OFF: ⚠️ Warnung Strict Mode ON: ❌ Blocker
Lösung:
- Mitarbeiter-Profil aktualisieren und Feld ausfüllen, ODER
- Andere Anforderung für die Schicht setzen
4. Ruhezeit zwischen Schichten (Warning or Blocker)
Problem: Nicht genug Ruhezeit zwischen Schichten
Gestrige Schicht: Sonntag 08:00 - 17:00
Neue Schicht: Montag 06:00 - 14:00
↑ Nur 13 Stunden Ruhe (mindestens 10 erforderlich)Strict Mode OFF: ⚠️ Warnung Strict Mode ON: ❌ Blocker (bei 10+ Stunden Anforderung)
5. Tägliche Arbeitszeit (Warning — immer)
Problem: Zu viele Stunden an einem Tag
Mitarbeiter hat bereits: 7 Stunden heute
Neue Schicht: 6 Stunden
Gesamt: 13 Stunden (max. 12 erlaubt)Aktion: ⚠️ Warnung (immer, auch mit Strict Mode OFF)
Grenze: Maximal 12 Stunden pro Tag
6. Verfügbarkeit (Warning or Blocker)
Problem: Mitarbeiter hat für diese Zeit keine Verfügbarkeit eingegeben
Mitarbeiter Verfügbarkeit: Montag 09:00 - 17:00
Neue Schicht: Montag 16:00 - 22:00
↑ Ab 17 Uhr nicht verfügbarStrict Mode OFF: ⚠️ Warnung (nur wenn availabilitiesEnabled = true) Strict Mode ON: ⚠️ Warnung (bleibt Warnung)
Konfigurationsbeispiele
Szenario 1: Restaurant — Flexible Aushilfen
Strict Mode: OFF
Verfügbarkeiten: AN
Position Restrictions: OFF
Required Custom Fields: Keine
Auswirkung:
- Admins können relativ frei zuweisen
- Warnungen erinnern an Probleme, blockieren aber nicht
- Ideal für schnelle, flexible PlanungSzenario 2: Klinik — Strikte Anforderungen
Strict Mode: ON
Verfügbarkeiten: AN
Position Restrictions: ON
Required Custom Fields:
- "Erste Hilfe Kurs" (required = true)
- "Ärztliche Freigabe" (required = true)
Auswirkung:
- Nur qualifiziertes Personal kann Schichten sehen (Position Restrict ON)
- Admins können nur zuweisen wenn beide Zertifikate vorhanden
- Strikte Einhaltung von Ruhezeiten und ArbeitszeitenSzenario 3: Events — Projekt-Teams
Strict Mode: OFF
Verfügbarkeiten: AUS (Event-spezifisch geplant)
Position Restrictions: ON
Required Custom Fields:
- "Event-Rolle" (required = true)
Auswirkung:
- Mitarbeiter sehen nur Schichten ihrer Position
- Event-Rolle beschreibt spezifische Funktion beim Event
- Admins haben Flexibilität bei ZuweisungenHäufige Probleme & Lösungen
Problem: "Mitarbeiter kann dieser Schicht nicht zugewiesen werden" Mögliche Gründe:
- Position passt nicht (check mit Position Restrictions)
- Strict Mode ist ON und ein Feld passt nicht
- Mitarbeiter hat Konflikt mit anderer Schicht
- Zu viele Arbeitszeiten bereits eingeplant
Lösung:
- Lies die Fehlermeldung genau
- Bethe den Grund (Position, Feld, Zeit, Verfügbarkeit)
- Behebe das Problem (Position ändern, Feld ausfüllen, andere Schicht wählen)
For Developers
Overview
The compatibility check algorithm is implemented in src/sections/admin-calendar-v2/daily-planning/utils/compatibility-check.ts.
Main function:
export const checkCompatibility = (props: CompatibilityProps): CompatibilityResultInput props:
type CompatibilityProps = {
team: Partial<DBTeam>; // Team config
employee: any; // Employee data
shift: KanbanBoardShift; // Shift being assigned
todaysShiftData?: KanbanBoardShift[];
yesterdaysShiftData: DBShift<true>[];
tomorrowsShiftData: DBShift<true>[];
availabilityTimeSpan?: { from: Date | string | null; to: Date | string | null };
scheduleType?: DBScheduleType;
eventCustomers?: Array<{ id: number; name: string }>;
};Output:
type CompatibilityResult = {
isCompatible: boolean; // No issues
isBlocking: boolean; // Has blocking issues (from isBlocking flag)
reasons: IncompatibilityReason[]; // Sorted: critical → warning → info
};
type IncompatibilityReason = {
id: string;
summary: string;
details: string;
severity: '1-critical' | '2-warning' | '3-info';
isBlocking?: boolean; // If true, cannot assign (regardless of strictMode)
};Critical Checks (Always Blocking)
These checks set isBlocking: true unconditionally:
-
Contract End Date
- If
employee.lockedAt <= shift.dateTo, block entire assignment - Code: L110-125
- If
-
Already Assigned
- If
shift.assignments.some(a => a.userId === employee.userId), block - Code: L128-134
- If
-
Shift Overlap
- If new shift overlaps with any existing assignment, block
- Calculated using
areIntervalsOverlapping()with date-fns - Code: L137-166
Warning/Info Checks (Affected by strictModeEnabled)
These checks add warnings; if strictModeEnabled = true, they become blocking:
-
Rest Time Between Shifts
- Minimum
minHoursBetweenShifts(typically 10 hours) - Code: L170-200
- Minimum
-
Daily Working Hours
- Warning threshold: 10 hours
- Maximum: 12 hours (warning even without strict mode)
- Code: L203-230
-
Position Mismatch
- If
positionIds.length > 0and!positionIds.includes(shift.positionId) - Code: L387-393
-
if (positionIds && positionIds.length > 0 && !positionIds.includes(shift.positionId)) { incompatibilities.push({ id: 'not-qualified', severity: '2-warning', }); }
- If
-
Availability Time Span Mismatch (if availabilitiesEnabled)
- If availability times don't cover shift times
- Code: L354-382
-
Required Custom Fields
- Checks
shift.requiredUserFieldsagainstteam.customUserFields - For each required field, checks if
employee.user.customFields[fieldId] === true - Code: L396-407
-
for (const requiredField of shift.requiredUserFields) { const field = team?.customUserFields?.find((f) => f.id === requiredField); if (employee.user.customFields?.[requiredField] !== true) { incompatibilities.push({ id: `missing-${requiredField}`, severity: '2-warning', }); } }
- Checks
Converting Warnings to Blockers
When strictModeEnabled = true:
const strictModeEnabled = team.strictModeEnabled ?? true;
// After collecting all incompatibilities:
if (strictModeEnabled) {
incompatibilities.forEach((reason) => {
if (reason.severity !== '1-critical') {
reason.isBlocking = true; // Convert warning → blocker
}
});
}Then the UI checks:
if (compatibilityResult.isBlocking) {
// Show error dialog, prevent assignment
} else {
// Show warning, allow "Proceed anyway" button
}Usage in DnD & Assignment UI
Called from:
src/sections/admin-calendar-v2/daily-planning/hooks/use-admin-calendar-dnd.ts— Drag-and-drop feedbacksrc/sections/admin-calendar-v2/dialogs/quick-assign-dialog-staff.tsx— Staff quick-assignsrc/sections/admin-calendar-v2/dialogs/bulk-assign-dialog.tsx— Bulk operations
Each caller passes the full context (today, yesterday, tomorrow shift data) so compatibility can consider adjacent days for rest-time checks.
Key Design Insights
-
Separation of Concerns:
- Position filtering (visibility) is separate from compatibility (assignment feedback)
- Restrict Positions hides shifts; Compatibility warns/blocks assignment
-
Strictness Levels:
- Critical = always blocking
- Warnings = blocking if strict mode, allowed with warning if not
- Info = never blocking
-
Custom Field Integration:
- Custom fields are treated like other binary checks
requiredUserFieldsarray in shift definition- Customizable per shift, enforced per-team via
customUserFieldsconfig
-
Time-Aware Checks:
- All checks account for shift time spans, not just dates
interval()andareIntervalsOverlapping()from date-fns for precise comparisons
Future Extensions
- Add skill-based compatibility (not just position/fields)
- Add customer affinity checks (User-Customer Relations for filtering)
- Add workload balancing (fair distribution of hours)
- Add external API validation (third-party qualification checks)