loko/streetup/contracts/views_teams.py
2026-07-22 14:48:40 +02:00

371 lines
13 KiB
Python

import json
from django.contrib.auth import get_user_model
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied
from django.http import JsonResponse
from django.shortcuts import get_object_or_404, render
from django.db.models import Count, Q
from django.views.decorators.http import require_GET, require_POST
from contracts.models import CompanyMember, CompanyTeam
from contracts.permissions import (
get_viewable_teams_for_user,
user_can_edit_specific_team,
user_can_edit_teams,
user_can_view_teams,
)
User = get_user_model()
def _get_user_company(user):
"""Retourne la Company associée à l'utilisateur, ou None."""
try:
return user.config.company
except AttributeError:
return None
def _is_admin(user):
try:
return user.config.roles.filter(name="admin").exists()
except AttributeError:
return False
@login_required
def teams_index(request):
"""Vue principale de gestion des équipes."""
if not user_can_view_teams(request.user):
raise PermissionDenied("Vous n'avez pas accès à la gestion des équipes.")
teams = (
get_viewable_teams_for_user(request.user)
.select_related("company")
.prefetch_related("members__user")
.annotate(
active_interventions_count=Count(
'assigned_interventions',
filter=Q(assigned_interventions__status_order__lte=51)
)
)
.order_by("company__name", "name")
)
can_edit = user_can_edit_teams(request.user)
user_company = _get_user_company(request.user)
is_admin_user = _is_admin(request.user)
# Les non-admins ne voient que les utilisateurs de leur propre société et des sociétés des équipes qu'ils ont le droit d'éditer
if is_admin_user or user_company is None:
available_users = User.objects.all().select_related("config__company").order_by("first_name", "last_name", "username")
else:
from contracts.models import UserCompanyTeamAccess
editable_team_companies = CompanyTeam.objects.filter(
user_accesses__user_config=request.user.config,
user_accesses__can_edit=True
).values_list('company_id', flat=True)
allowed_company_ids = set(editable_team_companies)
allowed_company_ids.add(user_company.id)
available_users = User.objects.filter(
config__company_id__in=allowed_company_ids
).select_related("config__company").order_by("first_name", "last_name", "username")
# Membres non assignés à une équipe
unassigned_qs = CompanyMember.objects.filter(teams__isnull=True).select_related("user")
if not is_admin_user and user_company is not None:
unassigned_qs = unassigned_qs.filter(user__config__company=user_company)
unassigned_members = unassigned_qs.order_by("name")
return render(
request,
"contracts/teams_index.html",
{
"teams": teams,
"can_edit": can_edit,
"available_users": available_users,
"unassigned_members": unassigned_members,
"user_company": user_company,
"is_admin": is_admin_user,
},
)
@login_required
@require_POST
def teams_move_member(request, member_id):
"""Déplacer un membre vers une autre équipe (AJAX)."""
try:
data = json.loads(request.body)
except (json.JSONDecodeError, ValueError):
return JsonResponse({"error": "JSON invalide"}, status=400)
# team_id peut être None / absent pour désassigner
raw_team_id = data.get("team_id")
new_team_id = int(raw_team_id) if raw_team_id is not None else None
# old_team_id represents the team column they are dragged out of
old_team_id = data.get("old_team_id")
old_team_id = int(old_team_id) if old_team_id is not None else None
member = get_object_or_404(CompanyMember, id=member_id)
viewable = get_viewable_teams_for_user(request.user)
# Vérifier l'accès à l'équipe source (si le membre était dans une équipe)
if old_team_id is not None:
if not viewable.filter(id=old_team_id).exists():
raise PermissionDenied("Accès refusé.")
if not user_can_edit_specific_team(request.user, old_team_id):
raise PermissionDenied("Droits d'édition insuffisants.")
else:
if not user_can_edit_teams(request.user):
raise PermissionDenied("Droits d'édition insuffisants.")
if new_team_id is not None:
new_team = get_object_or_404(CompanyTeam, id=new_team_id)
if not viewable.filter(id=new_team_id).exists():
raise PermissionDenied("Accès refusé.")
if not user_can_edit_specific_team(request.user, new_team_id):
raise PermissionDenied("Droits d'édition insuffisants.")
else:
new_team = None
old_team = None
if old_team_id is not None:
old_team = get_object_or_404(CompanyTeam, id=old_team_id)
member.teams.remove(old_team)
if new_team_id is not None:
member.teams.add(new_team)
member.save()
return JsonResponse(
{
"success": True,
"member_id": member.id,
"member_name": member.name,
"old_team_name": old_team.name if old_team else None,
"new_team_name": new_team.name if new_team else None,
}
)
@login_required
@require_POST
def teams_create_member(request):
"""Créer un nouveau membre, optionnellement dans une équipe (AJAX)."""
try:
data = json.loads(request.body)
except (json.JSONDecodeError, ValueError):
return JsonResponse({"error": "JSON invalide"}, status=400)
name = data.get("name", "").strip()
user_id = data.get("user_id")
raw_team_id = data.get("team_id")
team_id = int(raw_team_id) if raw_team_id is not None else None
if not name:
return JsonResponse({"error": "Le nom du membre est requis."}, status=400)
if not user_id:
return JsonResponse({"error": "L'utilisateur est requis."}, status=400)
if team_id is not None:
if not user_can_edit_specific_team(request.user, team_id):
raise PermissionDenied("Droits d'édition insuffisants.")
viewable = get_viewable_teams_for_user(request.user)
if not viewable.filter(id=team_id).exists():
raise PermissionDenied("Accès refusé.")
team = get_object_or_404(CompanyTeam, id=team_id)
else:
if not user_can_edit_teams(request.user):
raise PermissionDenied("Droits d'édition insuffisants.")
team = None
user_obj = get_object_or_404(User, id=user_id)
# Les non-admins ne peuvent ajouter que des utilisateurs de leur société, ou de la société de l'équipe concernée
user_company = _get_user_company(request.user)
if not _is_admin(request.user) and user_company is not None:
target_company = _get_user_company(user_obj)
allowed_companies = {user_company}
if team is not None:
allowed_companies.add(team.company)
if target_company not in allowed_companies:
return JsonResponse(
{"error": "Vous ne pouvez ajouter que des utilisateurs de votre société ou de la société de l'équipe."},
status=403,
)
# Check if there is already a CompanyMember for this user
member = CompanyMember.objects.filter(user=user_obj).first()
if member:
if team:
if member.teams.filter(id=team.id).exists():
return JsonResponse(
{"error": "Cet utilisateur fait déjà partie de cette équipe."},
status=400,
)
else:
if not member.teams.exists():
return JsonResponse(
{"error": "Cet utilisateur est déjà dans les membres non assignés."},
status=400,
)
if name:
member.name = name
member.save()
else:
member = CompanyMember.objects.create(name=name, user=user_obj)
if team:
member.teams.add(team)
return JsonResponse(
{
"success": True,
"member": {
"id": member.id,
"name": member.name,
"team_id": team.id if team else None,
"team_name": team.name if team else None,
"user_display": user_obj.get_full_name() or user_obj.username,
},
}
)
@login_required
@require_POST
def teams_delete_member(request, member_id):
"""Supprimer un membre (AJAX)."""
member = get_object_or_404(CompanyMember, id=member_id)
member_team_ids = list(member.teams.values_list('id', flat=True))
if member_team_ids:
viewable = get_viewable_teams_for_user(request.user)
for t_id in member_team_ids:
if not viewable.filter(id=t_id).exists():
raise PermissionDenied("Accès refusé.")
if not user_can_edit_specific_team(request.user, t_id):
raise PermissionDenied("Droits d'édition insuffisants.")
else:
# Membre non assigné : droits d'édition généraux suffisent
if not user_can_edit_teams(request.user):
raise PermissionDenied("Droits d'édition insuffisants.")
member_name = member.name
member.delete()
return JsonResponse({"success": True, "member_name": member_name})
@login_required
@require_POST
def teams_update_member(request, member_id):
"""Modifier le nom et/ou l'équipe d'un membre (AJAX)."""
member = get_object_or_404(CompanyMember, id=member_id)
viewable = get_viewable_teams_for_user(request.user)
try:
data = json.loads(request.body)
except (json.JSONDecodeError, ValueError):
return JsonResponse({"error": "JSON invalide"}, status=400)
# old_team_id represents the team column they clicked edit on (or None if unassigned)
old_team_id = data.get("old_team_id")
old_team_id = int(old_team_id) if old_team_id is not None else None
# Vérifier l'accès à l'équipe source
if old_team_id is not None:
if not viewable.filter(id=old_team_id).exists():
raise PermissionDenied("Accès refusé.")
if not user_can_edit_specific_team(request.user, old_team_id):
raise PermissionDenied("Droits d'édition insuffisants.")
else:
if not user_can_edit_teams(request.user):
raise PermissionDenied("Droits d'édition insuffisants.")
new_name = data.get("name", "").strip()
if not new_name:
return JsonResponse({"error": "Le nom du membre est requis."}, status=400)
raw_team_id = data.get("team_id") # peut être absent (non modifié) ou None (désassigner)
team_changed = "team_id" in data
new_team_id = None
new_team = None
if team_changed:
new_team_id = int(raw_team_id) if raw_team_id is not None else None
if new_team_id is not None:
new_team = get_object_or_404(CompanyTeam, id=new_team_id)
if not viewable.filter(id=new_team_id).exists():
raise PermissionDenied("Accès refusé.")
if not user_can_edit_specific_team(request.user, new_team_id):
raise PermissionDenied("Droits d'édition insuffisants.")
else:
new_team = None
# Effectuer le transfert
if old_team_id is not None:
old_team = get_object_or_404(CompanyTeam, id=old_team_id)
member.teams.remove(old_team)
if new_team_id is not None:
member.teams.add(new_team)
member.name = new_name
member.save()
return JsonResponse({
"success": True,
"member_id": member.id,
"member_name": member.name,
"team_id": new_team_id if team_changed else old_team_id,
"team_name": new_team.name if team_changed and new_team else None,
})
@login_required
@require_GET
def team_interventions_json(request, team_id):
"""Retourne les interventions assignées à une équipe (JSON, pour la modale)."""
from django.urls import reverse
from interventions.models import Intervention
team = get_object_or_404(CompanyTeam, id=team_id)
viewable = get_viewable_teams_for_user(request.user)
if not viewable.filter(id=team_id).exists():
raise PermissionDenied("Accès refusé.")
interventions = (
Intervention.objects
.filter(assigned_team=team, status_order__lte=51)
.select_related('assigned_member')
.order_by('-creation_time')
[:500]
)
data = []
for i in interventions:
data.append({
'id': i.id,
'code': i.code,
'title': i.title,
'status': i.status,
'status_display': i.get_status_display(),
'status_color': i.get_status_color(),
'assigned_member_name': i.assigned_member.name if i.assigned_member else None,
'summary_url': reverse('interventions:intervention_summary_modal', args=[i.id]),
'detail_url': reverse('interventions:intervention_detail', args=[i.id]),
})
return JsonResponse({'interventions': data, 'team_name': team.name})