diff --git a/apps/reports/documents.py b/apps/reports/documents.py index ae6bfb2..370b69e 100644 --- a/apps/reports/documents.py +++ b/apps/reports/documents.py @@ -29,22 +29,25 @@ def user_can_open_reports_hub(user) -> bool: def user_can_view_report_document(user, doc: ReportDocument) -> bool: if not user_can_open_reports_hub(user): return False + # Черновики — только руководителю своего отдела. + if user_can_manage_it_monthly_report(user) and getattr(user, 'department_id', None) == doc.department_id: + return True + # view_all и обычные сотрудники — только опубликованные. + if not doc.is_published: + return False if user_can_view_all_department_reports(user): return True - if getattr(user, 'department_id', None) != doc.department_id: - return False - if user_can_manage_it_monthly_report(user): - return True - return doc.is_published + return getattr(user, 'department_id', None) == doc.department_id def user_can_edit_report_document(user, doc: ReportDocument) -> bool: if not user_can_open_reports_hub(user): return False - if user_can_view_all_department_reports(user): + if user_can_manage_it_monthly_report(user) and getattr(user, 'department_id', None) == doc.department_id: return True - if user_can_manage_it_monthly_report(user): - return getattr(user, 'department_id', None) == doc.department_id + # view_all может править/снимать только уже опубликованные (черновики чужих отделов не видит). + if user_can_view_all_department_reports(user): + return doc.is_published return False @@ -65,13 +68,22 @@ def visible_report_documents(user): if not user_can_open_reports_hub(user): return ReportDocument.objects.none() qs = ReportDocument.objects.select_related('department', 'created_by', 'published_by') - if user_can_view_all_department_reports(user): - return qs dept_id = getattr(user, 'department_id', None) + manage_own = user_can_manage_it_monthly_report(user) and bool(dept_id) + + if user_can_view_all_department_reports(user): + # Все отделы — только опубликованные; свои черновики — если ещё и руководитель. + if manage_own: + from django.db.models import Q + return qs.filter( + Q(status=ReportDocument.STATUS_PUBLISHED) | Q(department_id=dept_id) + ).distinct() + return qs.filter(status=ReportDocument.STATUS_PUBLISHED) + if not dept_id: return ReportDocument.objects.none() qs = qs.filter(department_id=dept_id) - if user_can_manage_it_monthly_report(user): + if manage_own: return qs return qs.filter(status=ReportDocument.STATUS_PUBLISHED) diff --git a/apps/reports/tests.py b/apps/reports/tests.py index 49bb2bd..71ad7d3 100644 --- a/apps/reports/tests.py +++ b/apps/reports/tests.py @@ -643,3 +643,39 @@ class ReportDocumentTests(TestCase): fake = SimpleUploadedFile('x.docx', b'x') r = self.client.post('/reports/documents/upload/', {'file': fake}) self.assertEqual(r.status_code, 403) + + def test_view_all_sees_only_published(self): + from django.contrib.auth.models import Permission + from apps.reports.documents import visible_report_documents + from apps.reports.models import ReportDocument + + other_dept_type, _ = DirectoryType.objects.get_or_create( + code='department', defaults={'name': 'Відділи', 'hierarchy_type': 'strict'}, + ) + other = Directory.objects.create( + directory_type=other_dept_type, code='hr-docs', name='HR-docs', + ) + draft = ReportDocument.objects.create( + department=other, title='Черновик HR', filename='draft.docx', + kind=ReportDocument.KIND_EXTRA, status=ReportDocument.STATUS_DRAFT, + created_by=self.head, + ) + published = ReportDocument.objects.create( + department=other, title='Опубликован HR', filename='pub.docx', + kind=ReportDocument.KIND_EXTRA, status=ReportDocument.STATUS_PUBLISHED, + created_by=self.head, + ) + viewer = make_employee( + last_name='Все', first_name='Отделы', username='view.all.docs', + department=self.dept, user_location_type='office', + ) + it_group = Group.objects.get(name='IT') + viewer.groups.add(it_group) + perm = Permission.objects.get( + codename='view_all_departments', content_type__app_label='reports', + ) + viewer.user_permissions.add(perm) + + titles = set(visible_report_documents(viewer).values_list('title', flat=True)) + self.assertIn(published.title, titles) + self.assertNotIn(draft.title, titles) diff --git a/apps/tasks/views.py b/apps/tasks/views.py index 0fc5b8a..53a1311 100644 --- a/apps/tasks/views.py +++ b/apps/tasks/views.py @@ -38,7 +38,7 @@ def can_delete_task(user, task) -> bool: def _employee_dict(emp): return { 'pk': emp.pk, - 'label': emp.get_full_name() or emp.username, + 'label': emp.get_full_name() or '—', 'position': emp.post.name if emp.post_id else '', 'photo': emp.photo.url if emp.photo else '', } diff --git a/core/tests.py b/core/tests.py index 7eebcb5..1291e09 100644 --- a/core/tests.py +++ b/core/tests.py @@ -146,6 +146,7 @@ class EmployeeDetailTests(TestCase): self.assertNotIn('Личный телефон', html) self.assertNotIn('Интеграции', html) self.assertNotIn(self.employee.personal_phone, html) + self.assertNotIn(self.employee.username, html) def test_manager_chain_and_subordinates_shown(self): login_2fa(self.client, self.staff) diff --git a/templates/partials/employee-profile-aside.html b/templates/partials/employee-profile-aside.html index 92b32be..8a435a9 100644 --- a/templates/partials/employee-profile-aside.html +++ b/templates/partials/employee-profile-aside.html @@ -46,11 +46,11 @@
+ {% if can_view_pii or is_own_profile %}
{% trans "Логин" %} {{ employee.username }}
- {% if can_view_pii or is_own_profile %} {% if employee.email %}
Email