gmao/app_new/room_profiles/routes.py
root 31434c5ca2
Some checks are pending
CI - Tests et Syntax / lint-and-test (push) Waiting to run
Validate checkpoint B UI workflows
2026-08-23 18:10:40 +00:00

182 lines
8.6 KiB
Python

from flask import Blueprint, render_template, request, redirect, url_for, flash
from flask_login import login_required
from app_new.extensions import db
from app_new.core.models.college import RoomProfile, RoomProfileItem, RoomType, Room, RoomSurface
from app_new.core.models.equipment import EquipmentCategory, Equipment
from app_new.core.models.maintenance import Lot
from app_new.core.services.equipment_creation import create_equipment
room_profiles_bp = Blueprint("room_profiles", __name__, template_folder="templates")
def _context(profile=None):
return {
"profile": profile,
"room_types": RoomType.query.order_by(RoomType.name).all(),
"categories": EquipmentCategory.query.order_by(EquipmentCategory.name).all(),
"lots": Lot.query.order_by(Lot.name).all(),
}
@room_profiles_bp.route("/")
@login_required
def index():
return render_template("room_profiles/index.html", profiles=RoomProfile.query.order_by(RoomProfile.name).all())
@room_profiles_bp.route("/new", methods=["GET", "POST"])
@login_required
def new():
if request.method == "POST":
name = (request.form.get("name") or "").strip()
if not name:
flash("Le nom du profil est obligatoire.", "danger")
return render_template("room_profiles/form.html", title="Nouveau profil", **_context()), 400
if RoomProfile.query.filter(db.func.lower(RoomProfile.name) == name.lower()).first():
flash("Un profil porte déjà ce nom.", "danger")
return render_template("room_profiles/form.html", title="Nouveau profil", **_context()), 400
profile = RoomProfile(
name=name,
description=(request.form.get("description") or "").strip() or None,
room_type_id=request.form.get("room_type_id", type=int) or None,
)
db.session.add(profile)
db.session.commit()
flash("Profil créé. Ajoutez ses propositions puis prévisualisez avant toute création.", "success")
return redirect(url_for("room_profiles.edit", id=profile.id))
return render_template("room_profiles/form.html", title="Nouveau profil", **_context())
@room_profiles_bp.route("/<int:id>/edit", methods=["GET", "POST"])
@login_required
def edit(id):
profile = RoomProfile.query.get_or_404(id)
if request.method == "POST":
profile.name = (request.form.get("name") or "").strip()
profile.description = (request.form.get("description") or "").strip() or None
profile.room_type_id = request.form.get("room_type_id", type=int) or None
if not profile.name:
flash("Le nom du profil est obligatoire.", "danger")
return render_template("room_profiles/form.html", title="Modifier le profil", **_context(profile)), 400
db.session.commit()
flash("Profil mis à jour.", "success")
return redirect(url_for("room_profiles.edit", id=profile.id))
return render_template("room_profiles/form.html", title="Modifier le profil", **_context(profile))
@room_profiles_bp.route("/<int:id>/items", methods=["POST"])
@login_required
def add_item(id):
profile = RoomProfile.query.get_or_404(id)
label = (request.form.get("label") or "").strip()
quantity = request.form.get("quantity", 1, type=int)
if not label or not quantity or quantity < 1:
flash("Libellé et quantité positive sont obligatoires.", "danger")
return redirect(url_for("room_profiles.edit", id=id))
item = RoomProfileItem(
profile_id=profile.id,
label=label,
category_id=request.form.get("category_id", type=int) or None,
lot_id=request.form.get("lot_id", type=int) or None,
quantity=quantity,
is_group=request.form.get("is_group") == "1",
enabled_by_default=request.form.get("enabled_by_default") == "1",
sort_order=request.form.get("sort_order", 0, type=int),
)
db.session.add(item)
db.session.commit()
flash("Proposition ajoutée au profil.", "success")
return redirect(url_for("room_profiles.edit", id=id))
@room_profiles_bp.route("/<int:profile_id>/items/<int:item_id>/delete", methods=["POST"])
@login_required
def delete_item(profile_id, item_id):
item = RoomProfileItem.query.filter_by(id=item_id, profile_id=profile_id).first_or_404()
db.session.delete(item)
db.session.commit()
flash("Proposition retirée du profil.", "success")
return redirect(url_for("room_profiles.edit", id=profile_id))
@room_profiles_bp.route("/<int:profile_id>/items/<int:item_id>/edit", methods=["POST"])
@login_required
def edit_item(profile_id, item_id):
item = RoomProfileItem.query.filter_by(id=item_id, profile_id=profile_id).first_or_404()
label = (request.form.get("label") or item.label).strip()
quantity = request.form.get("quantity", item.quantity, type=int)
if not label or not quantity or quantity < 1:
flash("Libellé et quantité positive sont obligatoires.", "danger")
return redirect(url_for("room_profiles.edit", id=profile_id))
item.label = label
item.quantity = quantity
item.category_id = request.form.get("category_id", type=int) or None
item.lot_id = request.form.get("lot_id", type=int) or None
item.is_group = request.form.get("is_group") == "1"
item.enabled_by_default = request.form.get("enabled_by_default") == "1"
db.session.commit()
flash("Proposition du profil mise à jour. Les locaux déjà appliqués restent inchangés.", "success")
return redirect(url_for("room_profiles.edit", id=profile_id))
@room_profiles_bp.route("/<int:id>/preview", methods=["GET", "POST"])
@login_required
def preview(id):
profile = RoomProfile.query.get(id)
if profile is None:
from flask import abort
abort(404)
room_ids = request.form.getlist("room_ids", type=int) if request.method == "POST" else request.args.getlist("room_id", type=int)
rooms = Room.query.filter(Room.id.in_(room_ids)).order_by(Room.name).all() if room_ids else []
items = [item for item in profile.items if item.enabled_by_default]
existing = {(room.id, item.id): sum((equipment.quantity or 1) for equipment in Equipment.query.filter_by(
room_id=room.id, room_profile_item_id=item.id, is_deleted=False).all())
for room in rooms for item in items}
if request.method == "POST" and request.form.get("validate") == "1":
selected = {int(value) for value in request.form.getlist("item_ids") if value.isdigit()}
if not rooms or not selected:
flash("Sélectionnez au moins un local et une proposition.", "danger")
return render_template("room_profiles/preview.html", profile=profile, rooms=rooms, items=items, existing=existing)
created = 0
for room in rooms:
for item in items:
if item.id not in selected:
continue
quantity = request.form.get(f"quantity_{item.id}", item.quantity, type=int)
if quantity < 1:
continue
quantity = max(quantity - existing.get((room.id, item.id), 0), 0)
if not quantity:
continue
create_equipment(
name=item.label,
category_id=item.category_id,
lot_id=item.lot_id,
room_id=room.id,
quantity=quantity,
is_group=item.is_group,
room_profile_item_id=item.id,
)
created += 1
db.session.commit()
flash(f"{created} proposition(s) appliquée(s). Les équipements ont été créés via le service commun.", "success")
return redirect(url_for("rooms.index"))
return render_template("room_profiles/preview.html", profile=profile, rooms=rooms, items=items, existing=existing)
@room_profiles_bp.route("/<int:id>/surfaces", methods=["POST"])
@login_required
def add_surface(id):
profile = RoomProfile.query.get_or_404(id)
surface_type = request.form.get("surface_type")
material = (request.form.get("material") or "").strip()
if surface_type not in {"mur", "sol", "plafond", "autre"} or not material:
flash("Type et matériau de surface obligatoires.", "danger")
return redirect(url_for("room_profiles.edit", id=id))
db.session.add(RoomSurface(profile_id=profile.id, surface_type=surface_type, material=material,
finish=(request.form.get("finish") or "").strip() or None,
label=(request.form.get("label") or "").strip() or None))
db.session.commit()
flash("Composition proposée ajoutée.", "success")
return redirect(url_for("room_profiles.edit", id=id))