fix customer view and edit
This commit is contained in:
1 parent
81335b1439
commit
d5c22a39dc
13 files changed
+329
-81
No files matched your search
@@ -1,15 +1,19 @@
|
||||
import base64
|
||||
from datetime import timedelta
|
||||
from importlib import import_module
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
from django.apps import apps as django_apps
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.db import connection
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
|
||||
from apps.accounts.models import MagicLinkAccessLog, TicketMagicLink, User
|
||||
from apps.attachments.models import AttachmentPolicy
|
||||
from apps.customers.models import ContactGroup, CustomerProfile
|
||||
from apps.customers.models import ContactEditLog, ContactGroup, CustomerProfile
|
||||
from apps.notifications.models import NotificationJob
|
||||
from apps.staff.models import OrganizationalUnit, StaffProfile
|
||||
from apps.tickets.models import GroupDispatch, Ticket, TicketCategory, TicketEvent, TicketTag
|
||||
@@ -314,7 +318,7 @@ def test_dispatch_pages_are_available_only_to_the_dispatch_unit(client):
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_staff_operations_pages_respect_unit_scoped_permissions(client):
|
||||
def test_staff_contacts_are_shared_while_groups_remain_unit_scoped(client):
|
||||
unit = OrganizationalUnit.objects.create(name="عملیات", slug="operations")
|
||||
other = OrganizationalUnit.objects.create(name="واحد محرمانه", slug="private-unit")
|
||||
codenames = [
|
||||
@@ -336,7 +340,9 @@ def test_staff_operations_pages_respect_unit_scoped_permissions(client):
|
||||
response = client.get(reverse("staff_tickets:contacts"))
|
||||
assert response.status_code == 200
|
||||
assert "visible-contact" in response.content.decode()
|
||||
assert "hidden-contact" not in response.content.decode()
|
||||
assert "hidden-contact" in response.content.decode()
|
||||
assert client.get(reverse("staff_tickets:contact_edit", args=[hidden.public_id])).status_code == 200
|
||||
assert hidden_group.name not in response.content.decode()
|
||||
assert client.get(reverse("staff_tickets:groups")).status_code == 200
|
||||
assert client.get(reverse("staff_tickets:magic_audit")).status_code == 200
|
||||
assert client.get(reverse("staff_tickets:sms_templates")).status_code == 200
|
||||
@@ -348,7 +354,7 @@ def test_superuser_without_staff_profile_can_open_all_staff_contacts(client):
|
||||
contact = make_contact("admin-visible-contact", "09120000503")
|
||||
group = ContactGroup.objects.create(name="گروه قابل مشاهده مدیر", unit=unit)
|
||||
contact.customer_profile.groups.add(group)
|
||||
User.objects.create_user(
|
||||
incomplete = User.objects.create_user(
|
||||
username="customer-without-profile",
|
||||
mobile="09120000504",
|
||||
account_type=User.AccountType.CUSTOMER,
|
||||
@@ -364,7 +370,12 @@ def test_superuser_without_staff_profile_can_open_all_staff_contacts(client):
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "admin-visible-contact" in response.content.decode()
|
||||
assert "customer-without-profile" not in response.content.decode()
|
||||
assert "09120000504" in response.content.decode()
|
||||
searched = client.get(reverse("staff_tickets:contacts"), {"q": "09120000504"})
|
||||
assert searched.status_code == 200
|
||||
assert str(incomplete.public_id) in searched.content.decode()
|
||||
assert client.get(reverse("staff_tickets:contact_edit", args=[incomplete.public_id])).status_code == 200
|
||||
assert CustomerProfile.objects.filter(user=incomplete).exists()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -434,8 +445,8 @@ def test_staff_can_classify_and_edit_ticket_customer_without_an_existing_group(c
|
||||
"province": "",
|
||||
"city": "",
|
||||
})
|
||||
assert missing_group.status_code == 200
|
||||
assert missing_group.context["form"]["groups"].errors
|
||||
assert missing_group.status_code == 302
|
||||
assert not contact.customer_profile.groups.exists()
|
||||
|
||||
saved = client.post(url, {
|
||||
"mobile": contact.local_mobile,
|
||||
@@ -452,21 +463,141 @@ def test_staff_can_classify_and_edit_ticket_customer_without_an_existing_group(c
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_staff_cannot_edit_ungrouped_customer_from_an_unrelated_ticket_unit(client):
|
||||
ticket_unit = OrganizationalUnit.objects.create(name="واحد مالک مشتری", slug="ungrouped-owner-unit")
|
||||
unrelated_unit = OrganizationalUnit.objects.create(name="واحد نامرتبط مشتری", slug="ungrouped-unrelated-unit")
|
||||
unrelated_unit.permissions.add(*Permission.objects.filter(
|
||||
content_type__app_label="customers",
|
||||
codename__in=("view_customerprofile", "change_customerprofile"),
|
||||
))
|
||||
owner = make_staff("ungrouped-owner-agent", ticket_unit)
|
||||
outsider = make_staff("ungrouped-outsider-agent", unrelated_unit)
|
||||
contact = make_contact("private-ungrouped-customer", "09120000516")
|
||||
category = TicketCategory.objects.create(unit=ticket_unit, title="درخواست محرمانه", slug="private-ungrouped-ticket")
|
||||
create_ticket(customer=contact, created_by=owner, unit=ticket_unit, category=category, subject="محرمانه", body="شرح")
|
||||
client.force_login(outsider)
|
||||
def test_legacy_ungrouped_contact_is_searchable_editable_and_audited(client):
|
||||
unit = OrganizationalUnit.objects.create(name="واحد جستوجوی مخاطب", slug="shared-contact-search")
|
||||
staff = make_staff("shared-contact-editor", unit)
|
||||
contact = make_contact("legacy-ungrouped-contact", "09120000561")
|
||||
incomplete = User.objects.create_user(username="legacy-without-profile", mobile="09120000564", first_name="نام قدیمی ناقص", account_type=User.AccountType.CUSTOMER)
|
||||
edit_url = reverse("staff_tickets:contact_edit", args=[contact.public_id])
|
||||
client.force_login(staff)
|
||||
|
||||
assert client.get(reverse("staff_tickets:contact_edit", args=[contact.public_id])).status_code == 404
|
||||
for query in ("legacy-ungrouped-contact", "09120000561"):
|
||||
result = client.get(reverse("staff_tickets:contacts"), {"q": query})
|
||||
assert result.status_code == 200
|
||||
assert str(contact.public_id) in result.content.decode()
|
||||
assert str(incomplete.public_id) in client.get(reverse("staff_tickets:contacts"), {"q": "09120000564"}).content.decode()
|
||||
assert str(incomplete.public_id) in client.get(reverse("staff_tickets:contacts"), {"q": "نام قدیمی ناقص"}).content.decode()
|
||||
assert client.get(reverse("staff_tickets:contact_edit", args=[incomplete.public_id])).status_code == 200
|
||||
assert CustomerProfile.objects.filter(user=incomplete).exists()
|
||||
assert incomplete.customer_profile.first_name == "نام قدیمی ناقص"
|
||||
assert client.get(edit_url).status_code == 200
|
||||
|
||||
saved = client.post(edit_url, {
|
||||
"mobile": "09120000562",
|
||||
"first_name": "مخاطب اصلاحشده",
|
||||
"last_name": "",
|
||||
"organization_name": "",
|
||||
"stakeholder_type": CustomerProfile.StakeholderType.CUSTOMER,
|
||||
"groups": [],
|
||||
"province": "",
|
||||
"city": "",
|
||||
})
|
||||
assert saved.status_code == 302
|
||||
contact.refresh_from_db()
|
||||
contact.customer_profile.refresh_from_db()
|
||||
assert contact.mobile == "+989120000562"
|
||||
assert contact.customer_profile.first_name == "مخاطب اصلاحشده"
|
||||
assert not contact.customer_profile.groups.exists()
|
||||
assert User.objects.filter(pk=contact.pk).exists()
|
||||
assert CustomerProfile.objects.filter(user=contact).exists()
|
||||
log = ContactEditLog.objects.get(contact=contact.customer_profile)
|
||||
assert log.actor == staff
|
||||
assert log.actor_name == staff.username
|
||||
assert log.contact_public_id == contact.public_id
|
||||
assert {change["field"] for change in log.changes} == {"شماره همراه", "نام"}
|
||||
assert staff.username in client.get(edit_url).content.decode()
|
||||
assert ContactEditLog.objects.count() == 1
|
||||
|
||||
repeated = client.post(edit_url, {
|
||||
"mobile": "09120000562",
|
||||
"first_name": "مخاطب اصلاحشده",
|
||||
"last_name": "",
|
||||
"organization_name": "",
|
||||
"stakeholder_type": CustomerProfile.StakeholderType.CUSTOMER,
|
||||
"groups": [],
|
||||
"province": "",
|
||||
"city": "",
|
||||
})
|
||||
assert repeated.status_code == 302
|
||||
assert ContactEditLog.objects.count() == 1
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_customer_profile_backfill_is_repeatable_and_keeps_existing_contacts():
|
||||
existing = make_contact("existing-profile-backfill", "09120000565")
|
||||
incomplete = User.objects.create_user(username="missing-profile-backfill", mobile="09120000566", first_name="نام قدیمی", last_name="مشتری", account_type=User.AccountType.CUSTOMER)
|
||||
migration = import_module("apps.customers.migrations.0006_backfill_customer_profiles")
|
||||
editor = SimpleNamespace(connection=connection)
|
||||
|
||||
migration.backfill_customer_profiles(django_apps, editor)
|
||||
migration.backfill_customer_profiles(django_apps, editor)
|
||||
|
||||
assert CustomerProfile.objects.filter(user=existing, first_name="existing-profile-backfill").count() == 1
|
||||
assert CustomerProfile.objects.filter(user=incomplete).count() == 1
|
||||
assert incomplete.customer_profile.first_name == "نام قدیمی"
|
||||
assert not incomplete.customer_profile.groups.exists()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_shared_contact_edit_preserves_other_unit_groups_and_ticket_counts(client):
|
||||
sales = OrganizationalUnit.objects.create(name="فروش مشترک", slug="shared-sales")
|
||||
finance = OrganizationalUnit.objects.create(name="حسابداری مشترک", slug="shared-finance")
|
||||
view_ticket = Permission.objects.get(content_type__app_label="tickets", codename="view_ticket")
|
||||
sales.permissions.add(view_ticket)
|
||||
finance.permissions.add(view_ticket)
|
||||
sales_staff = make_staff("shared-sales-agent", sales)
|
||||
finance_staff = make_staff("shared-finance-agent", finance)
|
||||
contact = make_contact("shared-contact", "09120000563")
|
||||
finance_group = ContactGroup.objects.create(name="مخاطبان حسابداری مشترک", unit=finance)
|
||||
sales_category = TicketCategory.objects.create(unit=sales, title="فروش مشترک", slug="shared-sales-ticket")
|
||||
finance_category = TicketCategory.objects.create(unit=finance, title="مالی مشترک", slug="shared-finance-ticket")
|
||||
sales_ticket = create_ticket(customer=contact, created_by=sales_staff, unit=sales, category=sales_category, subject="درخواست فروش", body="شرح")
|
||||
finance_ticket = create_ticket(customer=contact, created_by=finance_staff, unit=finance, category=finance_category, subject="درخواست مالی", body="شرح")
|
||||
client.force_login(sales_staff)
|
||||
assert client.get(reverse("staff_tickets:contact_edit", args=[contact.public_id])).status_code == 200
|
||||
assert client.get(reverse("staff_tickets:detail", args=[finance_ticket.public_id])).status_code == 403
|
||||
contact.customer_profile.groups.add(finance_group)
|
||||
|
||||
directory = client.get(reverse("staff_tickets:contacts"), {"q": "shared-contact"})
|
||||
assert directory.status_code == 200
|
||||
assert directory.context["contacts"][0].ticket_count == 1
|
||||
assert finance_group.name not in directory.content.decode()
|
||||
assert client.get(reverse("staff_tickets:detail", args=[sales_ticket.public_id])).status_code == 200
|
||||
assert client.get(reverse("staff_tickets:detail", args=[finance_ticket.public_id])).status_code == 403
|
||||
|
||||
forged = client.post(reverse("staff_tickets:contact_edit", args=[contact.public_id]), {
|
||||
"mobile": contact.local_mobile,
|
||||
"first_name": "نام جعلی",
|
||||
"last_name": "",
|
||||
"organization_name": "",
|
||||
"stakeholder_type": CustomerProfile.StakeholderType.CUSTOMER,
|
||||
"groups": [finance_group.pk],
|
||||
"province": "",
|
||||
"city": "",
|
||||
})
|
||||
assert forged.status_code == 200
|
||||
assert forged.context["form"]["groups"].errors
|
||||
contact.customer_profile.refresh_from_db()
|
||||
assert contact.customer_profile.first_name == "shared-contact"
|
||||
assert ContactEditLog.objects.count() == 0
|
||||
|
||||
edited = client.post(reverse("staff_tickets:contact_edit", args=[contact.public_id]), {
|
||||
"mobile": contact.local_mobile,
|
||||
"first_name": "نام تازه",
|
||||
"last_name": "",
|
||||
"organization_name": "",
|
||||
"stakeholder_type": CustomerProfile.StakeholderType.CUSTOMER,
|
||||
"groups": [],
|
||||
"province": "",
|
||||
"city": "",
|
||||
})
|
||||
assert edited.status_code == 302
|
||||
assert contact.customer_profile.groups.filter(pk=finance_group.pk).exists()
|
||||
assert ContactEditLog.objects.filter(contact=contact.customer_profile, actor=sales_staff).exists()
|
||||
|
||||
client.force_login(contact)
|
||||
assert client.get(reverse("staff_tickets:contacts")).status_code == 403
|
||||
assert client.get(reverse("staff_tickets:contact_edit", args=[contact.public_id])).status_code == 403
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
||||
+6
-3
@@ -10,7 +10,7 @@ from apps.tickets.models import Ticket, TicketCategory
|
||||
from apps.tickets.services import create_ticket, claim_ticket, reply_ticket
|
||||
from apps.messaging.models import Message,MessageReadReceipt
|
||||
from apps.notifications.models import NotificationJob
|
||||
from apps.customers.models import CustomerProfile
|
||||
from apps.customers.models import ContactEditLog, CustomerProfile
|
||||
from apps.core.persian_text import normalize_persian_text
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -664,7 +664,8 @@ def test_staff_customer_directory_search_and_ticket_stats(client):
|
||||
response=client.get(legacy.url)
|
||||
content=response.content.decode()
|
||||
assert response.status_code==200 and "زهرا محمدی" in content and "09121234567" in content
|
||||
assert "کل تیکت" in content and "نیازمند اقدام" in content
|
||||
assert "تیکت قابل مشاهده" in content and "نیازمند اقدام" in content
|
||||
assert response.context["contacts"][0].ticket_count == 0
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_staff_customer_live_search_returns_profile_data(client):
|
||||
@@ -687,9 +688,11 @@ def test_staff_create_uses_existing_customer_selected_by_public_id(client):
|
||||
StaffProfile.objects.create(user=expert,display_name="کارشناس انتخاب")
|
||||
expert.user_permissions.add(Permission.objects.get(content_type__app_label="tickets",codename="add_ticket"))
|
||||
client.force_login(expert)
|
||||
response=client.post(reverse("staff_tickets:create"),{"customer_id":customer.public_id,"customer_mobile":"09125550000","category":category.pk,"subject":"پیگیری مشتری موجود","province":"گیلان","city":"رشت","body":"شرح کامل پیگیری"})
|
||||
response=client.post(reverse("staff_tickets:create"),{"customer_id":customer.public_id,"customer_mobile":"09125550000","first_name":"مریم تازه","category":category.pk,"subject":"پیگیری مشتری موجود","province":"گیلان","city":"رشت","body":"شرح کامل پیگیری"})
|
||||
assert response.status_code==302
|
||||
assert customer.customer_tickets.filter(subject="پیگیری مشتری موجود").exists()
|
||||
log = ContactEditLog.objects.get(contact__user=customer, actor=expert)
|
||||
assert {"field": "نام", "before": "مریم", "after": "مریم تازه"} in log.changes
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_staff_create_requires_name_for_unknown_mobile(client):
|
||||
|
||||
Reference in new issue
Block a user