diff --git a/app_new/core/preventive_duration_proposals.py b/app_new/core/preventive_duration_proposals.py new file mode 100644 index 0000000..0d7ec44 --- /dev/null +++ b/app_new/core/preventive_duration_proposals.py @@ -0,0 +1,27 @@ +"""Valeurs initiales de durée unitaire issues de la proposition métier. + +Ces valeurs sont des suggestions modifiables, jamais des durées +réglementaires. Elles ne sont utilisées que lors de la création d'une +nouvelle ligne de catalogue dont la durée est encore vide. +""" + +PROPOSED_UNIT_DURATIONS = { + 114: 2, 119: 2, 120: 2, 125: 5, 126: 5, 127: 10, 128: 5, + 231: 5, 236: 10, 239: 2, 240: 5, 254: 5, 255: 5, 251: 5, + 252: 10, 253: 10, 243: 10, 242: 2, 258: 5, 259: 5, 261: 5, + 245: 5, 246: 5, 247: 5, 1: 2, 3: 5, 2: 2, 5: 2, 7: 10, + 9: 2, 6: 2, 17: 10, 18: 2, 24: 2, 25: 5, 26: 5, 19: 2, + 20: 5, 31: 10, 32: 5, 75: 2, 76: 2, 78: 5, 79: 10, 82: 5, + 86: 5, 87: 10, 91: 5, 92: 10, 93: 10, 95: 10, 96: 10, + 98: 10, 99: 5, 109: 5, 112: 5, 158: 5, 154: 10, 160: 5, + 182: 5, 186: 10, 190: 5, 193: 5, 197: 5, 198: 5, 199: 5, + 181: 2, 180: 5, 203: 10, 205: 10, 209: 2, 210: 5, 211: 10, + 214: 5, 216: 5, 218: 2, 219: 5, 226: 5, 227: 5, 228: 5, + 215: 2, 224: 5, 263: 5, 264: 10, 268: 10, 269: 5, 271: 2, + 272: 2, 282: 2, 283: 10, 285: 5, 291: 10, 289: 5, 295: 5, + 296: 5, 299: 5, 300: 5, 319: 10, 320: 5, 328: 10, 35: 2, + 36: 5, 39: 10, 38: 5, 37: 10, 138: 2, 139: 2, 329: 10, + 41: 2, 42: 10, 43: 5, 44: 5, 45: 5, 46: 5, 49: 2, 50: 5, + 51: 5, 54: 5, 53: 2, 60: 5, 61: 5, 62: 5, 63: 10, 56: 10, + 57: 10, 11: 2, 12: 5, 13: 5, 14: 2, 15: 5, 70: 2, 71: 5, +} diff --git a/app_new/core/routes/setup_wizard.py b/app_new/core/routes/setup_wizard.py index 73e5130..c364d0a 100644 --- a/app_new/core/routes/setup_wizard.py +++ b/app_new/core/routes/setup_wizard.py @@ -472,6 +472,7 @@ def api_save_step(step): # ligne déjà présente n'est jamais écrasée : elle peut avoir été # adaptée par l'établissement depuis le dernier import. task_count = 0 + from ..preventive_duration_proposals import PROPOSED_UNIT_DURATIONS for item in LOT_TASKS: lot = lots_by_name.get(item['lot'].casefold()) if lot is None: @@ -493,6 +494,14 @@ def api_save_step(step): ): if field in item: setattr(task, field, item[field]) + # Les propositions sont uniquement des valeurs initiales pour + # une nouvelle ligne. Une ligne existante/personnalisée n'est + # jamais écrasée par une réexécution du wizard. + if not task.duree_minutes and task.num_tache: + try: + task.duree_minutes = PROPOSED_UNIT_DURATIONS.get(int(task.num_tache)) + except (TypeError, ValueError): + task.duree_minutes = None db.session.add(task) task_count += 1 diff --git a/app_new/core/services/maintenance_engine.py b/app_new/core/services/maintenance_engine.py index b96f1d7..51901b6 100644 --- a/app_new/core/services/maintenance_engine.py +++ b/app_new/core/services/maintenance_engine.py @@ -127,7 +127,7 @@ def generate_due_tasks(task: LotTask, event=None, today=None, equipment_ids=None existing = ScheduledTask.query.filter( ScheduledTask.lot_task_id == task.id, ScheduledTask.equipment_id == equipment.id, - ScheduledTask.status.in_(['planned', 'in_progress', 'suspended']), + ScheduledTask.status.in_(['planned', 'in_progress', 'suspended', 'needs_duration']), ).first() if existing: continue diff --git a/app_new/templates/lots/detail.html b/app_new/templates/lots/detail.html index fe130d3..5b75a55 100644 --- a/app_new/templates/lots/detail.html +++ b/app_new/templates/lots/detail.html @@ -121,7 +121,7 @@ {% if lot.tasks %}
| N° | Tâche | Exécutant | Périodicité / durée | Périmètre | |
|---|---|---|---|---|---|
| {{ task.num_tache or '—' }} | {{ task.tache or 'Tâche sans nom' }} {{ task.trigger_type|replace('calendar','calendrier')|replace('season','saison')|replace('event','événement')|replace('weather','météo')|replace('meter','compteur')|title }}{% if task.is_safety_critical %} · sécurité{% endif %} | {{ {'internal':'Interne','external_company':'Entreprise extérieure','department':'Département','mixed':'Mixte','undefined':'À définir'}.get(task.executor_type, 'À définir') }}{% if task.company %} {{ task.company.name }} {% endif %} | {{ task.periodicite or (task.jours_entre_interventions|string + ' jours' if task.jours_entre_interventions else 'Périodicité à définir') }} {% if task.duration_is_configured %}{{ task.duree_minutes }} min / élément{% else %}Durée à renseigner{% endif %} | {{ 'Sélection manuelle (%d)'|format(task.equipment_scope|length) if task.scope_mode == 'manual' else 'Dynamique : équipements du lot' }} | |
| {{ task.num_tache or '—' }} | {{ task.tache or 'Tâche sans nom' }} {{ task.trigger_type|replace('calendar','calendrier')|replace('season','saison')|replace('event','événement')|replace('weather','météo')|replace('meter','compteur')|title }}{% if task.is_safety_critical %} · sécurité{% endif %} | {{ {'internal':'Interne','external_company':'Entreprise extérieure','department':'Département','mixed':'Mixte','undefined':'À définir'}.get(task.executor_type, 'À définir') }}{% if task.company %} {{ task.company.name }} {% endif %} | {{ task.periodicite or (task.jours_entre_interventions|string + ' jours' if task.jours_entre_interventions else 'Périodicité à définir') }} {% if task.duration_is_configured %}{{ task.duree_minutes }} min / élément{% else %}Durée à renseigner{% endif %} | {{ 'Sélection manuelle (%d)'|format(task.equipment_scope|length) if task.scope_mode == 'manual' else 'Dynamique : équipements du lot' }} |