from django.db import transaction from django.shortcuts import get_object_or_404 from rest_framework.exceptions import ValidationError, PermissionDenied from apps.clients.models import Client from apps.projects.models import Project, ProjectMembership from apps.workspaces.models import WorkspaceMembership @transaction.atomic def create_project(user, workspace, name, client=None, description="", color=""): """ Creates a new project and automatically assigns the creator as an active MANAGER of that project. """ workspace_member = WorkspaceMembership.objects.filter( workspace=workspace, user=user, is_active=True, is_deleted=False ).first() if not workspace_member: raise PermissionDenied("You do not have access to this workspace.") if Project.objects.filter(workspace=workspace, name=name, is_deleted=False).exists(): raise ValidationError({"name": "A project with this name already exists in the workspace."}) project = Project.objects.create( workspace=workspace, name=name, client=client, description=description, color=color, created_by=user, updated_by=user, ) ProjectMembership.objects.create( project=project, user=user, role=ProjectMembership.Role.MANAGER, is_active=True ) return project def update_project(project, **kwargs): """ Updates specific fields of an existing project. """ update_fields = [] # Optional manual uniqueness check if name is being updated if "name" in kwargs and kwargs["name"] != project.name: if Project.objects.filter(workspace=project.workspace, name=kwargs["name"], is_deleted=False).exists(): raise ValidationError({"name": "A project with this name already exists in the workspace."}) client_id = kwargs.pop("client") client = get_object_or_404(Client, id=client_id, is_deleted=False) if client_id else None kwargs["client"] = client for field, value in kwargs.items(): if hasattr(project, field) and getattr(project, field) != value: setattr(project, field, value) update_fields.append(field) if update_fields: update_fields.append("updated_at") project.save(update_fields=update_fields) return project def toggle_project_archive(project) -> Project: """ Archives or unarchives a project. """ project.is_archived = not project.is_archived project.save(update_fields=["is_archived", "updated_at"]) return project