bale group for orgs, and sms for each org
This commit is contained in:
1 parent
a18a68578f
commit
1ee83374fd
13 files changed
+262
-20
No files matched your search
+7
-2
@@ -13,12 +13,17 @@ class UnitMembershipInline(admin.TabularInline):
|
||||
|
||||
@admin.register(OrganizationalUnit)
|
||||
class OrganizationalUnitAdmin(admin.ModelAdmin):
|
||||
list_display = ("name", "slug", "sort_order", "show_in_customer_form", "accepts_external_tickets", "is_active", "updated_at")
|
||||
list_display = ("name", "slug", "bale_chat_id", "sort_order", "show_in_customer_form", "accepts_external_tickets", "is_active", "updated_at")
|
||||
list_filter = ("show_in_customer_form", "accepts_external_tickets", "is_active")
|
||||
search_fields = ("name", "slug")
|
||||
search_fields = ("name", "slug", "bale_chat_id", "supervisor_mobile_1", "supervisor_mobile_2", "supervisor_mobile_3")
|
||||
list_editable = ("sort_order", "show_in_customer_form", "accepts_external_tickets", "is_active")
|
||||
filter_horizontal = ("permissions",)
|
||||
inlines = (UnitMembershipInline,)
|
||||
fieldsets = (
|
||||
("مشخصات واحد", {"fields": ("name", "slug", "description", "search_keywords", "sort_order")}),
|
||||
("اطلاعرسانی بله و سرپرستان", {"fields": ("bale_chat_id", "supervisor_mobile_1", "supervisor_mobile_2", "supervisor_mobile_3")}),
|
||||
("نمایش و دسترسی", {"fields": ("permissions", "show_in_customer_form", "accepts_external_tickets", "is_active")}),
|
||||
)
|
||||
|
||||
|
||||
@admin.register(UnitMembership)
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# Generated by Django 5.1.1 on 2026-09-13
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("staff", "0009_alter_staffprofile_sort_order"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="organizationalunit",
|
||||
name="bale_chat_id",
|
||||
field=models.CharField(blank=True, help_text="شناسه chat_id گروه بله این واحد؛ در صورت خالی بودن، اعلان گروهی بله برای این واحد ارسال نمیشود.", max_length=100, verbose_name="شناسه گروه بله"),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="organizationalunit",
|
||||
name="supervisor_mobile_1",
|
||||
field=models.CharField(blank=True, max_length=16, verbose_name="شماره همراه سرپرست اول"),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="organizationalunit",
|
||||
name="supervisor_mobile_2",
|
||||
field=models.CharField(blank=True, max_length=16, verbose_name="شماره همراه سرپرست دوم"),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="organizationalunit",
|
||||
name="supervisor_mobile_3",
|
||||
field=models.CharField(blank=True, max_length=16, verbose_name="شماره همراه سرپرست سوم"),
|
||||
),
|
||||
]
|
||||
@@ -1,5 +1,6 @@
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import models
|
||||
from apps.core.models import PersianTextNormalizationMixin, TimeStampedModel
|
||||
|
||||
@@ -26,6 +27,15 @@ class OrganizationalUnit(PersianTextNormalizationMixin, TimeStampedModel):
|
||||
help_text="اگر غیرفعال باشد، مشتریان نمیتوانند مستقیماً برای این واحد تیکت ثبت کنند.",
|
||||
)
|
||||
show_in_customer_form = models.BooleanField(default=True, verbose_name="نمایش در فرم مشتری", help_text="در صورت خاموش بودن، واحد در فرم ثبت تیکت و جستجوی مشتری نمایش داده نمیشود.")
|
||||
bale_chat_id = models.CharField(
|
||||
max_length=100,
|
||||
blank=True,
|
||||
verbose_name="شناسه گروه بله",
|
||||
help_text="شناسه chat_id گروه بله این واحد؛ در صورت خالی بودن، اعلان گروهی بله برای این واحد ارسال نمیشود.",
|
||||
)
|
||||
supervisor_mobile_1 = models.CharField(max_length=16, blank=True, verbose_name="شماره همراه سرپرست اول")
|
||||
supervisor_mobile_2 = models.CharField(max_length=16, blank=True, verbose_name="شماره همراه سرپرست دوم")
|
||||
supervisor_mobile_3 = models.CharField(max_length=16, blank=True, verbose_name="شماره همراه سرپرست سوم")
|
||||
|
||||
class Meta:
|
||||
ordering = ["sort_order", "name", "pk"]
|
||||
@@ -35,6 +45,47 @@ class OrganizationalUnit(PersianTextNormalizationMixin, TimeStampedModel):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
@property
|
||||
def supervisor_mobiles(self):
|
||||
"""Return configured supervisor mobiles once, in their configured order."""
|
||||
return tuple(dict.fromkeys(filter(None, (
|
||||
self.supervisor_mobile_1,
|
||||
self.supervisor_mobile_2,
|
||||
self.supervisor_mobile_3,
|
||||
))))
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
from apps.accounts.models import normalize_mobile
|
||||
|
||||
normalized = []
|
||||
errors = {}
|
||||
for field_name in ("supervisor_mobile_1", "supervisor_mobile_2", "supervisor_mobile_3"):
|
||||
value = getattr(self, field_name, "").strip()
|
||||
if not value:
|
||||
setattr(self, field_name, "")
|
||||
continue
|
||||
try:
|
||||
value = normalize_mobile(value)
|
||||
except ValueError as exc:
|
||||
errors[field_name] = str(exc)
|
||||
continue
|
||||
setattr(self, field_name, value)
|
||||
if value in normalized:
|
||||
errors[field_name] = "این شماره همراه قبلاً برای همین واحد وارد شده است."
|
||||
normalized.append(value)
|
||||
if errors:
|
||||
raise ValidationError(errors)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
from apps.accounts.models import normalize_mobile
|
||||
|
||||
for field_name in ("supervisor_mobile_1", "supervisor_mobile_2", "supervisor_mobile_3"):
|
||||
value = getattr(self, field_name, "").strip()
|
||||
setattr(self, field_name, normalize_mobile(value) if value else "")
|
||||
self.bale_chat_id = self.bale_chat_id.strip()
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class UnitMembership(TimeStampedModel):
|
||||
"""Dynamic per-user unit membership with optional extra permissions."""
|
||||
|
||||
Reference in new issue
Block a user