fix some bugs

This commit is contained in:
javad committed 2026-09-15 09:50:17 +03:30
1 parent b8b3aba855
commit 963fd839dc
22 files changed
+570 -33

No files matched your search

+21
View File
@@ -178,3 +178,24 @@ def test_dispatch_report_presents_delivery_and_engagement_professionally(live_se
page.set_viewport_size({"width":390,"height":844})
expect(page.locator(".dispatch-detail-table tbody tr").first).to_be_visible()
assert page.evaluate("document.documentElement.scrollWidth <= document.documentElement.clientWidth + 1")
page.goto(f"{live_server.url}{reverse('staff_tickets:detail',args=[ticket.public_id])}", wait_until="domcontentloaded")
expect(page.locator(".ticket-dispatch-context")).to_contain_text("استعلام حرفه‌ای")
dismiss_context = page.get_by_role("button", name="بستن پیام ارسال گروهی")
expect(dismiss_context).to_be_visible()
launch = page.locator(".composer-launch")
expect(launch).to_be_visible()
box = launch.bounding_box()
assert box is not None
assert box["x"] >= 0 and box["x"] + box["width"] <= 390
assert box["y"] >= 0 and box["y"] + box["height"] <= 844
assert page.evaluate("document.documentElement.scrollWidth <= document.documentElement.clientWidth + 1")
launch.click()
close_composer = page.get_by_role("button", name="بستن", exact=True)
expect(close_composer).to_be_visible()
close_box = close_composer.bounding_box()
assert close_box is not None
assert close_box["x"] >= 0 and close_box["x"] + close_box["width"] <= 390
assert close_box["y"] >= 0 and close_box["y"] + close_box["height"] <= 844
dismiss_context.click()
expect(page.locator(".ticket-dispatch-context")).to_have_count(0)
+17
View File
@@ -49,6 +49,23 @@ def test_developer_credit_html_requires_explicit_trusted_html_switch(client):
assert '<a href="https://example.com" style="color:red"><span>★</span> توسعه نمونه</a>' in rendered
@pytest.mark.django_db
def test_customer_new_renders_developer_credit_once_directly_below_wizard(client):
customer = User.objects.create_user(
username="wizard-credit-customer", mobile="09120000404", account_type=User.AccountType.CUSTOMER
)
branding = SiteBranding.load()
branding.developer_credit_text = "برنامه نویسی و توسعه: محمد جواد فصاحت | 09202202129"
branding.save()
client.force_login(customer)
content = client.get(reverse("tickets:new")).content.decode()
assert content.count(branding.developer_credit_text) == 1
assert '<footer class="ticket-wizard-credit">' in content
assert content.index('</form>') < content.index('<footer class="ticket-wizard-credit">')
assert '<footer class="site-footer">' not in content
@pytest.mark.django_db
def test_staff_sidebar_brand_is_database_backed(client):
user = User.objects.create_superuser(username="brand-admin", password="safe-password", account_type=User.AccountType.STAFF)
+89 -1
View File
@@ -140,10 +140,11 @@ def test_unit_notification_destinations_normalize_and_reject_duplicate_superviso
slug="notification-destinations",
supervisor_mobile_1="0912 111 2233",
supervisor_mobile_2="0935 222 3344",
supervisor_mobile_4="0990 444 5566",
)
unit.full_clean()
unit.save()
assert unit.supervisor_mobiles == ("+989121112233", "+989352223344")
assert unit.supervisor_mobiles == ("+989121112233", "+989352223344", "+989904445566")
unit.supervisor_mobile_3 = "09121112233"
with pytest.raises(ValidationError, match="قبلاً"):
@@ -158,6 +159,7 @@ def test_new_ticket_sms_is_queued_for_each_unit_supervisor_only_on_creation(cate
unit.supervisor_mobile_1 = "09121112233"
unit.supervisor_mobile_2 = "09352223344"
unit.supervisor_mobile_3 = "09903334455"
unit.supervisor_mobile_4 = "09214445566"
unit.save()
ticket = create_ticket(customer=customer, category=category, subject=" پیگیری بارگیری ", body="متن")
jobs = list(NotificationJob.objects.filter(ticket=ticket, event_type="UNIT_SUPERVISOR_NEW_TICKET").order_by("id"))
@@ -180,12 +182,98 @@ def test_new_ticket_sms_is_queued_for_each_unit_supervisor_only_on_creation(cate
assert customer.local_mobile in message
assert "موضوع: پیگیری بارگیری" in message
assert f"دسته: {category.title}" in message
assert "ارجاع: صف عمومی" in message
supervisor_job_count = NotificationJob.objects.filter(event_type="UNIT_SUPERVISOR_NEW_TICKET").count()
reply_ticket(ticket_id=ticket.pk, user=customer, body="پاسخ جدید")
assert NotificationJob.objects.filter(event_type="UNIT_SUPERVISOR_NEW_TICKET").count() == supervisor_job_count
@pytest.mark.django_db
def test_unit_supervisor_sms_uses_editable_unit_template_and_selected_expert(category, customer, monkeypatch):
from apps.notifications.management.commands.process_notifications import _send_sms
from apps.notifications.models import SMSTemplate
from apps.customers.models import CustomerProfile
unit = category.unit
unit.supervisor_mobile_1 = "09125556677"
unit.save()
expert = make_staff("selected-supervisor-sms-expert")
expert.staff_profile.display_name = "کارشناس انتخابی پیامک"
expert.staff_profile.primary_unit = unit
expert.staff_profile.save()
CustomerProfile.objects.create(user=customer, first_name="سارا", last_name="احمدی")
template = SMSTemplate.objects.create(
unit=unit,
event_type=SMSTemplate.Event.UNIT_SUPERVISOR_NEW_TICKET,
title="الگوی سفارشی سرپرستان",
body="{first_name} {last_name} | {unit_name} | {ticket_number} | {customer_mobile} | {category_name} | {expert_name}",
)
template.full_clean()
ticket = create_ticket(
customer=customer, category=category, subject="تست الگوی قابل تنظیم",
selected_expert=expert, body="متن",
)
job = NotificationJob.objects.get(ticket=ticket, event_type=SMSTemplate.Event.UNIT_SUPERVISOR_NEW_TICKET)
sent = []
class Provider:
def send_message(self, mobile, message):
sent.append((mobile, message))
return type("Result", (), {"success": True})()
monkeypatch.setattr("apps.notifications.management.commands.process_notifications.get_sms_provider", lambda: Provider())
_send_sms(job)
assert sent == [(
"+989125556677",
f"سارا احمدی | {unit.name} | {ticket.ticket_number} | {customer.local_mobile} | {category.title} | کارشناس انتخابی پیامک",
)]
@pytest.mark.django_db
def test_unit_supervisor_sms_handles_missing_customer_names_and_general_queue(category, customer, monkeypatch):
from apps.notifications.management.commands.process_notifications import _send_sms
from apps.notifications.models import SMSTemplate
category.unit.supervisor_mobile_1 = "09125556677"
category.unit.save()
SMSTemplate.objects.create(
unit=category.unit,
event_type=SMSTemplate.Event.UNIT_SUPERVISOR_NEW_TICKET,
title="الگوی نام خالی سرپرستان",
body="نام: {first_name} {last_name}\nنام جایگزین: {recipient_name}\nارجاع: {expert_name}",
)
ticket = create_ticket(customer=customer, category=category, subject="مشتری بدون نام", body="متن")
job = NotificationJob.objects.get(ticket=ticket, event_type=SMSTemplate.Event.UNIT_SUPERVISOR_NEW_TICKET)
sent = []
class Provider:
def send_message(self, mobile, message):
sent.append((mobile, message))
return type("Result", (), {"success": True})()
monkeypatch.setattr("apps.notifications.management.commands.process_notifications.get_sms_provider", lambda: Provider())
_send_sms(job)
assert sent == [(
"+989125556677",
"نام: \nنام جایگزین: ثبت نشده\nارجاع: صف عمومی",
)]
@pytest.mark.django_db
def test_global_supervisor_sms_template_is_seeded_and_editable():
from apps.notifications.models import SMSTemplate
template = SMSTemplate.objects.get(
unit__isnull=True, event_type=SMSTemplate.Event.UNIT_SUPERVISOR_NEW_TICKET
)
assert "{expert_name}" in template.body
assert "{customer_mobile}" in template.body
assert "{category_name}" in template.body
assert {"expert_name", "customer_mobile", "category_name"}.issubset(template.allowed_tags)
@pytest.mark.django_db
def test_staff_created_ticket_also_notifies_owning_unit_supervisors(category, customer):
category.unit.supervisor_mobile_1 = "09124445566"
+72 -1
View File
@@ -12,12 +12,83 @@ from apps.attachments.models import AttachmentPolicy
from apps.customers.models import ContactGroup, CustomerProfile
from apps.notifications.models import NotificationJob
from apps.staff.models import OrganizationalUnit, StaffProfile
from apps.tickets.models import GroupDispatch, Ticket, TicketCategory, TicketTag
from apps.tickets.models import GroupDispatch, Ticket, TicketCategory, TicketEvent, TicketTag
from apps.tickets.policies import TicketPolicy
from apps.tickets.selectors import staff_ticket_rows
from apps.tickets.services import create_ticket, dispatch_group_tickets, refresh_group_dispatch_status
@pytest.mark.django_db
def test_dispatch_owner_can_resolve_all_open_group_tickets_from_report_or_ticket(client):
unit = OrganizationalUnit.objects.create(name="حل گروهی", slug="resolve-dispatch")
unit.permissions.add(*Permission.objects.filter(
content_type__app_label="tickets",
codename__in=("view_ticket", "dispatch_group_tickets", "change_ticket"),
))
staff = make_staff("dispatch-resolver", unit)
first = make_contact("resolve-first", "09120000401")
second = make_contact("resolve-second", "09120000402")
group = ContactGroup.objects.create(name="گروه قابل حل", unit=unit)
first.customer_profile.groups.add(group)
second.customer_profile.groups.add(group)
category = TicketCategory.objects.create(unit=unit, title="پیگیری گروه", slug="resolve-group")
dispatch, tickets = dispatch_group_tickets(
user=staff, unit=unit, contact_group=group, category=category,
subject="پیگیری پاسخ گروه", body="لطفاً پاسخ دهید.",
)
tickets[1].status = Ticket.Status.CLOSED
tickets[1].closed_at = timezone.now()
tickets[1].save(update_fields=["status", "closed_at", "updated_at"])
client.force_login(staff)
report = client.get(reverse("staff_tickets:dispatch_detail", args=[dispatch.public_id]))
report_text = report.content.decode()
assert report.context["open_ticket_count"] == 1
assert "حل‌شدن همه تیکت‌ها (1)" in report_text
ticket_page = client.get(reverse("staff_tickets:detail", args=[tickets[0].public_id]))
assert "وابسته به ارسال گروهی" in ticket_page.content.decode()
assert dispatch.subject in ticket_page.content.decode()
assert "حل‌شدن تمامی تیکت‌های گروه (1)" in ticket_page.content.decode()
response = client.post(
reverse("staff_tickets:dispatch_resolve", args=[dispatch.public_id]),
{"return_ticket": tickets[0].public_id},
)
assert response.status_code == 302
assert response.url == reverse("staff_tickets:detail", args=[tickets[0].public_id])
tickets[0].refresh_from_db()
tickets[1].refresh_from_db()
assert tickets[0].status == Ticket.Status.RESOLVED
assert tickets[0].resolved_at is not None
assert tickets[1].status == Ticket.Status.CLOSED
assert TicketEvent.objects.filter(
ticket=tickets[0], event_type=TicketEvent.Type.STATUS_CHANGED,
metadata__group_dispatch_id=dispatch.pk,
).exists()
@pytest.mark.django_db
def test_dispatch_bulk_resolve_requires_change_ticket_permission(client):
unit = OrganizationalUnit.objects.create(name="گروه فقط مشاهده", slug="read-only-dispatch")
unit.permissions.add(*Permission.objects.filter(
content_type__app_label="tickets", codename__in=("view_ticket", "dispatch_group_tickets")
))
staff = make_staff("read-only-dispatcher", unit)
customer = make_contact("read-only-recipient", "09120000403")
group = ContactGroup.objects.create(name="گروه فقط خواندنی", unit=unit)
customer.customer_profile.groups.add(group)
category = TicketCategory.objects.create(unit=unit, title="فقط مشاهده", slug="read-only-group")
dispatch, tickets = dispatch_group_tickets(
user=staff, unit=unit, contact_group=group, category=category,
subject="عملیات فقط مشاهده", body="متن عملیات",
)
client.force_login(staff)
assert client.get(reverse("staff_tickets:dispatch_detail", args=[dispatch.public_id])).context["can_resolve_dispatch"] is False
client.post(reverse("staff_tickets:dispatch_resolve", args=[dispatch.public_id]))
tickets[0].refresh_from_db()
assert tickets[0].status != Ticket.Status.RESOLVED
def make_staff(username, unit):
user = User.objects.create_user(username=username, account_type=User.AccountType.STAFF)
StaffProfile.objects.create(user=user, display_name=username, primary_unit=unit)