Deux postes en même temps possibles, malus dans le solveur
Le chevauchement de créneaux (SHIFT_OVERLAP) n'est plus bloquant : il remonte en avertissement et l'affectation est enregistrée. Le solveur ne l'exclut plus mais applique un fort malus au bénévole déjà affecté ailleurs au même moment (proposé seulement sous l'effectif minimal, faute de mieux) ; il n'est jamais reproposé sur un créneau où il est déjà. Même malus dans le classement par défaut du panneau et dans le mock. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
d0fa1e109c
commit
0f904f5e3d
@@ -446,12 +446,12 @@ function runValidation(volunteerId: string, candidate: ShiftRead, excludeAssignm
|
||||
|
||||
const violations: Violation[] = [];
|
||||
|
||||
// SHIFT_OVERLAP (bloquante)
|
||||
// SHIFT_OVERLAP (avertissement : deux postes à la fois restent possibles)
|
||||
for (const other of existing) {
|
||||
if (overlaps(candidate, other)) {
|
||||
violations.push({
|
||||
code: 'SHIFT_OVERLAP',
|
||||
severity: 'BLOCKING',
|
||||
severity: 'WARNING',
|
||||
params: {
|
||||
other_shift_id: other.id,
|
||||
other_starts_at: other.starts_at,
|
||||
@@ -643,6 +643,8 @@ const SOLVER_CHAIN_PENALTY = 40;
|
||||
// traité comme réciproque : si A souhaite B, B en profite aussi (voir
|
||||
// `buildEffectiveCompanions`).
|
||||
const SOLVER_COMPANION_BONUS_PER_MATCH = 25;
|
||||
// Déjà affecté à un autre poste au même moment (cf. `_ALREADY_BUSY_PENALTY`).
|
||||
const SOLVER_ALREADY_BUSY_PENALTY = 300;
|
||||
|
||||
type TimeWindow = { starts_at: string; ends_at: string };
|
||||
|
||||
@@ -745,6 +747,7 @@ function solverScore(
|
||||
if (effectiveCompanionIds.has(id)) companionsPresent += 1;
|
||||
}
|
||||
score += SOLVER_COMPANION_BONUS_PER_MATCH * companionsPresent;
|
||||
if (existing.some((other) => overlaps(shift, other))) score -= SOLVER_ALREADY_BUSY_PENALTY;
|
||||
|
||||
const existingMinutes = existing.reduce(
|
||||
(sum, s) => sum + (naiveMinutes(s.ends_at) - naiveMinutes(s.starts_at)),
|
||||
@@ -787,13 +790,8 @@ function runSolver(): SolverRunResult {
|
||||
if (gap <= 0) continue;
|
||||
const alreadyAssignedIds = assignedVolunteerIds(shift.id);
|
||||
const picked = state.volunteers
|
||||
.filter((v) => {
|
||||
const busy = [
|
||||
...(existingByVolunteer.get(v.id) ?? []),
|
||||
...(takenByVolunteer.get(v.id) ?? []),
|
||||
];
|
||||
return !busy.some((other) => overlaps(shift, other));
|
||||
})
|
||||
.filter((v) => !alreadyAssignedIds.has(v.id))
|
||||
.filter((v) => !(takenByVolunteer.get(v.id) ?? []).some((other) => overlaps(shift, other)))
|
||||
.map((v) => {
|
||||
const existing = existingByVolunteer.get(v.id) ?? [];
|
||||
const predecessorIds = predecessorByShift.get(shift.id);
|
||||
|
||||
Reference in New Issue
Block a user