diff --git a/apps/ops/src/pages/PlanificationPage.vue b/apps/ops/src/pages/PlanificationPage.vue index ab39cd8..b169b45 100644 --- a/apps/ops/src/pages/PlanificationPage.vue +++ b/apps/ops/src/pages/PlanificationPage.vue @@ -4858,7 +4858,7 @@ async function optimizeSuggestion () { dates: suggestWindow.value, tech_ids: selTechs.map(t => t.id), // job_names = pool filtré (+ jobs DÉJÀ assignés de la fenêtre si « ré-optimiser l'existant ») → le solveur les replace. job_names: [...new Set([...suggestJobs().map(j => j.name), ...(suggestDlg.includeAssigned ? assignedNamesForWindow() : [])])], - opts: { rank_weight: solverOpts.rankWeight, speed_kmh: solverOpts.speedKmh, max_seconds: solverOpts.maxSeconds, overtime_coef: solverOpts.overtimeCoef }, + opts: { rank_weight: 20, versatility_weight: 3, speed_kmh: solverOpts.speedKmh, max_seconds: solverOpts.maxSeconds, overtime_coef: solverOpts.overtimeCoef }, // rang (compétence principale) DOMINE ; polyvalence = léger départage dur_overrides: { ...durOverride }, job_windows, }) if (!r || !r.ok || !Array.isArray(r.plan)) throw new Error((r && r.error) || 'réponse invalide') @@ -4919,7 +4919,7 @@ async function optimizeSuggestionLocal () { } if (!vehicles.length) { unassigned.push(...entries); continue } // aucun tech éligible ce jour → non assignés let res - try { res = await roster.optimizeRoutes({ jobs, vehicles, max_seconds: solverOpts.maxSeconds, rank_weight: solverOpts.rankWeight, speed_kmh: solverOpts.speedKmh, overtime_coef: solverOpts.overtimeCoef }) } catch (e) { unassigned.push(...entries); continue } + try { res = await roster.optimizeRoutes({ jobs, vehicles, max_seconds: solverOpts.maxSeconds, rank_weight: 20, versatility_weight: 3, speed_kmh: solverOpts.speedKmh, overtime_coef: solverOpts.overtimeCoef }) } catch (e) { unassigned.push(...entries); continue } if (!res || res.status !== 'OK') { unassigned.push(...entries); continue } // solveur absent/erreur → non assignés (ne jamais perdre de jobs) const placed = new Set() for (const rt of (res.routes || [])) { diff --git a/services/roster-solver/route_solver.py b/services/roster-solver/route_solver.py index cb7d41d..b07a582 100644 --- a/services/roster-solver/route_solver.py +++ b/services/roster-solver/route_solver.py @@ -57,7 +57,7 @@ def solve_route(req: dict) -> dict: eff_bias = float(req.get("eff_bias") if req.get("eff_bias") is not None else 8.0) # RÉSERVE DES EXPERTS : pénalise les techs POLYVALENTS (beaucoup de compétences) → les cas simples vont d'abord # aux moins polyvalents (qui ne pourraient rien faire d'autre), gardant les experts pour les jobs exigeants. - vers_w = float(req.get("versatility_weight") if req.get("versatility_weight") is not None else 6.0) + vers_w = float(req.get("versatility_weight") if req.get("versatility_weight") is not None else 3.0) # TIEBREAKER léger seulement — le RANG (compétence principale) doit dominer def eff_of(veh, sk): se = veh.get("skill_eff") or {} diff --git a/services/targo-hub/lib/roster.js b/services/targo-hub/lib/roster.js index 6db3f03..ae6816c 100644 --- a/services/targo-hub/lib/roster.js +++ b/services/targo-hub/lib/roster.js @@ -263,7 +263,7 @@ async function optimizePlan (body) { ...(w ? { tw_start_min: w.tw_start_min, tw_end_min: w.tw_end_min } : {}), } }) - const res = await solveVrp({ jobs: sJobs, vehicles, max_seconds: Number(opts.max_seconds) || 8, rank_weight: opts.rank_weight != null ? Number(opts.rank_weight) : 2, speed_kmh: Number(opts.speed_kmh) || 45, overtime_coef: opts.overtime_coef != null ? Number(opts.overtime_coef) : 20 }) + const res = await solveVrp({ jobs: sJobs, vehicles, max_seconds: Number(opts.max_seconds) || 8, rank_weight: opts.rank_weight != null ? Number(opts.rank_weight) : 20, versatility_weight: opts.versatility_weight != null ? Number(opts.versatility_weight) : 3, speed_kmh: Number(opts.speed_kmh) || 45, overtime_coef: opts.overtime_coef != null ? Number(opts.overtime_coef) : 20 }) if (!res || res.status !== 'OK') { for (const j of rest) unassigned.push(entryOf(j, iso, durOf(j))); continue } const placed = new Set() for (const rt of (res.routes || [])) {