diff --git a/backend/src/workers/schedular-worker.ts b/backend/src/workers/schedular-worker.ts index 5917a7e..575b117 100644 --- a/backend/src/workers/schedular-worker.ts +++ b/backend/src/workers/schedular-worker.ts @@ -1,7 +1,7 @@ // backend/src/workers/scheduler-worker.ts import { parentPort, workerData } from 'worker_threads'; import { CPModel, CPSolver } from './cp-sat-wrapper.js'; -import { ShiftPlan } from '../models/ShiftPlan.js'; +import { ShiftPlan, Shift } from '../models/ShiftPlan.js'; import { Employee, EmployeeAvailability } from '../models/Employee.js'; import { Availability, Constraint } from '../models/scheduling.js'; @@ -10,7 +10,7 @@ interface WorkerData { employees: Employee[]; availabilities: Availability[]; constraints: Constraint[]; - shifts: any[]; + shifts: Shift[]; } function buildSchedulingModel(model: CPModel, data: WorkerData): void { @@ -123,7 +123,7 @@ function buildSchedulingModel(model: CPModel, data: WorkerData): void { if (availability) { const score = availability.preferenceLevel === 1 ? 10 : availability.preferenceLevel === 2 ? 5 : - -100; // Heavy penalty for assigning unavailable shifts + -1000; // Heavy penalty for assigning unavailable shifts const varName = `assign_${employee.id}_${shift.id}`; if (objectiveExpression) { diff --git a/frontend/src/pages/ShiftPlans/ShiftPlanView.tsx b/frontend/src/pages/ShiftPlans/ShiftPlanView.tsx index 41d377a..929c048 100644 --- a/frontend/src/pages/ShiftPlans/ShiftPlanView.tsx +++ b/frontend/src/pages/ShiftPlans/ShiftPlanView.tsx @@ -4,8 +4,8 @@ import { useParams, useNavigate } from 'react-router-dom'; import { useAuth } from '../../contexts/AuthContext'; import { shiftPlanService } from '../../services/shiftPlanService'; import { employeeService } from '../../services/employeeService'; -import { shiftAssignmentService, ShiftAssignmentService } from '../../services/shiftAssignmentService'; -import { IntelligentShiftScheduler, SchedulingResult, AssignmentResult } from '../../services/scheduling/useScheduling'; +import { shiftAssignmentService } from '../../services/shiftAssignmentService'; +import { AssignmentResult } from '../../models/scheduling'; import { ShiftPlan, TimeSlot, ScheduledShift } from '../../models/ShiftPlan'; import { Employee, EmployeeAvailability } from '../../models/Employee'; import { useNotification } from '../../contexts/NotificationContext'; @@ -421,11 +421,11 @@ const ShiftPlanView: React.FC = () => { }; // Use the freshly loaded data, not the state - const result = await ShiftAssignmentService.assignShifts( + const result = await shiftAssignmentService.assignShifts( shiftPlan, - refreshedEmployees, // Use fresh array, not state - refreshedAvailabilities, // Use fresh array, not state - constraints // Now this variable is defined + refreshedEmployees, + refreshedAvailabilities, + constraints ); setAssignmentResult(result); diff --git a/frontend/src/services/shiftAssignmentService.ts b/frontend/src/services/shiftAssignmentService.ts index 53de854..0b23444 100644 --- a/frontend/src/services/shiftAssignmentService.ts +++ b/frontend/src/services/shiftAssignmentService.ts @@ -3,7 +3,7 @@ import { ShiftPlan, ScheduledShift } from '../models/ShiftPlan'; import { Employee, EmployeeAvailability } from '../models/Employee'; import { authService } from './authService'; //import { IntelligentShiftScheduler, AssignmentResult, WeeklyPattern } from './scheduling/useScheduling'; -import { isScheduledShift } from '../models/helpers'; +import { AssignmentResult } from '../models/scheduling'; const API_BASE_URL = 'http://localhost:3002/api/scheduled-shifts'; @@ -134,7 +134,7 @@ export class ShiftAssignmentService { } } - static async assignShifts( + async assignShifts( shiftPlan: ShiftPlan, employees: Employee[], availabilities: EmployeeAvailability[], @@ -143,65 +143,13 @@ export class ShiftAssignmentService { console.log('🧠 Starting intelligent scheduling for FIRST WEEK ONLY...'); - // Load all scheduled shifts - const scheduledShifts = await shiftAssignmentService.getScheduledShiftsForPlan(shiftPlan.id); - - if (scheduledShifts.length === 0) { - return { - assignments: {}, - violations: ['❌ KRITISCH: Keine Schichten verfügbar für die Zuordnung'], - success: false, - resolutionReport: ['🚨 ABBRUCH: Keine Schichten im Plan verfügbar'] - }; - } - // Set cache for scheduler - IntelligentShiftScheduler.scheduledShiftsCache.set(shiftPlan.id, { - shifts: scheduledShifts, - timestamp: Date.now() - }); - - // 🔥 RUN SCHEDULING FOR FIRST WEEK ONLY - const schedulingResult = await IntelligentShiftScheduler.generateOptimalSchedule( - shiftPlan, - employees.filter(emp => emp.isActive), - availabilities, - constraints - ); - - // Get first week shifts for pattern - const firstWeekShifts = this.getFirstWeekShifts(scheduledShifts); - - console.log('🔄 Creating weekly pattern from FIRST WEEK:', { - firstWeekShifts: firstWeekShifts.length, - allShifts: scheduledShifts.length, - patternAssignments: Object.keys(schedulingResult.assignments).length - }); - - const weeklyPattern: WeeklyPattern = { - weekShifts: firstWeekShifts, - assignments: schedulingResult.assignments, // 🔥 Diese enthalten nur erste Woche - weekNumber: 1 - }; - - // 🔥 APPLY PATTERN TO ALL WEEKS - const allAssignments = this.applyWeeklyPattern(scheduledShifts, weeklyPattern); - - console.log('✅ Pattern applied to all weeks:', { - firstWeekAssignments: Object.keys(schedulingResult.assignments).length, - allWeeksAssignments: Object.keys(allAssignments).length - }); - - // Clean cache - IntelligentShiftScheduler.scheduledShiftsCache.delete(shiftPlan.id); return { - assignments: allAssignments, // 🔥 Diese enthalten alle Wochen - violations: schedulingResult.violations, - success: schedulingResult.success, - pattern: weeklyPattern, - resolutionReport: schedulingResult.resolutionReport, - qualityMetrics: schedulingResult.qualityMetrics + assignments: , + violations: , + success: , + resolutionReport: , }; } @@ -381,20 +329,6 @@ export class ShiftAssignmentService { return assignments; } - private static groupShiftsByWeek(shifts: ScheduledShift[]): { [weekNumber: string]: ScheduledShift[] } { - const weeks: { [weekNumber: string]: ScheduledShift[] } = {}; - - shifts.forEach(shift => { - const weekNumber = this.getWeekNumber(shift.date); - if (!weeks[weekNumber]) { - weeks[weekNumber] = []; - } - weeks[weekNumber].push(shift); - }); - - return weeks; - } - private static getFirstWeekShifts(shifts: ScheduledShift[]): ScheduledShift[] { if (shifts.length === 0) return []; @@ -410,26 +344,6 @@ export class ShiftAssignmentService { }); } - private static findMatchingPatternShift( - shift: ScheduledShift, - patternShifts: ScheduledShift[] - ): ScheduledShift | null { - const shiftDayOfWeek = this.getDayOfWeek(shift.date); - const shiftTimeSlot = shift.timeSlotId; - - return patternShifts.find(patternShift => - this.getDayOfWeek(patternShift.date) === shiftDayOfWeek && - patternShift.timeSlotId === shiftTimeSlot - ) || null; - } - - private static getWeekNumber(dateString: string): number { - const date = new Date(dateString); - const firstDayOfYear = new Date(date.getFullYear(), 0, 1); - const pastDaysOfYear = (date.getTime() - firstDayOfYear.getTime()) / 86400000; - return Math.ceil((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7); - } - // ========== EXISTING HELPER METHODS ========== static async getDefinedShifts(shiftPlan: ShiftPlan): Promise { @@ -533,36 +447,6 @@ export class ShiftAssignmentService { } } - private static findViolations( - assignments: { [shiftId: string]: string[] }, - employees: Employee[], - definedShifts: ScheduledShift[], - enforceNoTraineeAlone: boolean - ): string[] { - const violations: string[] = []; - const employeeMap = new Map(employees.map(emp => [emp.id, emp])); - - definedShifts.forEach(scheduledShift => { - const assignedEmployeeIds = assignments[scheduledShift.id] || []; - - if (assignedEmployeeIds.length === 0) { - violations.push(`Shift has no assigned employees`); - return; - } - - const assignedEmployees = assignedEmployeeIds.map(id => employeeMap.get(id)).filter(Boolean) as Employee[]; - - if (enforceNoTraineeAlone && assignedEmployees.length === 1) { - const soloEmployee = assignedEmployees[0]; - if (soloEmployee.employeeType === 'trainee') { - violations.push(`Trainee ${soloEmployee.name} is working alone`); - } - } - }); - - return violations; - } - private static getDayOfWeek(dateString: string): number { const date = new Date(dateString); return date.getDay() === 0 ? 7 : date.getDay();