refactor(departments): label role-only members
This commit is contained in:
parent
a4910421c3
commit
274e5767c9
@ -9,6 +9,7 @@ from employees.models import (
|
||||
DepartmentPermission,
|
||||
DeptPerm,
|
||||
EmployeeDepartment,
|
||||
RoleAssignment,
|
||||
)
|
||||
from phonenumbers import PhoneNumberFormat
|
||||
|
||||
@ -183,6 +184,7 @@ def _build_links_for_dept(dept: Department, serializer) -> list[dict]:
|
||||
"is_active": bool}, ...]
|
||||
"""
|
||||
links: list[dict] = []
|
||||
seen_employee_ids: set[int] = set()
|
||||
qs = (
|
||||
EmployeeDepartment.objects.filter(department_id=dept.id)
|
||||
.select_related("employee", "role")
|
||||
@ -195,6 +197,7 @@ def _build_links_for_dept(dept: Department, serializer) -> list[dict]:
|
||||
)
|
||||
for link in qs:
|
||||
emp_data = serializer(link.employee).data # содержит display_name
|
||||
seen_employee_ids.add(link.employee_id)
|
||||
role = (
|
||||
{"id": link.role_id, "name": link.role.name}
|
||||
if link.role_id
|
||||
@ -205,6 +208,65 @@ def _build_links_for_dept(dept: Department, serializer) -> list[dict]:
|
||||
"employee": emp_data,
|
||||
"role": role,
|
||||
"is_active": bool(link.is_active),
|
||||
"via_assignment": False,
|
||||
}
|
||||
)
|
||||
|
||||
assignment_qs = (
|
||||
RoleAssignment.objects.filter(
|
||||
role__department_id=dept.id,
|
||||
is_active=True,
|
||||
)
|
||||
.select_related("employee", "role")
|
||||
.order_by(
|
||||
"employee__last_name",
|
||||
"employee__first_name",
|
||||
"employee__patronymic",
|
||||
"employee_id",
|
||||
"role__name",
|
||||
)
|
||||
)
|
||||
|
||||
grouped_assignments: dict[int, dict[str, object]] = {}
|
||||
for assignment in assignment_qs:
|
||||
if assignment.employee_id in seen_employee_ids:
|
||||
continue
|
||||
|
||||
bucket = grouped_assignments.get(assignment.employee_id)
|
||||
if bucket is None:
|
||||
bucket = {
|
||||
"employee": serializer(assignment.employee).data,
|
||||
"role_ids": [],
|
||||
"role_names": [],
|
||||
}
|
||||
grouped_assignments[assignment.employee_id] = bucket
|
||||
|
||||
role_ids = bucket["role_ids"]
|
||||
role_names = bucket["role_names"]
|
||||
assert isinstance(role_ids, list)
|
||||
assert isinstance(role_names, list)
|
||||
role_ids.append(assignment.role_id)
|
||||
role_names.append(assignment.role.name)
|
||||
|
||||
for employee_id, bucket in grouped_assignments.items():
|
||||
seen_employee_ids.add(employee_id)
|
||||
role_ids = bucket["role_ids"]
|
||||
role_names = bucket["role_names"]
|
||||
assert isinstance(role_ids, list)
|
||||
assert isinstance(role_names, list)
|
||||
links.append(
|
||||
{
|
||||
"employee": bucket["employee"],
|
||||
"role": (
|
||||
{
|
||||
"id": role_ids[0] if role_ids else None,
|
||||
"name": ", ".join(role_names) if role_names else "",
|
||||
}
|
||||
if role_names
|
||||
else None
|
||||
),
|
||||
"is_active": True,
|
||||
"via_assignment": True,
|
||||
}
|
||||
)
|
||||
|
||||
@ -214,7 +276,13 @@ def _build_links_for_dept(dept: Department, serializer) -> list[dict]:
|
||||
):
|
||||
head_data = serializer(dept.head).data
|
||||
links.insert(
|
||||
0, {"employee": head_data, "role": None, "is_active": True}
|
||||
0,
|
||||
{
|
||||
"employee": head_data,
|
||||
"role": None,
|
||||
"is_active": True,
|
||||
"via_assignment": False,
|
||||
},
|
||||
)
|
||||
|
||||
return links
|
||||
|
||||
@ -168,6 +168,32 @@ class TestRoleAssignmentEndpoints:
|
||||
|
||||
assert resp.status_code == status.HTTP_201_CREATED
|
||||
|
||||
def test_department_members_include_role_only_employees(self, api_client: APIClient):
|
||||
"""members endpoint показывает сотрудников с ролью в отделе даже без membership."""
|
||||
admin = make_user("admin-members@test.com", is_staff=True)
|
||||
dept = make_dept("IT")
|
||||
role = make_role(dept, "Developer", [])
|
||||
role_only_user = make_user("role-only@test.com")
|
||||
|
||||
RoleAssignment.objects.create(
|
||||
employee=role_only_user,
|
||||
role=role,
|
||||
is_active=True,
|
||||
)
|
||||
|
||||
api_client.force_authenticate(admin)
|
||||
resp = api_client.get(f"/api/v1/departments/{dept.id}/members/")
|
||||
|
||||
assert resp.status_code == status.HTTP_200_OK
|
||||
role_only_link = next(
|
||||
item
|
||||
for item in resp.data["results"]
|
||||
if item["employee"]["id"] == role_only_user.id
|
||||
)
|
||||
assert role_only_link["via_assignment"] is True
|
||||
assert role_only_link["is_active"] is True
|
||||
assert role_only_link["role"]["name"] == "Developer"
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestRoleAssignmentPermissions:
|
||||
"""Тесты для проверки прав через RoleAssignment."""
|
||||
|
||||
@ -103,6 +103,7 @@ function DepartmentMemberRow({
|
||||
const managementMode = canAssignRoles || canChangeHead || canManage;
|
||||
const roleLabel = member.role?.name || "Без роли";
|
||||
const isRoleMenuOpen = roleMenuOpenForId === member.employee.id;
|
||||
const isRoleOnly = member.via_assignment === true;
|
||||
const personName = displayUserName(member.employee);
|
||||
const profileLink = userProfileLink(member.employee, currentUserId);
|
||||
const fallback = (
|
||||
@ -139,6 +140,7 @@ function DepartmentMemberRow({
|
||||
<Crown size={12} />
|
||||
</MetaChip>
|
||||
) : null}
|
||||
{isRoleOnly ? <MetaChip>Роль вне состава</MetaChip> : null}
|
||||
</span>
|
||||
);
|
||||
|
||||
@ -282,6 +284,8 @@ function DepartmentMemberRow({
|
||||
</MetaChip>
|
||||
) : null}
|
||||
|
||||
{isRoleOnly ? <MetaChip>Роль вне состава</MetaChip> : null}
|
||||
|
||||
{canChangeHead && !isHead ? (
|
||||
<button
|
||||
type="button"
|
||||
@ -294,7 +298,7 @@ function DepartmentMemberRow({
|
||||
</button>
|
||||
) : null}
|
||||
|
||||
{canManage && !isHead ? (
|
||||
{canManage && !isHead && !isRoleOnly ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void onRemoveMember(member.employee.id)}
|
||||
|
||||
@ -131,6 +131,7 @@ export interface DepartmentMemberLink {
|
||||
employee: User;
|
||||
role?: DepartmentMemberRole | null;
|
||||
is_active: boolean;
|
||||
via_assignment?: boolean;
|
||||
}
|
||||
|
||||
export interface DepartmentUserPermissions {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user