Rendre le workflow des interventions modifiable
Some checks are pending
CI - Tests et Syntax / lint-and-test (push) Waiting to run

This commit is contained in:
root 2026-08-20 22:53:04 +00:00
parent a29067284a
commit 7d64a29601
3 changed files with 40 additions and 14 deletions

View file

@ -404,19 +404,27 @@ def detail(id):
def save_work_details(id): def save_work_details(id):
"""Enregistre les informations de travaux depuis la fiche intervention.""" """Enregistre les informations de travaux depuis la fiche intervention."""
intervention = Intervention.query.get_or_404(id) intervention = Intervention.query.get_or_404(id)
intervention.execution_mode = request.form.get('execution_mode', 'interne') if 'execution_mode' in request.form:
service_id = request.form.get('department_service_id') intervention.execution_mode = request.form.get('execution_mode') or 'interne'
intervention.department_service_id = int(service_id) if service_id and service_id.isdigit() else None if 'department_service_id' in request.form:
company_id = request.form.get('company_id') service_id = request.form.get('department_service_id')
intervention.company_id = int(company_id) if company_id and company_id.isdigit() else None intervention.department_service_id = int(service_id) if service_id and service_id.isdigit() else None
intervention.quote_status = request.form.get('quote_status', 'non_requis') if 'company_id' in request.form:
intervention.quote_reference = request.form.get('quote_reference') or None company_id = request.form.get('company_id')
intervention.quote_amount_ht = float(request.form['quote_amount_ht']) if request.form.get('quote_amount_ht') else None intervention.company_id = int(company_id) if company_id and company_id.isdigit() else None
intervention.quote_amount_ttc = float(request.form['quote_amount_ttc']) if request.form.get('quote_amount_ttc') else None if 'quote_status' in request.form:
intervention.quote_status = request.form.get('quote_status') or 'non_requis'
if 'quote_reference' in request.form:
intervention.quote_reference = request.form.get('quote_reference') or None
for field in ('quote_amount_ht', 'quote_amount_ttc'):
if field in request.form:
setattr(intervention, field, float(request.form[field]) if request.form.get(field) else None)
for field in ('department_request_date', 'chief_validation_date', 'department_validation_date', 'quote_date', 'quote_valid_until'): for field in ('department_request_date', 'chief_validation_date', 'department_validation_date', 'quote_date', 'quote_valid_until'):
value = request.form.get(field) if field in request.form:
setattr(intervention, field, datetime.strptime(value, '%Y-%m-%d').date() if value else None) value = request.form.get(field)
intervention.work_notes = request.form.get('work_notes') or None setattr(intervention, field, datetime.strptime(value, '%Y-%m-%d').date() if value else None)
if 'work_notes' in request.form:
intervention.work_notes = request.form.get('work_notes') or None
db.session.commit() db.session.commit()
flash('Workflow travaux enregistré.', 'success') flash('Workflow travaux enregistré.', 'success')
return redirect(url_for('interventions.detail', id=id)) return redirect(url_for('interventions.detail', id=id))
@ -499,7 +507,9 @@ def change_status(id):
"""Changer le statut d'une intervention.""" """Changer le statut d'une intervention."""
intervention = Intervention.query.get_or_404(id) intervention = Intervention.query.get_or_404(id)
old_status = intervention.status old_status = intervention.status
new_status = request.form.get('status') # Les anciennes fiches utilisent ``to_status`` ; accepter aussi ``status``
# pour les formulaires d'édition et conserver une transition réellement modifiable.
new_status = request.form.get('to_status') or request.form.get('status')
comment = request.form.get('comment', '') comment = request.form.get('comment', '')
allowed = INTERVENTION_TRANSITIONS.get(old_status, set()) allowed = INTERVENTION_TRANSITIONS.get(old_status, set())

View file

@ -51,6 +51,15 @@
<div class="card border-0 shadow-sm mb-3 mb-md-4 bg-white"> <div class="card border-0 shadow-sm mb-3 mb-md-4 bg-white">
<div class="card-body py-2 py-md-3"> <div class="card-body py-2 py-md-3">
<h6 class="text-muted mb-2" style="font-size:0.85rem;"><i class="bi bi-diagram-3"></i> Workflow</h6> <h6 class="text-muted mb-2" style="font-size:0.85rem;"><i class="bi bi-diagram-3"></i> Workflow</h6>
<form method="POST" action="{{ url_for('interventions.save_work_details', id=intervention.id) }}" class="d-flex flex-wrap gap-2 align-items-center mb-2">
<label class="small text-muted mb-0">Parcours :</label>
<select name="execution_mode" class="form-select form-select-sm" style="max-width:280px;">
{% for value, label in [('interne', 'Intervention interne'), ('chef', 'Validation du chef'), ('departement', 'Demande au Département'), ('entreprise', 'Entreprise extérieure')] %}
<option value="{{ value }}" {% if intervention.execution_mode == value %}selected{% endif %}>{{ label }}</option>
{% endfor %}
</select>
<button class="btn btn-outline-primary btn-sm" type="submit"><i class="bi bi-arrow-repeat"></i> Appliquer</button>
</form>
<div class="d-flex flex-wrap align-items-center gap-1" style="font-size:0.75rem;"> <div class="d-flex flex-wrap align-items-center gap-1" style="font-size:0.75rem;">
{% set workflow_order = ['brouillon', 'attente_chef', 'demande_departement', 'attente_devis', 'devis_a_valider', 'devis_accepte', 'entreprise_mandatee', 'travaux_planifies', 'travaux_en_cours', 'travaux_termines', 'reception', 'cloturee'] if intervention.execution_mode != 'interne' else ['brouillon', 'attente_chef', 'en_cours', 'en_attente_pieces', 'en_attente_entreprise', 'terminee', 'cloturee'] %} {% set workflow_order = ['brouillon', 'attente_chef', 'demande_departement', 'attente_devis', 'devis_a_valider', 'devis_accepte', 'entreprise_mandatee', 'travaux_planifies', 'travaux_en_cours', 'travaux_termines', 'reception', 'cloturee'] if intervention.execution_mode != 'interne' else ['brouillon', 'attente_chef', 'en_cours', 'en_attente_pieces', 'en_attente_entreprise', 'terminee', 'cloturee'] %}
{% for step in workflow_order %} {% for step in workflow_order %}

View file

@ -11,11 +11,15 @@ STATUSES = ["brouillon", "à_valider_chef", "travaux_internes_autorisés", "devi
@works_bp.route("/") @works_bp.route("/")
@login_required @login_required
def index(): def index():
return render_template("works/index.html", requests=WorkRequest.query.order_by(WorkRequest.created_at.desc()).all()) flash("Les demandes de travaux sont désormais gérées directement dans les interventions.", "info")
return redirect(url_for("interventions.index"))
@works_bp.route("/new", methods=["GET", "POST"]) @works_bp.route("/new", methods=["GET", "POST"])
@login_required @login_required
def new(): def new():
if request.method == "GET":
flash("Créez une intervention puis choisissez son workflow travaux dans sa fiche.", "info")
return redirect(url_for("interventions.create", workflow="travaux"))
if request.method == "POST": if request.method == "POST":
item = WorkRequest(title=request.form["title"].strip(), description=request.form["description"].strip(), execution_mode=request.form.get("execution_mode", "interne"), priority=request.form.get("priority", "normale"), equipment_id=request.form.get("equipment_id", type=int) or None, created_by_id=current_user.id, status="à_valider_chef") item = WorkRequest(title=request.form["title"].strip(), description=request.form["description"].strip(), execution_mode=request.form.get("execution_mode", "interne"), priority=request.form.get("priority", "normale"), equipment_id=request.form.get("equipment_id", type=int) or None, created_by_id=current_user.id, status="à_valider_chef")
db.session.add(item); db.session.flush(); db.session.add(WorkRequestEvent(request=item, user_id=current_user.id, new_status=item.status, comment="Demande créée")); db.session.commit(); flash("Demande de travaux créée.", "success"); return redirect(url_for("works.detail", id=item.id)) db.session.add(item); db.session.flush(); db.session.add(WorkRequestEvent(request=item, user_id=current_user.id, new_status=item.status, comment="Demande créée")); db.session.commit(); flash("Demande de travaux créée.", "success"); return redirect(url_for("works.detail", id=item.id))
@ -24,6 +28,9 @@ def new():
@works_bp.route("/<int:id>", methods=["GET", "POST"]) @works_bp.route("/<int:id>", methods=["GET", "POST"])
@login_required @login_required
def detail(id): def detail(id):
# Compatibilité avec les anciennes URLs : le nouveau suivi est dans l'intervention.
flash("Le suivi des travaux est désormais intégré aux interventions.", "info")
return redirect(url_for("interventions.index"))
item = WorkRequest.query.get_or_404(id) item = WorkRequest.query.get_or_404(id)
if request.method == "POST": if request.method == "POST":
old = item.status; item.status = request.form.get("status", old); item.notes = request.form.get("notes") or item.notes; item.company_id = request.form.get("company_id", type=int) or item.company_id; item.service_id = request.form.get("service_id", type=int) or item.service_id old = item.status; item.status = request.form.get("status", old); item.notes = request.form.get("notes") or item.notes; item.company_id = request.form.get("company_id", type=int) or item.company_id; item.service_id = request.form.get("service_id", type=int) or item.service_id