feat(workspaces): add bulk member import endpoints
Some checks failed
Backend CI/CD / test (push) Has been cancelled
Backend CI/CD / deploy (push) Has been cancelled

This commit is contained in:
2026-06-18 22:53:34 +03:30
parent 027afb7e23
commit 95f5e85e44
2 changed files with 445 additions and 3 deletions

View File

@@ -1,4 +1,5 @@
from datetime import timedelta
from decimal import Decimal
from django.utils import timezone
from rest_framework.test import APITestCase
@@ -7,7 +8,13 @@ from apps.clients.models import Client
from apps.projects.models import Project
from apps.tags.models import Tag
from apps.users.models import User
from apps.workspaces.models import Workspace, WorkspaceMembership
from apps.workspaces.models import (
HourlyRateHistory,
PriceUnit,
Workspace,
WorkspaceMembership,
WorkspaceUserRate,
)
class WorkspaceCapabilityTests(APITestCase):
@@ -298,6 +305,139 @@ class WorkspaceCapabilityTests(APITestCase):
self.assertEqual(update_response.status_code, 403)
self.assertEqual(delete_response.status_code, 403)
def test_owner_can_validate_and_commit_member_import_with_rate(self):
PriceUnit.objects.create(code="IRT", name="Toman", local_name="Toman", symbol="Toman")
target = self._user(21)
self.client.force_authenticate(user=self.owner)
validate_response = self.client.post(
"/api/workspace-memberships/import/validate/",
{
"workspace": str(self.workspace.id),
"rows": [
{
"line": 2,
"mobile": target.mobile,
"role": "member",
"hourly_rate": "150000",
"currency": "IRT",
}
],
},
format="json",
)
self.assertEqual(validate_response.status_code, 200)
self.assertTrue(validate_response.data["can_commit"])
self.assertEqual(validate_response.data["summary"]["valid"], 1)
self.assertTrue(validate_response.data["import_token"])
commit_response = self.client.post(
"/api/workspace-memberships/import/commit/",
{
"workspace": str(self.workspace.id),
"import_token": validate_response.data["import_token"],
},
format="json",
)
self.assertEqual(commit_response.status_code, 201)
self.assertEqual(commit_response.data["created_memberships"], 1)
self.assertEqual(commit_response.data["created_or_updated_rates"], 1)
self.assertTrue(
WorkspaceMembership.objects.filter(
workspace=self.workspace,
user=target,
role=WorkspaceMembership.Role.MEMBER,
is_active=True,
).exists()
)
rate = WorkspaceUserRate.objects.get(workspace=self.workspace, user=target)
self.assertEqual(rate.hourly_rate, Decimal("150000.00"))
self.assertEqual(rate.currency, "IRT")
self.assertTrue(
HourlyRateHistory.objects.filter(
workspace=self.workspace,
user=target,
hourly_rate=Decimal("150000.00"),
currency="IRT",
).exists()
)
def test_member_import_rejects_invalid_rows(self):
PriceUnit.objects.create(code="USD", name="Dollar", local_name="Dollar", symbol="$")
target = self._user(22)
self.client.force_authenticate(user=self.owner)
response = self.client.post(
"/api/workspace-memberships/import/validate/",
{
"workspace": str(self.workspace.id),
"rows": [
{"line": 2, "mobile": "", "role": "member"},
{"line": 3, "mobile": "09120000000", "role": "member"},
{"line": 4, "mobile": target.mobile, "role": "owner"},
{"line": 5, "mobile": target.mobile, "role": "member"},
{"line": 6, "mobile": self.member.mobile, "role": "member"},
{"line": 7, "mobile": self.guest.mobile, "role": "guest", "hourly_rate": "10"},
{"line": 8, "mobile": self.admin.mobile, "role": "admin", "hourly_rate": "0", "currency": "USD"},
{"line": 9, "mobile": self.extra_owner.mobile, "role": "guest", "hourly_rate": "10", "currency": "XXX"},
],
},
format="json",
)
self.assertEqual(response.status_code, 200)
self.assertFalse(response.data["can_commit"])
self.assertIsNone(response.data["import_token"])
self.assertEqual(response.data["summary"]["invalid"], 8)
def test_admin_import_follows_role_assignment_rules(self):
target = self._user(23)
self.client.force_authenticate(user=self.admin)
response = self.client.post(
"/api/workspace-memberships/import/validate/",
{
"workspace": str(self.workspace.id),
"rows": [{"line": 2, "mobile": target.mobile, "role": "admin"}],
},
format="json",
)
self.assertEqual(response.status_code, 200)
self.assertFalse(response.data["can_commit"])
self.assertIn("permission", response.data["rows"][0]["messages"][0].lower())
def test_member_cannot_import_workspace_members(self):
target = self._user(24)
self.client.force_authenticate(user=self.member)
response = self.client.post(
"/api/workspace-memberships/import/validate/",
{
"workspace": str(self.workspace.id),
"rows": [{"line": 2, "mobile": target.mobile, "role": "member"}],
},
format="json",
)
self.assertEqual(response.status_code, 403)
def test_import_commit_rejects_expired_token(self):
self.client.force_authenticate(user=self.owner)
response = self.client.post(
"/api/workspace-memberships/import/commit/",
{
"workspace": str(self.workspace.id),
"import_token": "invalid-token",
},
format="json",
)
self.assertEqual(response.status_code, 400)
def test_admin_can_delete_only_owned_clients_tags_and_projects(self):
self.client.force_authenticate(user=self.owner)
owner_client_response = self.client.post(