From fe7deff50416af338dcaaa5b6713804729459a48 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 21 Aug 2026 10:08:21 +0000 Subject: [PATCH] =?UTF-8?q?Ajouter=20le=20refus=20motiv=C3=A9=20des=20inte?= =?UTF-8?q?rventions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app_new/constants.py | 14 ++++++----- app_new/core/models/maintenance.py | 6 +++++ app_new/interventions/crud.py | 23 +++++++++++++++++++ app_new/interventions/templates/detail.html | 19 +++++++++++++++ .../fe5f6a7b8c9d_add_intervention_refusal.py | 20 ++++++++++++++++ 5 files changed, 76 insertions(+), 6 deletions(-) create mode 100644 migrations/versions/fe5f6a7b8c9d_add_intervention_refusal.py diff --git a/app_new/constants.py b/app_new/constants.py index deb3f8b..39a8770 100644 --- a/app_new/constants.py +++ b/app_new/constants.py @@ -15,6 +15,7 @@ INTERVENTION_STATUSES = { "terminee": {"label": "Terminée", "color": "success", "icon": "check-circle"}, "cloturee": {"label": "Clôturée", "color": "dark", "icon": "archive"}, "annulee": {"label": "Annulée", "color": "danger", "icon": "times-circle"}, + "refusee": {"label": "Refusée", "color": "danger", "icon": "ban"}, "reportee": {"label": "Reportée", "color": "info", "icon": "calendar-alt"}, "ignoree": {"label": "Ignorée", "color": "secondary", "icon": "dash-circle"}, # Étapes détaillées d'une demande de travaux (dans la fiche intervention) @@ -31,13 +32,13 @@ INTERVENTION_STATUSES = { } INTERVENTION_TRANSITIONS = { - "brouillon": {"en_attente", "planifiee", "attente_chef", "demande_departement", "attente_devis", "en_cours", "annulee"}, - "en_attente": {"planifiee", "en_cours", "annulee"}, - "planifiee": {"en_cours", "reportee", "annulee", "ignoree"}, + "brouillon": {"en_attente", "planifiee", "attente_chef", "demande_departement", "attente_devis", "en_cours", "annulee", "refusee"}, + "en_attente": {"planifiee", "en_cours", "annulee", "refusee"}, + "planifiee": {"en_cours", "reportee", "annulee", "ignoree", "refusee"}, "reportee": {"planifiee", "annulee"}, - "en_cours": {"en_attente_pieces", "en_attente_entreprise", "terminee", "annulee"}, - "en_attente_pieces": {"en_cours", "annulee"}, - "en_attente_entreprise": {"en_cours", "annulee"}, + "en_cours": {"en_attente_pieces", "en_attente_entreprise", "terminee", "annulee", "refusee"}, + "en_attente_pieces": {"en_cours", "annulee", "refusee"}, + "en_attente_entreprise": {"en_cours", "annulee", "refusee"}, "terminee": {"cloturee", "en_cours"}, "cloturee": set(), "annulee": {"brouillon"}, @@ -52,6 +53,7 @@ INTERVENTION_TRANSITIONS = { "travaux_en_cours": {"travaux_termines", "en_attente_pieces", "annulee"}, "travaux_termines": {"reception", "cloturee"}, "reception": {"cloturee", "travaux_en_cours"}, + "refusee": {"brouillon"}, } # Statuts équipements diff --git a/app_new/core/models/maintenance.py b/app_new/core/models/maintenance.py index 194002c..bef4ed9 100644 --- a/app_new/core/models/maintenance.py +++ b/app_new/core/models/maintenance.py @@ -93,6 +93,9 @@ class Intervention(db.Model): is_deleted = db.Column(db.Boolean, default=False) deleted_at = db.Column(db.DateTime, nullable=True) deleted_by_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=True) + refusal_reason = db.Column(db.Text, nullable=True) + refused_at = db.Column(db.DateTime, nullable=True) + refused_by_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=True) # ENT ent_message_id = db.Column(db.String(100), nullable=True) @@ -137,6 +140,7 @@ class Intervention(db.Model): technician = db.relationship("User", foreign_keys=[technician_id]) author = db.relationship("User", foreign_keys=[author_id]) deleted_by = db.relationship("User", foreign_keys=[deleted_by_id]) + refused_by = db.relationship("User", foreign_keys=[refused_by_id]) # Lot relation lot = db.relationship("Lot", backref="interventions_list") @@ -160,6 +164,7 @@ class Intervention(db.Model): 'cloturee': 'dark', 'annulee': 'danger', 'ignoree': 'secondary', + 'refusee': 'danger', 'attente_chef': 'warning', 'demande_departement': 'info', 'attente_devis': 'orange', 'devis_a_valider': 'warning', 'devis_accepte': 'success', 'entreprise_mandatee': 'info', @@ -182,6 +187,7 @@ class Intervention(db.Model): 'cloturee': 'Cloturee', 'annulee': 'Annulee', 'ignoree': 'Ignoree', + 'refusee': 'Refusée', 'attente_chef': 'Validation du chef', 'demande_departement': 'Demande au Département', 'attente_devis': 'Devis attendu', 'devis_a_valider': 'Devis à valider', 'devis_accepte': 'Devis accepté', 'entreprise_mandatee': 'Entreprise mandatée', diff --git a/app_new/interventions/crud.py b/app_new/interventions/crud.py index 465dde1..bdae491 100644 --- a/app_new/interventions/crud.py +++ b/app_new/interventions/crud.py @@ -549,6 +549,29 @@ def change_status(id): return redirect(url_for('interventions.detail', id=intervention.id)) +@interventions_bp.route('//refuse', methods=['POST']) +@login_required +def refuse(id): + """Refuse une demande avec un motif obligatoire.""" + intervention = Intervention.query.get_or_404(id) + reason = (request.form.get('refusal_reason') or '').strip() + if not reason: + flash('Le motif du refus est obligatoire.', 'danger') + return redirect(url_for('interventions.detail', id=id)) + if intervention.status in {'terminee', 'cloturee', 'refusee'}: + flash('Cette intervention ne peut plus être refusée depuis son statut actuel.', 'danger') + return redirect(url_for('interventions.detail', id=id)) + old_status = intervention.status + intervention.status = 'refusee' + intervention.refusal_reason = reason + intervention.refused_at = datetime.now(timezone.utc) + intervention.refused_by_id = current_user.id + db.session.add(StatusChange(intervention_id=id, from_status=old_status, to_status='refusee', changed_by_id=current_user.id, comment=reason)) + db.session.commit() + flash('Intervention refusée et motif enregistré.', 'success') + return redirect(url_for('interventions.detail', id=id)) + + @interventions_bp.route('//comment', methods=['POST']) @login_required def add_comment(id): diff --git a/app_new/interventions/templates/detail.html b/app_new/interventions/templates/detail.html index 33354f8..ba48fb1 100644 --- a/app_new/interventions/templates/detail.html +++ b/app_new/interventions/templates/detail.html @@ -102,6 +102,17 @@ + + {% if intervention.status not in ['terminee', 'cloturee', 'refusee', 'annulee'] %} +
+
+ +
+ + +
+
+ {% endif %} {% if intervention.equipment and intervention.equipment.status not in ['a_jeter', 'jete'] %}
@@ -116,6 +127,14 @@ {% endif %} + + {% if intervention.status == 'refusee' %} +
+
Intervention refusée
+
{{ intervention.refusal_reason or 'Motif non renseigné' }}
+ Le {{ intervention.refused_at|datetime_fmt if intervention.refused_at else '—' }}{% if intervention.refused_by %} par {{ intervention.refused_by.full_name }}{% endif %} +
+ {% endif %} {% else %} diff --git a/migrations/versions/fe5f6a7b8c9d_add_intervention_refusal.py b/migrations/versions/fe5f6a7b8c9d_add_intervention_refusal.py new file mode 100644 index 0000000..a8540ca --- /dev/null +++ b/migrations/versions/fe5f6a7b8c9d_add_intervention_refusal.py @@ -0,0 +1,20 @@ +"""Ajoute le refus motivé des interventions.""" +from alembic import op +import sqlalchemy as sa + +revision = "fe5f6a7b8c9d" +down_revision = "fd4e5f6a7b8c" +branch_labels = None +depends_on = None + + +def upgrade(): + op.add_column("interventions", sa.Column("refusal_reason", sa.Text(), nullable=True)) + op.add_column("interventions", sa.Column("refused_at", sa.DateTime(), nullable=True)) + op.add_column("interventions", sa.Column("refused_by_id", sa.Integer(), sa.ForeignKey("users.id"), nullable=True)) + + +def downgrade(): + op.drop_column("interventions", "refused_by_id") + op.drop_column("interventions", "refused_at") + op.drop_column("interventions", "refusal_reason")