Files
timerr/apps/reports/test_reports.py
T
MS.Kondratev 0feeaad4fa
Build and publish Docker image / publish (push) Successful in 12s
feat: export consolidated user report PDF
2026-07-15 17:25:40 +05:00

78 lines
4.2 KiB
Python

from datetime import date
from decimal import Decimal
from django.test import TestCase
from django.urls import reverse
from apps.accounts.models import User
from apps.companies.models import Company
from apps.projects.models import Project, ProjectMember
from apps.tasks.models import Task
from .services import build_monthly_report
class MonthlyReportTests(TestCase):
def setUp(self):
self.user = User.objects.create_user(username="worker", password="password123", must_change_password=False)
self.company_a = Company.objects.create(name="Клиент A")
self.company_b = Company.objects.create(name="Клиент B")
self.project_a = Project.objects.create(company=self.company_a, name="Проект A")
self.project_b = Project.objects.create(company=self.company_b, name="Проект B")
for project in (self.project_a, self.project_b):
ProjectMember.objects.create(project=project, user=self.user, hourly_rate=Decimal("1000.00"))
self._task(self.project_a, "Выполнена A", Task.Status.COMPLETED, 60)
self._task(self.project_b, "Выполнена B", Task.Status.COMPLETED, 30)
self._task(self.project_a, "Отменена", Task.Status.CANCELLED, 120)
def _task(self, project, title, status, minutes):
return Task.objects.create(
project=project, assignee=self.user, title=title, description="Описание", work_date=date(2026, 7, 10),
duration_minutes=minutes, hourly_rate=Decimal("1000.00"),
amount=Task.calculate_amount(minutes, Decimal("1000.00")), status=status, created_by=self.user,
)
def test_internal_report_groups_companies_and_excludes_cancelled(self):
report = build_monthly_report(self.user, 2026, 7)
self.assertEqual(len(report["companies"]), 2)
self.assertEqual(report["total_minutes"], 90)
self.assertEqual(report["total_amount"], Decimal("1500.00"))
def test_client_report_contains_only_selected_company(self):
report = build_monthly_report(self.user, 2026, 7, company=self.company_a)
self.assertEqual(len(report["companies"]), 1)
self.assertEqual(report["companies"][0]["company"], self.company_a)
self.assertEqual(report["total_minutes"], 60)
def test_worker_cannot_request_other_users_report(self):
other = User.objects.create_user(username="other", password="password123", must_change_password=False)
self.client.force_login(self.user)
response = self.client.get(reverse("monthly-report"), {"user": other.pk, "year": 2026, "month": 7})
self.assertEqual(response.context["report"]["user"], self.user)
def test_worker_does_not_see_client_pdf_button(self):
self.client.force_login(self.user)
response = self.client.get(reverse("monthly-report"), {"year": 2026, "month": 7})
self.assertNotContains(response, "PDF для клиента")
def test_client_pdf_is_generated_for_one_company(self):
self.client.force_login(self.user)
response = self.client.get(reverse("company-report-pdf", args=(self.company_a.pk,)), {"year": 2026, "month": 7})
self.assertEqual(response.status_code, 200)
self.assertEqual(response["Content-Type"], "application/pdf")
self.assertTrue(response.content.startswith(b"%PDF"))
def test_user_pdf_is_generated_for_all_companies(self):
self.client.force_login(self.user)
response = self.client.get(reverse("user-report-pdf"), {"year": 2026, "month": 7})
self.assertEqual(response.status_code, 200)
self.assertEqual(response["Content-Type"], "application/pdf")
self.assertEqual(response["Content-Disposition"], 'attachment; filename="report-worker-2026-07.pdf"')
self.assertTrue(response.content.startswith(b"%PDF"))
def test_worker_cannot_export_another_users_general_report(self):
other = User.objects.create_user(username="other", password="password123", must_change_password=False)
self.client.force_login(self.user)
response = self.client.get(reverse("user-report-pdf"), {"user": other.pk, "year": 2026, "month": 7})
self.assertEqual(response["Content-Disposition"], 'attachment; filename="report-worker-2026-07.pdf"')