mirror of
https://github.com/donpat1to/Schichtenplaner.git
synced 2025-12-01 06:55:45 +01:00
shift assignment works
This commit is contained in:
@@ -863,4 +863,193 @@ export function checkAllProblemsResolved(
|
||||
const allResolved = criticalProblems.length === 0;
|
||||
|
||||
return { resolved, remaining, allResolved };
|
||||
}
|
||||
|
||||
export function createDetailedResolutionReport(
|
||||
assignments: Assignment,
|
||||
employees: Map<string, SchedulingEmployee>,
|
||||
shifts: SchedulingShift[],
|
||||
managerShifts: string[],
|
||||
repairContext: RepairContext
|
||||
): string[] {
|
||||
const report: string[] = [];
|
||||
|
||||
report.push('=== DETAILIERTER REPARATUR-BERICHT ===');
|
||||
report.push('');
|
||||
|
||||
// 1. ZUSAMMENFASSUNG
|
||||
report.push('📊 ZUSAMMENFASSUNG');
|
||||
const totalShifts = shifts.length;
|
||||
const assignedShifts = Object.values(assignments).filter(a => a.length > 0).length;
|
||||
const emptyShifts = totalShifts - assignedShifts;
|
||||
|
||||
report.push(` • Gesamtschichten: ${totalShifts}`);
|
||||
report.push(` • Zugewiesene Schichten: ${assignedShifts}`);
|
||||
report.push(` • Leere Schichten: ${emptyShifts}`);
|
||||
report.push(` • Pool-Mitarbeiter: ${repairContext.unassignedPool.length}`);
|
||||
report.push(` • Gesperrte Schichten: ${repairContext.lockedShifts.size}`);
|
||||
report.push('');
|
||||
|
||||
// 2. DURCHGEFÜHRTE AKTIONEN
|
||||
if (repairContext.warnings.length > 0) {
|
||||
report.push('🔧 DURCHGEFÜHRTE REPARATURAKTIONEN');
|
||||
repairContext.warnings.forEach((action, index) => {
|
||||
if (action.includes('zugewiesen') || action.includes('entfernt') || action.includes('getauscht') || action.includes('bewegt')) {
|
||||
report.push(` ✅ ${action}`);
|
||||
}
|
||||
});
|
||||
report.push('');
|
||||
}
|
||||
|
||||
// 3. DETAILLIERTE SCHICHTANALYSE
|
||||
report.push('📅 DETAILLIERTE SCHICHTANALYSE');
|
||||
|
||||
shifts.forEach(shift => {
|
||||
const assignment = assignments[shift.id] || [];
|
||||
const assignedEmployees = assignment.map(empId => {
|
||||
const emp = employees.get(empId);
|
||||
return emp ? `${emp.name} (${emp.role})` : 'Unbekannt';
|
||||
}).join(', ');
|
||||
|
||||
const isManagerShift = managerShifts.includes(shift.id);
|
||||
const isEmpty = assignment.length === 0;
|
||||
const hasOnlyNew = onlyNeuAssigned(assignment, employees);
|
||||
const experiencedAloneCheck = hasExperiencedAloneNotAllowed(assignment, employees);
|
||||
|
||||
let status = '✅ OK';
|
||||
if (isEmpty) status = '❌ LEER';
|
||||
else if (isManagerShift && hasOnlyNew) status = '⚠️ MANAGER + NUR NEUE';
|
||||
else if (experiencedAloneCheck.hasViolation) status = '❌ ERFAHRENER ALLEIN';
|
||||
else if (hasOnlyNew) status = '⚠️ NUR NEUE';
|
||||
|
||||
report.push(` • Schicht ${shift.id}:`);
|
||||
report.push(` - Status: ${status}`);
|
||||
report.push(` - Zugewiesene: ${assignedEmployees || 'Keine'}`);
|
||||
report.push(` - Benötigt: ${shift.requiredEmployees} Mitarbeiter`);
|
||||
report.push(` - Aktuell: ${assignment.length} Mitarbeiter`);
|
||||
if (isManagerShift) report.push(` - 💼 MANAGER-SCHICHT`);
|
||||
report.push('');
|
||||
});
|
||||
|
||||
// 4. PROBLEMANALYSE NACH TYP
|
||||
report.push('🚨 PROBLEMANALYSE NACH TYP');
|
||||
|
||||
const emptyShiftsList = shifts.filter(shift =>
|
||||
(assignments[shift.id] || []).length === 0
|
||||
);
|
||||
|
||||
const managerWithOnlyNewList = shifts.filter(shift =>
|
||||
managerShifts.includes(shift.id) &&
|
||||
isManagerShiftWithOnlyNew(assignments[shift.id] || [], employees, Array.from(employees.values()).find(e => e.role === 'manager')?.id)
|
||||
);
|
||||
|
||||
const experiencedAloneList = shifts.filter(shift => {
|
||||
const check = hasExperiencedAloneNotAllowed(assignments[shift.id] || [], employees);
|
||||
return check.hasViolation;
|
||||
});
|
||||
|
||||
const onlyNewList = shifts.filter(shift =>
|
||||
!managerShifts.includes(shift.id) &&
|
||||
onlyNeuAssigned(assignments[shift.id] || [], employees)
|
||||
);
|
||||
|
||||
if (emptyShiftsList.length > 0) {
|
||||
report.push(` ❌ LEERE SCHICHTEN (${emptyShiftsList.length}):`);
|
||||
emptyShiftsList.forEach(shift => {
|
||||
report.push(` - ${shift.id}`);
|
||||
});
|
||||
report.push('');
|
||||
}
|
||||
|
||||
if (managerWithOnlyNewList.length > 0) {
|
||||
report.push(` ⚠️ MANAGER + NUR NEUE (${managerWithOnlyNewList.length}):`);
|
||||
managerWithOnlyNewList.forEach(shift => {
|
||||
const assignment = assignments[shift.id] || [];
|
||||
const employeesList = assignment.map(empId => {
|
||||
const emp = employees.get(empId);
|
||||
return emp ? emp.name : 'Unbekannt';
|
||||
}).join(', ');
|
||||
report.push(` - ${shift.id}: ${employeesList}`);
|
||||
});
|
||||
report.push('');
|
||||
}
|
||||
|
||||
if (experiencedAloneList.length > 0) {
|
||||
report.push(` ❌ ERFAHRENER ALLEIN (${experiencedAloneList.length}):`);
|
||||
experiencedAloneList.forEach(shift => {
|
||||
const check = hasExperiencedAloneNotAllowed(assignments[shift.id] || [], employees);
|
||||
const emp = employees.get(check.employeeId!);
|
||||
report.push(` - ${shift.id}: ${emp?.name || check.employeeId}`);
|
||||
});
|
||||
report.push('');
|
||||
}
|
||||
|
||||
if (onlyNewList.length > 0) {
|
||||
report.push(` ⚠️ NUR NEUE IN SCHICHT (${onlyNewList.length}):`);
|
||||
onlyNewList.forEach(shift => {
|
||||
report.push(` - ${shift.id}`);
|
||||
});
|
||||
report.push('');
|
||||
}
|
||||
|
||||
// 5. REPARATURVERSUCHE
|
||||
report.push('🛠️ REPARATURVERSUCHE UND -ERGEBNISSE');
|
||||
|
||||
// Zähle die verschiedenen Reparaturtypen
|
||||
const assignmentActions = repairContext.warnings.filter(w =>
|
||||
w.includes('zugewiesen')
|
||||
).length;
|
||||
|
||||
const removalActions = repairContext.warnings.filter(w =>
|
||||
w.includes('entfernt')
|
||||
).length;
|
||||
|
||||
const swapActions = repairContext.warnings.filter(w =>
|
||||
w.includes('getauscht') || w.includes('bewegt')
|
||||
).length;
|
||||
|
||||
report.push(` • Zuweisungen: ${assignmentActions}`);
|
||||
report.push(` • Entfernungen: ${removalActions}`);
|
||||
report.push(` • Tausche/Bewegungen: ${swapActions}`);
|
||||
report.push(` • Gesamte Aktionen: ${repairContext.warnings.length}`);
|
||||
report.push('');
|
||||
|
||||
// 6. EMPFEHLUNGEN
|
||||
report.push('💡 EMPFEHLUNGEN ZUR PROBLEMBEHANDLUNG');
|
||||
|
||||
if (emptyShiftsList.length > 0) {
|
||||
report.push(` • Für ${emptyShiftsList.length} leere Schichten:`);
|
||||
report.push(` - Verfügbare Mitarbeiter prüfen`);
|
||||
report.push(` - Vertragskapazitäten überprüfen`);
|
||||
report.push(` - Ggf. Schichtanforderungen reduzieren`);
|
||||
report.push('');
|
||||
}
|
||||
|
||||
if (managerWithOnlyNewList.length > 0) {
|
||||
report.push(` • Für ${managerWithOnlyNewList.length} Manager+Neue-Schichten:`);
|
||||
report.push(` - Erfahrene Mitarbeiter zuweisen`);
|
||||
report.push(` - Manager-Verfügbarkeit anpassen`);
|
||||
report.push(` - Teamzusammensetzung optimieren`);
|
||||
report.push('');
|
||||
}
|
||||
|
||||
// 7. FINALE BEWERTUNG
|
||||
report.push('🎯 FINALE BEWERTUNG');
|
||||
|
||||
const totalProblems = emptyShiftsList.length + experiencedAloneList.length;
|
||||
const totalWarnings = managerWithOnlyNewList.length + onlyNewList.length;
|
||||
|
||||
if (totalProblems === 0) {
|
||||
report.push(' ✅ ALLE KRITISCHEN PROBLEME BEHOBEN');
|
||||
report.push(' Der Schichtplan kann veröffentlicht werden.');
|
||||
} else {
|
||||
report.push(` ❌ ${totalProblems} KRITISCHE PROBLEME VERBLEIBEND`);
|
||||
report.push(` ⚠️ ${totalWarnings} WARNUNGEN`);
|
||||
report.push(' Der Schichtplan kann nicht veröffentlicht werden.');
|
||||
}
|
||||
|
||||
report.push('');
|
||||
report.push('=== ENDE DES DETAILIERTEN BERICHTES ===');
|
||||
|
||||
return report;
|
||||
}
|
||||
@@ -29,7 +29,8 @@ import {
|
||||
resolveOverstaffedExperienced,
|
||||
prioritizeWarningsWithPool,
|
||||
resolveExperiencedAloneNotAllowed,
|
||||
checkAllProblemsResolved
|
||||
checkAllProblemsResolved,
|
||||
createDetailedResolutionReport
|
||||
} from './repairFunctions';
|
||||
|
||||
// Phase A: Regular employee scheduling (without manager)
|
||||
@@ -144,7 +145,7 @@ function phaseBInsertManager(
|
||||
console.log(`🎯 Phase B: Processing ${managerShifts.length} manager shifts`);
|
||||
|
||||
for (const shiftId of managerShifts) {
|
||||
//const shift = nonManagerShifts.find(s => s.id === shiftId) || { id: shiftId, requiredEmployees: 2 };
|
||||
let _shift = nonManagerShifts.find(s => s.id === shiftId) || { id: shiftId, requiredEmployees: 2 };
|
||||
|
||||
// Assign manager to his chosen shifts
|
||||
if (!assignments[shiftId].includes(manager.id)) {
|
||||
@@ -240,11 +241,12 @@ export function enhancedPhaseCRepairValidate(
|
||||
const employeeMap = new Map(employees.map(emp => [emp.id, emp]));
|
||||
const manager = employees.find(emp => emp.role === 'manager');
|
||||
|
||||
console.log('🔄 Starting Enhanced Phase C: Smart Repair & Validation');
|
||||
console.log('🔄 Starting Enhanced Phase C: Detailed Repair & Validation');
|
||||
|
||||
// 1. Manager-Schutzregel
|
||||
managerShifts.forEach(shiftId => {
|
||||
repairContext.lockedShifts.add(shiftId);
|
||||
repairContext.warnings.push(`Schicht ${shiftId} als Manager-Schicht gesperrt`);
|
||||
});
|
||||
|
||||
// 2. Überbesetzte erfahrene Mitarbeiter identifizieren und in Pool verschieben
|
||||
@@ -276,6 +278,7 @@ export function enhancedPhaseCRepairValidate(
|
||||
severity: 'error',
|
||||
message: `Leere Schicht: ${shift.id}`
|
||||
});
|
||||
repairContext.warnings.push(`Konnte leere Schicht ${shift.id} nicht beheben`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,7 +305,7 @@ export function enhancedPhaseCRepairValidate(
|
||||
shiftId: shift.id,
|
||||
employeeId: experiencedAloneCheck.employeeId,
|
||||
severity: 'error',
|
||||
message: `Erfahrener Mitarbeiter ${emp?.name || experiencedAloneCheck.employeeId} arbeitet allein, darf aber nicht alleine arbeiten`
|
||||
message: `Erfahrener Mitarbeiter ${emp?.name || experiencedAloneCheck.employeeId} arbeitet allein in Schicht ${shift.id}`
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -325,26 +328,26 @@ export function enhancedPhaseCRepairValidate(
|
||||
managerShifts.forEach(shiftId => {
|
||||
const assignment = assignments[shiftId] || [];
|
||||
|
||||
// Manager allein -> KRITISCH (error)
|
||||
// Manager allein
|
||||
if (isManagerAlone(assignment, manager?.id)) {
|
||||
repairContext.violations.push({
|
||||
repairContext.violations.push({
|
||||
type: 'ManagerAlone',
|
||||
shiftId: shiftId,
|
||||
severity: 'error', // KRITISCH
|
||||
severity: 'error',
|
||||
message: `Manager allein in Schicht ${shiftId}`
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Manager + nur Neue -> NUR WARNUNG (warning)
|
||||
// Manager + nur Neue
|
||||
if (isManagerShiftWithOnlyNew(assignment, employeeMap, manager?.id)) {
|
||||
repairContext.violations.push({
|
||||
repairContext.violations.push({
|
||||
type: 'ManagerWithOnlyNew',
|
||||
shiftId: shiftId,
|
||||
severity: 'warning', // NUR WARNUNG
|
||||
severity: 'warning',
|
||||
message: `Manager mit nur Neuen in Schicht ${shiftId}`
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Erstelle finale Violations-Liste
|
||||
const uniqueViolations = repairContext.violations.filter((v, index, self) =>
|
||||
@@ -360,56 +363,40 @@ export function enhancedPhaseCRepairValidate(
|
||||
);
|
||||
|
||||
const finalViolations = [
|
||||
// Nur ERROR-Violations als ERROR markieren
|
||||
...uniqueViolations
|
||||
.filter(v => v.severity === 'error')
|
||||
.map(v => `ERROR: ${v.message}`),
|
||||
|
||||
// WARNING-Violations als WARNING markieren
|
||||
.filter(v => v.severity === 'error')
|
||||
.map(v => `ERROR: ${v.message}`),
|
||||
...uniqueViolations
|
||||
.filter(v => v.severity === 'warning')
|
||||
.map(v => `WARNING: ${v.message}`),
|
||||
|
||||
// Andere Warnungen als INFO (Aktionen)
|
||||
.filter(v => v.severity === 'warning')
|
||||
.map(v => `WARNING: ${v.message}`),
|
||||
...uniqueWarnings.map(w => `INFO: ${w}`)
|
||||
];
|
||||
];
|
||||
|
||||
// 9. FINALE ÜBERPRÜFUNG: Prüfe ob alle kritischen Probleme gelöst wurden
|
||||
const resolutionCheck = checkAllProblemsResolved(
|
||||
// 9. DETAILLIERTER REPARATUR-BERICHT
|
||||
const resolutionReport = createDetailedResolutionReport(
|
||||
assignments,
|
||||
employeeMap,
|
||||
shifts,
|
||||
managerShifts,
|
||||
finalViolations
|
||||
repairContext
|
||||
);
|
||||
|
||||
const resolutionReport = [
|
||||
'=== REPARATUR-BERICHT ===',
|
||||
`Aufgelöste Probleme: ${resolutionCheck.resolved.length}`,
|
||||
`Verbleibende Probleme: ${resolutionCheck.remaining.length}`,
|
||||
`Alle kritischen Probleme behoben: ${resolutionCheck.allResolved ? '✅ JA' : '❌ NEIN'}`,
|
||||
'',
|
||||
'--- AUFGELÖSTE PROBLEME ---',
|
||||
...(resolutionCheck.resolved.length > 0 ? resolutionCheck.resolved : ['Keine']),
|
||||
'',
|
||||
'--- VERBLEIBENDE PROBLEME ---',
|
||||
...(resolutionCheck.remaining.length > 0 ? resolutionCheck.remaining : ['Keine']),
|
||||
'',
|
||||
'=== ENDE BERICHT ==='
|
||||
];
|
||||
// Bestimme ob alle kritischen Probleme behoben wurden
|
||||
const criticalProblems = uniqueViolations.filter(v => v.severity === 'error');
|
||||
const allProblemsResolved = criticalProblems.length === 0;
|
||||
|
||||
console.log('📊 Enhanced Phase C completed:', {
|
||||
poolSize: repairContext.unassignedPool.length,
|
||||
violations: uniqueViolations.length,
|
||||
warnings: uniqueWarnings.length,
|
||||
allProblemsResolved: resolutionCheck.allResolved
|
||||
totalActions: uniqueWarnings.length,
|
||||
criticalProblems: criticalProblems.length,
|
||||
warnings: uniqueViolations.filter(v => v.severity === 'warning').length,
|
||||
allProblemsResolved
|
||||
});
|
||||
|
||||
return {
|
||||
assignments,
|
||||
violations: finalViolations,
|
||||
resolutionReport,
|
||||
allProblemsResolved: resolutionCheck.allResolved
|
||||
allProblemsResolved
|
||||
};
|
||||
}
|
||||
|
||||
@@ -421,7 +408,7 @@ export function scheduleWithManager(
|
||||
): SchedulingResult & { resolutionReport?: string[]; allProblemsResolved?: boolean } {
|
||||
|
||||
const assignments: Assignment = {};
|
||||
//const allViolations: string[] = [];
|
||||
const allViolations: string[] = [];
|
||||
|
||||
// Initialisiere Zuweisungen
|
||||
shifts.forEach(shift => {
|
||||
@@ -444,14 +431,14 @@ export function scheduleWithManager(
|
||||
console.log('🔄 Starting Phase B: Enhanced Manager insertion');
|
||||
|
||||
// Phase B: Erweiterte Manager-Einfügung
|
||||
/*const phaseBResult = phaseBInsertManager(
|
||||
const phaseBResult = phaseBInsertManager(
|
||||
assignments,
|
||||
manager,
|
||||
managerShifts,
|
||||
employees,
|
||||
nonManagerShifts,
|
||||
constraints
|
||||
);*/
|
||||
);
|
||||
|
||||
console.log('🔄 Starting Enhanced Phase C: Smart Repair & Validation');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user