feat(planning): make optimization rules composable

Evaluate explicit, ordered optimization configurations through one shared
planner model. Add six editable presets, durable one-time legacy migration,
and consistent completed and cancelled plan provenance.

Include functional regressions and the agreed trace-removal and benchmark
follow-up specs. Performance acceptance remains in #78 under unchanged gates.

Refs: #70, #71, #72, #73, #74, #75, #76, #77, #78
This commit is contained in:
nessar committed 2026-10-10 16:07:34 +02:00
1 parent 209bf05f36
commit 6811142299
45 files changed
+8795 -3075

No files matched your search

+54 -2
View File
@@ -133,6 +133,51 @@
"after": "Après",
"around": "Autour",
"auto": "Auto (Meilleur choix)"
},
"optimization": {
"newRule": "Nouvelle règle d’Optimisation",
"defaultName": "Prévention des pertes",
"elementsLabel": "Éléments du planning",
"conditionsLabel": "Conditions (toutes doivent être vérifiées)",
"measureLabel": "Mesure",
"aggregationLabel": "Agrégation",
"directionLabel": "Direction",
"thresholdLabel": "Seuil de préférence en jours ouvrés",
"thresholdSummary": "seuil de préférence : {count} jours ouvrés",
"summary": "{direction} {aggregation} : {measure} — {elements}{conditions}.",
"invalidConfiguration": "Choisissez des éléments, conditions, une mesure et une agrégation compatibles, avec un seuil entier positif si nécessaire.",
"nameRequired": "Saisissez un nom de règle.",
"elements": {
"leave-balances": "Soldes de congés",
"public-holiday-weeks": "Semaines avec jour férié",
"time-off-periods": "Périodes non travaillées",
"work-sequences": "Séquences de travail",
"leave-placements": "Congés placés"
},
"conditions": {
"at-risk": "À risque d’expiration",
"complete": "Complètes",
"internal": "Internes",
"non-empty": "Non vides",
"supporting": "D’appui"
},
"measures": {
"unplaced-quantity": "Quantité non placée",
"public-holiday-week-value": "Valeur des semaines fériées",
"non-working-day-gain": "Gain de jours non travaillés",
"work-sequence-length": "Longueur des séquences de travail",
"work-sequence-deficit": "Déficit des séquences de travail",
"placement-count": "Nombre de placements"
},
"aggregations": {
"sum": "Somme",
"count": "Nombre",
"maximum": "Maximum"
},
"directions": {
"minimize": "Minimiser",
"maximize": "Maximiser"
}
}
},
"planning": {
@@ -276,7 +321,7 @@
"withoutDay": "Sans ce jour",
"withDay": "Avec ce jour",
"noChange": "Ces totaux sont identiques. Les règles et les derniers critères de départage (expiration du solde, puis date) peuvent encore influencer le choix.",
"priorityHelp": "Les critères sont classés par priorité. La première différence prime sur les suivantes.",
"priorityHelp": "Les obligations sont résolues conjointement. Les préférences actives sont comparées dans l’ordre de vos règles. Seules ces règles déclenchent des placements ; l’expiration puis les dates départagent les résultats égaux.",
"tryDate": "Essayer une autre date",
"plannedDate": "Date actuellement prévue :",
"currentPlan": "Planning actuel",
@@ -327,6 +372,10 @@
"placement-date": {
"label": "Dates les plus proches d’abord",
"help": "Dernier départage lorsque tous les autres critères sont égaux."
},
"optimization": {
"label": "{rule}: {summary}",
"help": "Cette préférence d’optimisation évalue sa configuration enregistrée. Les préférences actives suivent l’ordre affiché ; les premières passent avant les suivantes."
}
},
"stages": {
@@ -342,7 +391,9 @@
"unavailable": "Ce solde de congés ne peut pas être utilisé à cette date.",
"expired": "Cette date dépasse la date d’expiration du solde.",
"type-incompatible": "Ce type de congé ne peut pas être utilisé à cette date.",
"rule-invalid": "Utiliser cette date ne respecterait pas une règle du planning."
"rule-invalid": "Utiliser cette date ne respecterait pas une règle du planning.",
"invalid-optimization-configuration": "Choisissez des éléments, conditions, une mesure et une agrégation compatibles, avec un seuil entier positif si nécessaire.",
"unsupported-optimization-configuration": "Cette règle combine des éléments du planning et des mesures incompatibles."
},
"preferenceHelp": {
"time_window": "Jours de congé dans cette période souhaitée. Plus, c’est mieux.",
@@ -373,6 +424,7 @@
"no-working-date": "Aucun jour ouvré ne se trouve à la fois dans la période planifiée et entre ses dates de disponibilité et d’expiration.",
"all-dates-occupied": "Tous les jours ouvrés où ce solde est utilisable sont déjà occupés par des congés calculés ou des jours déjà posés.",
"preserved": "Conservé pour plus tard : il expire après la période planifiée et le calcul n’a pas retenu de pose supplémentaire selon vos priorités.",
"active-priorities": "Le calcul a conservé ce solde inutilisé selon vos règles actives. Modifiez les règles ou leur ordre pour demander un autre planning.",
"calculation-incomplete": "Le calcul a été interrompu avant de terminer la planification. Sa non-utilisation n’est pas une décision définitive.",
"not-established": "La raison précise de sa non-utilisation n’a pas été établie par ce calcul."
}