from decimal import Decimal import pytest from rest_framework.test import APIClient from apps.projects.models import Project from apps.time_entries.services.rates import resolve_rate from apps.users.models import User from apps.workspaces.models import PriceUnit, Workspace, WorkspaceMembership, WorkspaceUserRate @pytest.fixture() def api_client(): return APIClient() @pytest.fixture() def owner(db): return User.objects.create_user(mobile="09127770001", password="secret123") @pytest.fixture() def admin(db): return User.objects.create_user(mobile="09127770002", password="secret123") @pytest.fixture() def member(db): return User.objects.create_user(mobile="09127770003", password="secret123") @pytest.fixture() def workspace(owner, admin, member): workspace = Workspace.objects.create(name="Rates", owner=owner) WorkspaceMembership.objects.create(workspace=workspace, user=admin, role=WorkspaceMembership.Role.ADMIN, is_active=True) WorkspaceMembership.objects.create(workspace=workspace, user=member, role=WorkspaceMembership.Role.MEMBER, is_active=True) return workspace @pytest.fixture() def project(workspace, owner, admin, member): return Project.objects.create(workspace=workspace, name="Billing") @pytest.fixture() def price_units(db): PriceUnit.objects.create(code="USD", name="US Dollar", local_name="دلار آمریکا", symbol="$") PriceUnit.objects.create(code="EUR", name="Euro", local_name="یورو", symbol="€") def test_resolve_rate_uses_workspace_user_rate(workspace, project, member): WorkspaceUserRate.objects.create( workspace=workspace, user=member, hourly_rate=Decimal("40.00"), currency="EUR", effective_from=project.created_at, is_active=True, ) hourly_rate, currency = resolve_rate(member, project) assert hourly_rate == Decimal("40.00") assert currency == "EUR" def test_resolve_rate_falls_back_to_workspace_user_rate(workspace, project, member): WorkspaceUserRate.objects.create( workspace=workspace, user=member, hourly_rate=Decimal("40.00"), currency="EUR", effective_from=project.created_at, is_active=True, ) hourly_rate, currency = resolve_rate(member, project) assert hourly_rate == Decimal("40.00") assert currency == "EUR" def test_admin_can_manage_workspace_user_rates(api_client, admin, member, workspace, price_units): api_client.force_authenticate(user=admin) create_response = api_client.post( "/api/workspace-user-rates/", { "workspace_id": str(workspace.id), "user_id": str(member.id), "hourly_rate": "35.50", "currency": "USD", }, format="json", ) assert create_response.status_code == 201 rate_id = create_response.data["id"] assert WorkspaceUserRate.objects.filter(id=rate_id, is_deleted=False).exists() update_response = api_client.patch( f"/api/workspace-user-rates/{rate_id}/", {"hourly_rate": "42.00"}, format="json", ) assert update_response.status_code == 200 assert update_response.data["hourly_rate"] == "42.00" delete_response = api_client.delete(f"/api/workspace-user-rates/{rate_id}/") assert delete_response.status_code == 204 assert WorkspaceUserRate.all_objects.get(id=rate_id).is_deleted is True def test_member_cannot_manage_rates(api_client, member, workspace, price_units): api_client.force_authenticate(user=member) workspace_response = api_client.post( "/api/workspace-user-rates/", { "workspace_id": str(workspace.id), "user_id": str(member.id), "hourly_rate": "25.00", "currency": "USD", }, format="json", ) assert workspace_response.status_code == 403