2026-08-14 18:02:25 +02:00
|
|
|
"""Planning Admin Rules Routes - GMAO Collège"""
|
|
|
|
|
from flask import render_template, request, redirect, url_for, flash
|
|
|
|
|
from flask_login import login_required, current_user
|
|
|
|
|
from ..extensions import db
|
|
|
|
|
from ..core.models.planning import (
|
|
|
|
|
PreventiveTask, PreventiveTaskConsumable, ScheduledTask,
|
|
|
|
|
Meter, MeterReading, Consumable, ConsumableUsage,
|
|
|
|
|
TechnicianAvailability, AdminTask, ZoneAccessRule,
|
|
|
|
|
WorkSchedule, CollegeClosure, PlanningDay, PlanningItem
|
|
|
|
|
)
|
|
|
|
|
from ..core.models.maintenance import Lot, Intervention
|
|
|
|
|
from ..core.models.equipment import EquipmentCategory
|
|
|
|
|
from ..core.models.company import Company
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
|
|
|
|
from .schedules import planning_bp
|
|
|
|
|
|
|
|
|
|
@planning_bp.route('/admin-tasks')
|
|
|
|
|
@login_required
|
|
|
|
|
def admin_tasks():
|
|
|
|
|
"""Tâches administratives."""
|
|
|
|
|
tasks = AdminTask.query.order_by(AdminTask.frequency, AdminTask.start_time).all()
|
|
|
|
|
return render_template('planning/admin_tasks.html', tasks=tasks)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@planning_bp.route('/admin-tasks/new', methods=['GET', 'POST'])
|
|
|
|
|
@login_required
|
|
|
|
|
def new_admin_task():
|
|
|
|
|
"""Créer une tâche administrative."""
|
|
|
|
|
if request.method == 'POST':
|
|
|
|
|
task = AdminTask(
|
|
|
|
|
name=request.form.get('name'),
|
|
|
|
|
description=request.form.get('description'),
|
|
|
|
|
frequency=request.form.get('frequency', 'daily'),
|
|
|
|
|
day_of_week=request.form.get('day_of_week', type=int),
|
|
|
|
|
day_of_month=request.form.get('day_of_month', type=int),
|
|
|
|
|
start_time=datetime.strptime(request.form.get('start_time'), '%H:%M').time() if request.form.get('start_time') else None,
|
|
|
|
|
duration_minutes=request.form.get('duration_minutes', type=int, default=30),
|
|
|
|
|
is_active=True
|
|
|
|
|
)
|
|
|
|
|
db.session.add(task)
|
|
|
|
|
db.session.commit()
|
|
|
|
|
flash('Tâche administrative créée', 'success')
|
|
|
|
|
return redirect(url_for('planning.admin_tasks'))
|
|
|
|
|
|
|
|
|
|
return render_template('planning/admin_task_form.html',
|
|
|
|
|
title='Nouvelle tâche administrative',
|
|
|
|
|
task=None)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@planning_bp.route('/admin-tasks/<int:id>/edit', methods=['GET', 'POST'])
|
|
|
|
|
@login_required
|
|
|
|
|
def edit_admin_task(id):
|
|
|
|
|
"""Modifier une tâche administrative."""
|
|
|
|
|
task = AdminTask.query.get_or_404(id)
|
|
|
|
|
|
|
|
|
|
if request.method == 'POST':
|
|
|
|
|
task.name = request.form.get('name')
|
|
|
|
|
task.description = request.form.get('description')
|
|
|
|
|
task.frequency = request.form.get('frequency', 'daily')
|
|
|
|
|
task.day_of_week = request.form.get('day_of_week', type=int)
|
|
|
|
|
task.day_of_month = request.form.get('day_of_month', type=int)
|
|
|
|
|
task.start_time = datetime.strptime(request.form.get('start_time'), '%H:%M').time() if request.form.get('start_time') else None
|
|
|
|
|
task.duration_minutes = request.form.get('duration_minutes', type=int)
|
|
|
|
|
task.is_active = request.form.get('is_active') == 'on'
|
|
|
|
|
db.session.commit()
|
|
|
|
|
flash('Tâche administrative mise à jour', 'success')
|
|
|
|
|
return redirect(url_for('planning.admin_tasks'))
|
|
|
|
|
|
|
|
|
|
return render_template('planning/admin_task_form.html',
|
|
|
|
|
title='Modifier la tâche administrative',
|
|
|
|
|
task=task)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@planning_bp.route('/admin-tasks/<int:id>/delete', methods=['POST'])
|
|
|
|
|
@login_required
|
|
|
|
|
def delete_admin_task(id):
|
|
|
|
|
"""Supprimer une tâche administrative."""
|
|
|
|
|
task = AdminTask.query.get_or_404(id)
|
|
|
|
|
db.session.delete(task)
|
|
|
|
|
db.session.commit()
|
|
|
|
|
flash('Tâche administrative supprimée', 'success')
|
|
|
|
|
return redirect(url_for('planning.admin_tasks'))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@planning_bp.route('/rules')
|
|
|
|
|
@login_required
|
|
|
|
|
def rules():
|
|
|
|
|
"""Règles d'accès aux zones."""
|
|
|
|
|
rules = ZoneAccessRule.query.all()
|
|
|
|
|
from ..core.models.college import Zone
|
|
|
|
|
zones = Zone.query.order_by(Zone.name).all()
|
|
|
|
|
return render_template('planning/rules.html', rules=rules, zones=zones)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@planning_bp.route('/rules/add', methods=['POST'])
|
|
|
|
|
@login_required
|
|
|
|
|
def add_rule():
|
|
|
|
|
"""Ajouter une règle d'accès."""
|
|
|
|
|
zone_id = request.form.get('zone_id', type=int)
|
|
|
|
|
rule_type = request.form.get('rule_type', '')
|
|
|
|
|
rule_value = request.form.get('rule_value', '')
|
|
|
|
|
|
|
|
|
|
if zone_id and rule_type and rule_value:
|
|
|
|
|
rule = ZoneAccessRule(zone_id=zone_id, rule_type=rule_type, rule_value=rule_value)
|
|
|
|
|
db.session.add(rule)
|
|
|
|
|
db.session.commit()
|
|
|
|
|
flash('Règle ajoutée avec succès.', 'success')
|
|
|
|
|
else:
|
|
|
|
|
flash('Veuillez remplir tous les champs.', 'error')
|
|
|
|
|
|
|
|
|
|
return redirect(url_for('planning.rules'))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@planning_bp.route('/rules/<int:id>/delete', methods=['POST'])
|
|
|
|
|
@login_required
|
|
|
|
|
def delete_rule(id):
|
|
|
|
|
"""Supprimer une règle d'accès."""
|
|
|
|
|
rule = ZoneAccessRule.query.get_or_404(id)
|
|
|
|
|
db.session.delete(rule)
|
|
|
|
|
db.session.commit()
|
|
|
|
|
flash('Règle supprimée.', 'success')
|
|
|
|
|
return redirect(url_for('planning.rules'))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@planning_bp.route('/help')
|
|
|
|
|
@login_required
|
|
|
|
|
def help_page():
|
|
|
|
|
"""Page d'aide sur le système de planification."""
|
|
|
|
|
return render_template('planning/help.html')
|
|
|
|
|
|
|
|
|
|
@planning_bp.route('/day/<date>/create-interventions', methods=['POST'])
|
|
|
|
|
@login_required
|
|
|
|
|
def create_interventions_for_day(date):
|
|
|
|
|
"""Créer les interventions à partir des tâches planifiées d'une journée."""
|
|
|
|
|
from datetime import datetime, date as date_type
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
target_date = datetime.strptime(date, '%Y-%m-%d').date()
|
|
|
|
|
except ValueError:
|
|
|
|
|
flash('Date invalide.', 'error')
|
|
|
|
|
return redirect(url_for('planning.index'))
|
|
|
|
|
|
|
|
|
|
# Récupérer les tâches planifiées pour cette journée (statut 'planned' ou 'scheduled', avec équipement)
|
|
|
|
|
tasks = db.session.query(ScheduledTask).filter(
|
|
|
|
|
ScheduledTask.equipment_id.isnot(None),
|
|
|
|
|
ScheduledTask.scheduled_date == target_date,
|
|
|
|
|
ScheduledTask.status.in_(['planned', 'scheduled'])
|
|
|
|
|
).all()
|
|
|
|
|
|
|
|
|
|
created = 0
|
|
|
|
|
for task in tasks:
|
|
|
|
|
# Vérifier si une intervention existe déjà pour cette tâche
|
|
|
|
|
existing = db.session.query(Intervention).filter(
|
|
|
|
|
Intervention.equipment_id == task.equipment_id,
|
|
|
|
|
Intervention.scheduled_date == task.scheduled_date,
|
|
|
|
|
Intervention.scheduled_start == task.scheduled_start
|
|
|
|
|
).first()
|
|
|
|
|
|
|
|
|
|
if not existing:
|
|
|
|
|
intervention = Intervention(
|
|
|
|
|
title=f"Intervention: {task.preventive_task.name if task.preventive_task else (task.lot_task.tache if task.lot_task else 'Tâche planifiée')}",
|
|
|
|
|
description=task.notes or '',
|
|
|
|
|
equipment_id=task.equipment_id,
|
|
|
|
|
room_id=task.room_id,
|
|
|
|
|
scheduled_date=task.scheduled_date,
|
|
|
|
|
scheduled_start=task.scheduled_start,
|
|
|
|
|
scheduled_end=task.scheduled_end,
|
|
|
|
|
estimated_duration=task.estimated_duration or 30,
|
|
|
|
|
status='en_attente',
|
|
|
|
|
type='preventif',
|
2026-08-21 12:12:23 +02:00
|
|
|
workflow_type='preventive',
|
2026-08-14 18:02:25 +02:00
|
|
|
assigned_to_id=task.assigned_to_id,
|
|
|
|
|
company_id=task.company_id,
|
|
|
|
|
author_id=current_user.id,
|
|
|
|
|
requester_name='Système automatique'
|
|
|
|
|
)
|
|
|
|
|
db.session.add(intervention)
|
|
|
|
|
created += 1
|
|
|
|
|
|
|
|
|
|
db.session.commit()
|
|
|
|
|
flash(f'{created} interventions créées à partir des tâches du {date}', 'success')
|
2026-08-21 12:12:23 +02:00
|
|
|
return redirect(url_for('planning.day', date=date))
|