Allow procurement requests for any department

This commit is contained in:
lizerk 2026-06-01 11:26:33 +03:00
parent 2b8e02e1f7
commit e6eee5ddf8
3 changed files with 26 additions and 57 deletions

View File

@ -96,10 +96,8 @@ class CanManageProcurementRequest(permissions.BasePermission):
- Просмотр (SAFE_METHODS): любой аутентифицированный (фильтрация в queryset)
- Создание:
- admin/staff/superuser → любой отдел
- модельные права (add_procurementrequest) → любой отдел
- сотрудник отдела → только свой отдел
- без отдела → запрещено
- любой аутентифицированный пользователь → любой отдел
- requestor всегда задаётся текущим пользователем в сериализаторе
- Изменение:
- admin/staff/superuser → любая заявка в DRAFT
@ -131,24 +129,6 @@ class CanManageProcurementRequest(permissions.BasePermission):
perm = perm_map.get(action)
return perm and user.has_perm(perm)
def _get_user_departments(self, user) -> list[int]:
"""Возвращает список ID отделов, где пользователь состоит."""
return list(
EmployeeDepartment.objects.filter(
employee_id=user.id, is_active=True
).values_list("department_id", flat=True)
)
def _get_dept_id_from_request(self, request) -> int | None:
"""Извлекает ID отдела из данных запроса."""
dept_id = request.data.get("department")
if dept_id is not None:
try:
return int(dept_id)
except (ValueError, TypeError):
pass
return None
def _is_dept_head(self, user, dept_id: int) -> bool:
"""Проверяет, является ли пользователь начальником отдела."""
return Department.objects.filter(id=dept_id, head_id=user.id).exists()
@ -180,22 +160,9 @@ class CanManageProcurementRequest(permissions.BasePermission):
if self._has_model_perm(user, action):
return True
# Для создания проверяем отдел
# Любой аутентифицированный пользователь может создать заявку
# от имени любого отдела. Валидность department проверит сериализатор.
if action == "create":
dept_id = self._get_dept_id_from_request(request)
if dept_id is None:
return False
# Пользователь должен состоять в указанном отделе
user_depts = self._get_user_departments(user)
if not user_depts:
# Пользователь без отдела не может создавать
return False
if dept_id not in user_depts:
# Нельзя создавать заявку в чужом отделе
return False
return True
# Для update/delete проверка на уровне объекта

View File

@ -316,10 +316,10 @@ class TestProcurementRequestCreate:
assert request.items.count() == 2
assert request.total_cost == Decimal('305000.00') # 5*25k + 10*18k
def test_create_request_wrong_department(
def test_create_request_for_other_department(
self, api_client, user, db
):
"""Нельзя создать заявку для чужого отдела."""
"""Можно создать заявку для любого отдела."""
other_dept = Department.objects.create(
name="HR отдел", description="Другой отдел"
)
@ -336,11 +336,9 @@ class TestProcurementRequestCreate:
}
response = api_client.post(url, data, format='json')
# Должна быть ошибка валидации
assert response.status_code in [
status.HTTP_400_BAD_REQUEST,
status.HTTP_403_FORBIDDEN
]
assert response.status_code == status.HTTP_201_CREATED
assert response.data['department'] == other_dept.id
assert response.data['requestor'] == user.id
def test_create_processing_department_request_skips_approvals(
self, api_client, user, department, db

View File

@ -3,10 +3,8 @@
Сценарии:
1. Создание заявки:
- admin/staff/superuser → любой отдел
- модельные права (add_procurementrequest) → любой отдел
- сотрудник отдела → только свой отдел
- сотрудник без отдела → запрещено
- любой аутентифицированный пользователь → любой отдел
- requestor всегда текущий пользователь
2. Изменение заявки:
- admin/staff/superuser → любая заявка в DRAFT
@ -292,14 +290,16 @@ class TestProcurementRequestCreateAccess:
assert response.status_code == status.HTTP_201_CREATED
assert response.data['department'] == dept_it.id
def test_dept_member_cannot_create_in_other_dept(
def test_dept_member_can_create_in_other_dept(
self, api_client, dept_member, dept_hr
):
"""Сотрудник отдела НЕ может создать заявку в ЧУЖОМ отделе."""
"""Сотрудник отдела может создать заявку в чужом отделе."""
api_client.force_authenticate(user=dept_member)
data = valid_request_data(dept_hr)
response = api_client.post(request_list_url(), data, format="json")
assert response.status_code == status.HTTP_403_FORBIDDEN
assert response.status_code == status.HTTP_201_CREATED
assert response.data['department'] == dept_hr.id
assert response.data['requestor'] == dept_member.id
def test_dept_head_can_create_in_own_dept(
self, api_client, dept_head, dept_it
@ -310,23 +310,27 @@ class TestProcurementRequestCreateAccess:
response = api_client.post(request_list_url(), data, format="json")
assert response.status_code == status.HTTP_201_CREATED
def test_dept_head_cannot_create_in_other_dept(
def test_dept_head_can_create_in_other_dept(
self, api_client, dept_head, dept_hr
):
"""Начальник отдела НЕ может создать заявку в чужом отделе."""
"""Начальник отдела может создать заявку в чужом отделе."""
api_client.force_authenticate(user=dept_head)
data = valid_request_data(dept_hr)
response = api_client.post(request_list_url(), data, format="json")
assert response.status_code == status.HTTP_403_FORBIDDEN
assert response.status_code == status.HTTP_201_CREATED
assert response.data['department'] == dept_hr.id
assert response.data['requestor'] == dept_head.id
def test_user_without_dept_cannot_create(
def test_user_without_dept_can_create(
self, api_client, user_without_dept, dept_it
):
"""Пользователь без отдела НЕ может создавать заявки."""
"""Пользователь без отдела может создать заявку в выбранном отделе."""
api_client.force_authenticate(user=user_without_dept)
data = valid_request_data(dept_it)
response = api_client.post(request_list_url(), data, format="json")
assert response.status_code == status.HTTP_403_FORBIDDEN
assert response.status_code == status.HTTP_201_CREATED
assert response.data['department'] == dept_it.id
assert response.data['requestor'] == user_without_dept.id
def test_requestor_is_set_to_current_user(
self, api_client, dept_member, dept_it