Files
ACORE/apps/reports/tests.py
T
lokses bf5310e489 Hide peer logins on profiles and restrict view_all to published reports.
Login stays visible only to self/manager/HR; draft report docs stay department-head only.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-14 15:20:49 +03:00

682 lines
31 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Тесты раздела Отчёты и месячного IT-отчёта."""
from datetime import date
import json
from django.contrib.auth.models import Group
from django.test import Client, TestCase
from apps.it_manager.models import WorkLog
from apps.it_manager.tests import _dir_id, make_employee
from apps.locations.models.pharmacies import Pharmacy
from apps.reports.it_monthly import (
build_monthly_report, report_to_markdown, user_can_view_it_monthly_report,
)
from apps.reports.it_monthly.builder import split_work_items
from apps.reports.it_monthly.conclusions import build_conclusions
from apps.reports.registry import visible_reports
from core.models.directories import Directory, DirectoryType
from core.test_utils import login_2fa
class MonthlyReportTests(TestCase):
def setUp(self):
dept_type, _ = DirectoryType.objects.get_or_create(
code='department', defaults={'name': 'Відділи', 'hierarchy_type': 'strict'},
)
self.dept = Directory.objects.create(
directory_type=dept_type, code='it-rep', name='IT-відділ-rep',
)
self.head = make_employee(
last_name='Бабич', first_name='Виктор', username='babich.rep',
department=self.dept, user_location_type='office',
)
self.it_user = make_employee(
last_name='Апродов', first_name='Артур', username='apr.rep',
department=self.dept, manager=self.head, user_location_type='office',
)
it_group = Group.objects.get(name='IT')
self.head.groups.add(it_group)
self.it_user.groups.add(it_group)
self.client = Client()
region_type, _ = DirectoryType.objects.get_or_create(code='region', defaults={'name': 'Регион'})
org_type, _ = DirectoryType.objects.get_or_create(code='organization', defaults={'name': 'Организация'})
region = Directory.objects.create(directory_type=region_type, code='od-rep', name='Одесса-rep')
org = Directory.objects.create(
directory_type=org_type, code='akc-rep', name='АКС-rep',
extra_data={'mail_prefix': 'a'},
)
pharmacy = Pharmacy.objects.create(
organization=org, location_region=region,
pharmacy_number=35, location_type='pharmacy',
street='Реп', house_number='2',
)
WorkLog.objects.create(
date=date(2026, 5, 3),
technician=self.it_user,
location=pharmacy,
work_type_id=_dir_id('worklog_type', 'software'),
description='Восстановление ПРРО на кассе',
)
WorkLog.objects.create(
date=date(2026, 5, 4),
technician=self.it_user,
work_type_id=_dir_id('worklog_type', 'network'),
description='Диагностика локальной сети',
)
def test_only_department_head_can_view(self):
self.assertTrue(user_can_view_it_monthly_report(self.head))
self.assertFalse(user_can_view_it_monthly_report(self.it_user))
self.assertEqual([s.code for s in visible_reports(self.head)], ['it_monthly'])
self.assertEqual(visible_reports(self.it_user), [])
def test_published_visible_to_department_and_view_all(self):
from django.contrib.auth.models import Permission
from apps.reports.publication import publish_it_monthly
self.assertFalse(user_can_view_it_monthly_report(self.it_user))
publish_it_monthly(self.head, 2026, 5)
self.assertTrue(user_can_view_it_monthly_report(self.it_user))
self.assertEqual([s.code for s in visible_reports(self.it_user)], ['it_monthly'])
login_2fa(self.client, self.it_user)
page = self.client.get('/reports/it/monthly/?month=2026-05')
self.assertEqual(page.status_code, 200)
self.assertContains(page, 'Восстановление ПРРО')
self.assertNotContains(page, 'Опубликовать')
# неопубликованный месяц в URL → показывают последний опубликованный
switched = self.client.get('/reports/it/monthly/?month=2026-04')
self.assertEqual(switched.status_code, 200)
self.assertContains(switched, '2026-05')
self.assertEqual(self.client.get('/reports/it/monthly/2026-04.md').status_code, 403)
other_dept_type, _ = DirectoryType.objects.get_or_create(
code='department', defaults={'name': 'Відділи', 'hierarchy_type': 'strict'},
)
other_dept = Directory.objects.create(
directory_type=other_dept_type, code='sales-rep', name='Sales-rep',
)
outsider = make_employee(
last_name='Чужой', first_name='Юзер', username='outsider.rep',
department=other_dept, user_location_type='office',
)
self.assertFalse(user_can_view_it_monthly_report(outsider))
viewer = make_employee(
last_name='Все', first_name='Отделы', username='all.depts',
department=other_dept, user_location_type='office',
)
perm = Permission.objects.get(codename='view_all_departments', content_type__app_label='reports')
viewer.user_permissions.add(perm)
self.assertTrue(user_can_view_it_monthly_report(viewer))
login_2fa(self.client, viewer)
self.assertEqual(self.client.get('/reports/it/monthly/?month=2026-05').status_code, 200)
self.assertEqual(self.client.get('/reports/it/monthly/2026-04.pdf').status_code, 403)
def test_head_can_publish_and_unpublish(self):
login_2fa(self.client, self.head)
page = self.client.get('/reports/it/monthly/?month=2026-05')
self.assertContains(page, 'Опубликовать')
self.assertContains(page, 'Черновик')
pub = self.client.post('/reports/it/monthly/2026-05/publish/')
self.assertEqual(pub.status_code, 302)
page = self.client.get('/reports/it/monthly/?month=2026-05')
self.assertContains(page, 'Опубликован')
self.assertContains(page, 'Снять с публикации')
unpub = self.client.post('/reports/it/monthly/2026-05/unpublish/')
self.assertEqual(unpub.status_code, 302)
self.assertFalse(user_can_view_it_monthly_report(self.it_user))
def test_build_report(self):
company = {
'staff': [{
'id': 'aprodov', 'name': 'Апродов Артур', 'short': 'Апродов А.',
'role': 'L1', 'match': ['апродов'],
}],
'directions': [
{'id': 'prro', 'name': 'ПРРО', 'keywords': ['прро']},
{'id': 'network', 'name': 'Сеть', 'keywords': ['сет']},
],
}
report = build_monthly_report(2026, 5, company=company)
self.assertEqual(report['total_tasks'], 2)
self.assertEqual(report['people_active'], 1)
self.assertIn('А35', report['pharmacies'])
self.assertEqual(report['employees'][0]['tasks_count'], 2)
self.assertTrue(report['conclusions'])
self.assertTrue(any('ежедневном режиме' in (c.get('text') or '') for c in report['conclusions']))
md = report_to_markdown(report)
self.assertIn('Восстановление ПРРО', md)
self.assertIn('## Сводка по направлениям', md)
self.assertIn('Основные выводы и рекомендации', md)
items = report['employees'][0]['tasks'][0]['work_items']
self.assertEqual(items, ['Восстановление ПРРО на кассе'])
def test_conclusions_cover_roles(self):
company = {
'staff': [
{'id': 'aprodov', 'name': 'Апродов Артур', 'short': 'Апродов А.', 'match': ['апродов']},
{'id': 'kogutenko', 'name': 'Когутенко Сергей', 'short': 'Когутенко С.', 'match': ['когутенко']},
{'id': 'khizhniak', 'name': 'Хижняк Николай', 'short': 'Хижняк Н.', 'match': ['хижняк']},
{'id': 'babich', 'name': 'Бабич Виктор', 'short': 'Бабич В.И.', 'match': ['бабич']},
],
'directions': [
{'id': 'prro', 'name': 'ПРРО', 'keywords': ['прро']},
{'id': 'network', 'name': 'Сеть', 'keywords': ['mikrotik', 'роутер']},
],
}
report = {
'month': 7,
'total_tasks': 100,
'summary': [{'name': 'Обслуживание кассового ПО (ПРРО)', 'count': 40, 'people': 'Апродов А.'}],
'employees': [
{
'id': 'aprodov', 'name': 'Апродов Артур', 'tasks_count': 50, 'systems_count': 0,
'tasks': [{'object': 'А10', 'work': 'Восстановление ПРРО'}], 'systems': [],
},
{
'id': 'kogutenko', 'name': 'Когутенко Сергей', 'tasks_count': 30, 'systems_count': 0,
'tasks': [
{'object': 'А12', 'work': 'Замена роутера MikroTik'},
{'object': 'А15', 'work': 'Монтаж оборудования'},
],
'systems': [],
},
{
'id': 'khizhniak', 'name': 'Хижняк Николай', 'tasks_count': 20, 'systems_count': 0,
'tasks': [{'object': 'ОФИС', 'work': 'Подготовка ПК сотруднику; обслуживание МФУ'}],
'systems': [],
},
{
'id': 'babich', 'name': 'Бабич', 'tasks_count': 0, 'systems_count': 2,
'tasks': [],
'systems': [{'name': 'Proxmox', 'work': 'Мониторинг'}],
},
],
}
conclusions = build_conclusions(report, company)
text = '\n'.join(c['text'] for c in conclusions)
self.assertIn('Артура Апродова', text)
self.assertIn('Сергея Когутенко', text)
self.assertIn('Николаем Хижняком', text)
self.assertIn('Proxmox', text)
last = conclusions[-1]
self.assertTrue(last.get('bullets'))
self.assertTrue(any('удаленно' in b for b in last['bullets']))
def test_pharmacy_object_is_not_field_visit_for_remote_or_unknown(self):
"""Удалёнка и неизвестные сотрудники с объектом «А35» не должны попадать в «с выездом»."""
from apps.reports.it_monthly.conclusions import resolve_work_bucket
self.assertEqual(
resolve_work_bucket(
'aprodov',
{'role': 'Специалист удалённой поддержки', 'tasks': [{'object': 'А35'}]},
{},
),
'remote',
)
# Поле карточки сотрудника важнее STAFF_PHRASES / объекта аптеки
self.assertEqual(
resolve_work_bucket(
'kogutenko',
{'it_work_channel': 'remote', 'tasks': [{'object': 'А12'}]},
{},
),
'remote',
)
company = {
'staff': [
{
'id': 'new_remote',
'name': 'Тестов Удалённый',
'role': 'Специалист удалённой поддержки',
'bucket': 'remote',
},
],
}
self.assertEqual(
resolve_work_bucket('new_remote', {'role': '', 'tasks': []}, company),
'remote',
)
# unmatched: аптека → remote, не field
self.assertEqual(
resolve_work_bucket(
'user-99',
{'role': '', 'tasks': [{'object': 'А35', 'work': 'ПРРО'}], 'tasks_count': 1},
company,
),
'unknown',
)
report = {
'month': 8,
'total_tasks': 40,
'summary': [{'name': 'ПРРО', 'count': 40, 'people': 'X'}],
'employees': [
{
'id': 'aprodov',
'name': 'Апродов',
'role': 'Специалист удалённой поддержки',
'it_work_channel': 'remote',
'tasks_count': 20,
'systems_count': 0,
'tasks': [{'object': 'А35', 'work': 'ПРРО'}, {'object': 'Т2', 'work': 'Терминал'}],
'systems': [],
},
{
'id': 'user-42',
'name': 'Удалённый сотрудник',
'role': '',
'it_work_channel': 'remote',
'tasks_count': 20,
'systems_count': 0,
'tasks': [{'object': 'А10', 'work': 'Helpdesk'}],
'systems': [],
},
],
}
conclusions = build_conclusions(report, company)
bullets = conclusions[-1].get('bullets') or []
field_line = next(b for b in bullets if 'выезд' in b)
remote_line = next(b for b in bullets if 'удален' in b)
# все 40 задач — remote; field должен быть ~0%
self.assertIn('~0%', field_line)
self.assertIn('~100%', remote_line)
def test_split_semicolon_work_items(self):
items = split_work_items('восстановление печати на МФУ; добавление баз; настройка сканера')
self.assertEqual(len(items), 3)
self.assertEqual(items[0], 'Восстановление печати на МФУ')
def test_pharmacy_object_codes_all_org_letters(self):
from apps.reports.it_monthly.builder import _is_pharmacy_object_code
for code in ('А37', 'A37', 'Т2', 'T2', 'П10', 'Г3', 'Л1'):
self.assertTrue(_is_pharmacy_object_code(code), code)
for code in ('ОФИС', 'Склад', 'Вайтер Юг', '№5', ''):
self.assertFalse(_is_pharmacy_object_code(code), code)
def test_reports_hub_and_it_monthly_for_head(self):
login_2fa(self.client, self.head)
hub = self.client.get('/reports/')
self.assertEqual(hub.status_code, 200)
self.assertContains(hub, 'Месячный IT-отчёт')
self.assertContains(hub, '/reports/it/monthly/')
self.assertContains(hub, 'IT-отдел')
self.assertContains(hub, 'Открыть')
page = self.client.get('/reports/it/monthly/?month=2026-05')
self.assertEqual(page.status_code, 200)
self.assertContains(page, 'Восстановление ПРРО')
self.assertContains(page, 'Скачать Word')
self.assertContains(page, 'Скачать PDF')
it_page = self.client.get('/it-manager/')
self.assertEqual(it_page.status_code, 200)
self.assertContains(it_page, '/reports/it/monthly/')
self.assertNotContains(it_page, 'id="it-panel-report"')
md = self.client.get('/reports/it/monthly/2026-05.md')
self.assertEqual(md.status_code, 200)
self.assertIn('attachment', md['Content-Disposition'])
self.assertIn('Восстановление ПРРО', md.content.decode())
docx = self.client.get('/reports/it/monthly/2026-05.docx')
self.assertEqual(docx.status_code, 200)
self.assertTrue(docx.content.startswith(b'PK'))
pdf = self.client.get('/reports/it/monthly/2026-05.pdf')
self.assertEqual(pdf.status_code, 200)
self.assertTrue(pdf.content.startswith(b'%PDF'))
preview = self.client.get('/reports/it/monthly/2026-05/preview/')
self.assertEqual(preview.status_code, 200)
self.assertContains(preview, 'ОТЧЁТ О ВЫПОЛНЕННЫХ РАБОТАХ')
self.assertEqual(preview.get('X-Frame-Options'), 'SAMEORIGIN')
def test_legacy_urls_redirect(self):
login_2fa(self.client, self.head)
for fmt, path in [
('md', '/it-manager/report/2026-05.md'),
('docx', '/it-manager/report/2026-05.docx'),
('pdf', '/it-manager/report/2026-05.pdf'),
]:
r = self.client.get(path)
self.assertEqual(r.status_code, 302, path)
self.assertEqual(r['Location'], f'/reports/it/monthly/2026-05.{fmt}')
r = self.client.get('/it-manager/report/2026-05/preview/')
self.assertEqual(r.status_code, 302)
self.assertEqual(r['Location'], '/reports/it/monthly/2026-05/preview/')
def test_report_forbidden_for_regular_it(self):
login_2fa(self.client, self.it_user)
r = self.client.get('/it-manager/')
self.assertEqual(r.status_code, 200)
self.assertNotContains(r, '/reports/it/monthly/')
self.assertEqual(self.client.get('/reports/it/monthly/').status_code, 403)
self.assertEqual(self.client.get('/reports/it/monthly/2026-05.md').status_code, 403)
self.assertEqual(self.client.get('/reports/it/monthly/2026-05.docx').status_code, 403)
self.assertEqual(self.client.get('/reports/it/monthly/2026-05.pdf').status_code, 403)
self.assertEqual(self.client.get('/reports/it/monthly/2026-05/preview/').status_code, 403)
class ReportTemplateTests(TestCase):
def setUp(self):
from apps.reports.classic_seed import CLASSIC_BLOCKS, CLASSIC_HTML, CLASSIC_MD
from apps.reports.models import ReportTemplate
dept_type, _ = DirectoryType.objects.get_or_create(
code='department', defaults={'name': 'Відділи', 'hierarchy_type': 'strict'},
)
self.dept = Directory.objects.create(
directory_type=dept_type, code='it-tpl', name='IT-відділ-tpl',
)
self.head = make_employee(
last_name='Бабич', first_name='Виктор', username='babich.tpl',
department=self.dept, user_location_type='office',
)
self.it_user = make_employee(
last_name='Апродов', first_name='Артур', username='apr.tpl',
department=self.dept, manager=self.head, user_location_type='office',
)
it_group = Group.objects.get(name='IT')
self.head.groups.add(it_group)
self.it_user.groups.add(it_group)
self.client = Client()
self.classic, _ = ReportTemplate.objects.update_or_create(
code='it_monthly_classic',
defaults={
'report_type': ReportTemplate.REPORT_IT_MONTHLY,
'name': 'Классический IT',
'engine': ReportTemplate.ENGINE_BLOCKS_V1,
'body_html': CLASSIC_HTML,
'body_md': CLASSIC_MD,
'blocks': CLASSIC_BLOCKS,
'is_default': True,
'is_active': True,
'is_system': True,
},
)
WorkLog.objects.create(
date=date(2026, 5, 3),
technician=self.it_user,
work_type_id=_dir_id('worklog_type', 'software'),
description='Восстановление ПРРО на кассе',
)
def test_classic_render_html(self):
from apps.reports.engine import render_report
report = build_monthly_report(2026, 5, company={'staff': [], 'directions': []})
html = render_report(report, self.classic, 'html')
self.assertIn('ОТЧЁТ О ВЫПОЛНЕННЫХ РАБОТАХ', html)
self.assertIn('Восстановление ПРРО', html)
def test_blocks_v1_hides_section(self):
from apps.reports.engine import render_report
from apps.reports.models import ReportTemplate
tpl = ReportTemplate.objects.create(
code='it-no-staff',
report_type=ReportTemplate.REPORT_IT_MONTHLY,
name='Без состава',
engine=ReportTemplate.ENGINE_BLOCKS_V1,
blocks={
'labels': {'main_title': 'КОРОТКИЙ', 'summary_heading': 'Сводка'},
'sections': ['stats', 'summary', 'signature'],
},
is_system=False,
)
report = build_monthly_report(2026, 5, company={'staff': [], 'directions': []})
html = render_report(report, tpl, 'html')
self.assertIn('КОРОТКИЙ', html)
self.assertIn('Сводка', html)
self.assertNotIn('Состав IT-отдела', html)
self.assertNotIn('Выполненные работы по сотрудникам', html)
def test_custom_title_in_template(self):
from apps.reports.engine import render_report
from apps.reports.models import ReportTemplate
custom = ReportTemplate.objects.create(
code='it-custom-title',
report_type=ReportTemplate.REPORT_IT_MONTHLY,
name='Свой заголовок',
engine=ReportTemplate.ENGINE_DJANGO_HTML,
body_html='<h1>{{ labels.main_title }}</h1><p>{{ report.total_tasks }}</p>',
body_md='# {{ labels.main_title }}',
blocks={'labels': {'main_title': 'КАСТОМНЫЙ ОТЧЁТ IT'}},
is_system=False,
)
report = build_monthly_report(2026, 5, company={'staff': [], 'directions': []})
html = render_report(report, custom, 'html')
self.assertIn('КАСТОМНЫЙ ОТЧЁТ IT', html)
def test_invalid_template_returns_400(self):
from apps.reports.engine import TemplateRenderError, render_report
from apps.reports.models import ReportTemplate
bad = ReportTemplate.objects.create(
code='it-bad',
report_type=ReportTemplate.REPORT_IT_MONTHLY,
name='Bad',
engine=ReportTemplate.ENGINE_DJANGO_HTML,
body_html='{% invalid_tag %}',
is_system=False,
is_default=True,
)
self.classic.is_default = False
self.classic.save(update_fields=['is_default'])
report = build_monthly_report(2026, 5, company={'staff': [], 'directions': []})
with self.assertRaises(TemplateRenderError):
render_report(report, bad, 'html')
login_2fa(self.client, self.head)
r = self.client.get('/reports/it/monthly/2026-05/preview/')
self.assertEqual(r.status_code, 400)
def test_head_can_open_editor_and_copy(self):
login_2fa(self.client, self.head)
r = self.client.get('/reports/templates/')
self.assertEqual(r.status_code, 200)
self.assertContains(r, 'it_monthly_classic')
r = self.client.post(f'/reports/templates/{self.classic.pk}/copy/')
self.assertEqual(r.status_code, 302)
from apps.reports.models import ReportTemplate
copy = ReportTemplate.objects.exclude(pk=self.classic.pk).get()
self.assertFalse(copy.is_system)
self.assertIn('копия', copy.name.lower())
self.assertEqual(copy.engine, ReportTemplate.ENGINE_BLOCKS_V1)
r = self.client.get(f'/reports/templates/{copy.pk}/')
self.assertEqual(r.status_code, 200)
self.assertContains(r, 'Конструктор')
self.assertContains(r, 'Секции отчёта')
# системный нельзя сохранить через POST
r = self.client.post(f'/reports/templates/{self.classic.pk}/', {
'name': 'Hacked', 'description': '', 'body_html': 'x', 'body_md': '',
'blocks': '{}', 'engine': 'blocks_v1',
})
self.assertEqual(r.status_code, 403)
self.classic.refresh_from_db()
self.assertNotEqual(self.classic.name, 'Hacked')
# копию можно сохранить через конструктор
r = self.client.post(f'/reports/templates/{copy.pk}/', {
'name': copy.name,
'description': '',
'engine': 'blocks_v1',
'body_html': '',
'body_md': '',
'blocks': json.dumps({
'labels': {'main_title': 'МОЙ ОТЧЁТ'},
'sections': ['stats', 'summary', 'signature'],
}),
'is_active': 'on',
})
self.assertEqual(r.status_code, 200)
copy.refresh_from_db()
self.assertEqual(copy.blocks['labels']['main_title'], 'МОЙ ОТЧЁТ')
self.assertEqual(copy.blocks['sections'], ['stats', 'summary', 'signature'])
def test_regular_it_forbidden_templates(self):
login_2fa(self.client, self.it_user)
self.assertEqual(self.client.get('/reports/templates/').status_code, 403)
def test_preview_draft_renders_and_rejects_bad_syntax(self):
login_2fa(self.client, self.head)
url = f'/reports/templates/{self.classic.pk}/preview-draft/'
ok = self.client.post(url, {
'engine': 'blocks_v1',
'body_html': '',
'blocks': json.dumps({
'labels': {'main_title': 'ПРЕВЬЮ'},
'sections': ['stats', 'signature'],
}),
'month': '2026-05',
})
self.assertEqual(ok.status_code, 200)
self.assertIn('ПРЕВЬЮ', ok.content.decode())
bad = self.client.post(url, {
'engine': 'django_html',
'body_html': '{% broken %}',
'blocks': '{}',
'month': '2026-05',
})
self.assertEqual(bad.status_code, 400)
self.assertIn('Ошибка', bad.content.decode())
class ReportDocumentTests(TestCase):
def setUp(self):
dept_type, _ = DirectoryType.objects.get_or_create(
code='department', defaults={'name': 'Відділи', 'hierarchy_type': 'strict'},
)
self.dept = Directory.objects.create(
directory_type=dept_type, code='it-docs', name='IT-docs',
)
self.head = make_employee(
last_name='Глава', first_name='Отдел', username='head.docs',
department=self.dept, user_location_type='office',
)
self.member = make_employee(
last_name='Сотр', first_name='Отдел', username='mem.docs',
department=self.dept, manager=self.head, user_location_type='office',
)
it_group = Group.objects.get(name='IT')
self.head.groups.add(it_group)
self.member.groups.add(it_group)
self.client = Client()
def test_hub_shows_documents_block_for_head(self):
login_2fa(self.client, self.head)
r = self.client.get('/reports/')
self.assertEqual(r.status_code, 200)
self.assertContains(r, 'Документы отдела')
self.assertContains(r, 'Создать')
def test_member_cannot_create(self):
login_2fa(self.client, self.member)
r = self.client.post('/reports/documents/create/', {'title': 'План', 'file_type': 'docx'})
self.assertEqual(r.status_code, 403)
def test_head_creates_docx_when_storage_configured(self):
from unittest.mock import patch
from apps.reports.models import ReportDocument
login_2fa(self.client, self.head)
with patch('apps.reports.views._store_report_file', return_value=None):
r = self.client.post('/reports/documents/create/', {'title': 'План работ', 'file_type': 'docx'})
self.assertEqual(r.status_code, 302)
doc = ReportDocument.objects.get(title='План работ')
self.assertTrue(doc.filename.endswith('.docx'))
self.assertEqual(doc.kind, ReportDocument.KIND_EXTRA)
self.assertEqual(doc.status, ReportDocument.STATUS_DRAFT)
self.assertIn('/reports/', r['Location'])
def test_head_upload_docx_and_publish_for_member(self):
from unittest.mock import patch
from django.core.files.uploadedfile import SimpleUploadedFile
from apps.reports.models import ReportDocument
login_2fa(self.client, self.head)
fake = SimpleUploadedFile(
'Отчёт.docx',
b'PK\x03\x04fake-docx',
content_type='application/vnd.openxmlformats-officedocument.wordprocessingml.document',
)
with patch('apps.reports.views._store_report_file', return_value=None):
r = self.client.post(
'/reports/documents/upload/',
{'title': 'Месячный обзор', 'file': fake},
)
self.assertEqual(r.status_code, 302)
doc = ReportDocument.objects.get(title='Месячный обзор')
self.assertEqual(doc.status, ReportDocument.STATUS_DRAFT)
self.assertTrue(doc.filename.endswith('.docx'))
login_2fa(self.client, self.member)
hub = self.client.get('/reports/')
self.assertNotContains(hub, 'Месячный обзор')
login_2fa(self.client, self.head)
pub = self.client.post(f'/reports/documents/{doc.pk}/publish/')
self.assertEqual(pub.status_code, 302)
doc.refresh_from_db()
self.assertTrue(doc.is_published)
login_2fa(self.client, self.member)
hub = self.client.get('/reports/')
self.assertContains(hub, 'Месячный обзор')
def test_member_cannot_upload(self):
from django.core.files.uploadedfile import SimpleUploadedFile
login_2fa(self.client, self.member)
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)