Files
customerTicket/apps/core/models.py
T
2026-09-28 19:37:19 +03:30

171 lines
9.4 KiB
Python

import uuid
from django.db import models
from .persian_text import normalize_persian_text
class PersianTextNormalizationMixin:
"""Normalize only explicitly listed human-authored model fields on save."""
persian_text_fields = {}
def save(self, *args, **kwargs):
update_fields = kwargs.get("update_fields")
for field_name, multiline in self.persian_text_fields.items():
if update_fields is None or field_name in update_fields:
value = getattr(self, field_name, None)
if value is not None:
setattr(
self,
field_name,
normalize_persian_text(value, multiline=multiline),
)
return super().save(*args, **kwargs)
class TimeStampedModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class PublicIdModel(models.Model):
public_id = models.UUIDField(default=uuid.uuid4, unique=True, editable=False)
class Meta:
abstract = True
class SingletonConfiguration(models.Model):
class Meta:
abstract = True
def save(self, *args, **kwargs):
self.pk = 1
return super().save(*args, **kwargs)
@classmethod
def load(cls):
return cls.objects.filter(pk=1).first() or cls()
class SiteBranding(SingletonConfiguration):
"""Database-backed copy shared by customer, authentication and staff pages."""
site_title = models.CharField(max_length=160, default="گفت‌وگو با واحد فروش")
header_title = models.CharField(max_length=160, default="گفت‌وگو با واحد فروش")
header_subtitle = models.CharField(max_length=200, default="درخواست روشن، پیگیری مطمئن")
staff_brand_title = models.CharField(max_length=160, default="گفت‌وگو با واحد فروش")
staff_brand_subtitle = models.CharField(max_length=200, default="مرکز ارتباط با مشتری")
staff_panel_label = models.CharField(max_length=120, default="پنل کارشناسان")
pwa_title = models.CharField(max_length=80, default="واحد فروش")
company_logo = models.ImageField(upload_to="branding/logo/", blank=True, verbose_name="لوگوی اصلی")
favicon = models.ImageField(upload_to="branding/favicon/", blank=True, verbose_name="Favicon")
pwa_icon = models.ImageField(upload_to="branding/pwa/", blank=True, verbose_name="آیکون PWA")
pwa_name = models.CharField(max_length=160, default="گفت‌وگو با واحد فروش", verbose_name="نام کامل PWA")
pwa_description = models.CharField(max_length=300, default="ثبت و پیگیری درخواست‌های فروش لوله گستر گلپایگان", verbose_name="توضیح PWA")
pwa_install_label = models.CharField(max_length=160, default="نصب گفت‌وگو با واحد فروش", verbose_name="عنوان پیشنهاد نصب PWA")
pwa_install_help = models.CharField(max_length=300, default="برای دسترسی سریع، برنامه را به صفحه اصلی گوشی اضافه کنید.", verbose_name="راهنمای نصب PWA")
login_intro_enabled = models.BooleanField(default=True, verbose_name="نمایش بخش معرفی ورود")
login_intro_icon = models.CharField(max_length=32, default="✦", blank=True, verbose_name="آیکون معرفی ورود")
login_intro_eyebrow = models.CharField(max_length=200, default="ارتباط مستقیم با تیم فروش", verbose_name="بالانویس معرفی ورود")
login_intro_heading = models.TextField(default="پاسخ روشن،\nپیگیری ساده.", verbose_name="عنوان معرفی ورود")
login_intro_body = models.TextField(
default="درخواست خود را ثبت کنید، فایل و پیام صوتی بفرستید و پاسخ کارشناس را در یک فضای امن دنبال کنید.",
verbose_name="متن معرفی ورود",
)
staff_login_intro_enabled = models.BooleanField(default=True, verbose_name="نمایش معرفی ورود کارشناسان")
staff_login_intro_icon = models.CharField(max_length=32, default="◈", blank=True, verbose_name="آیکون معرفی ورود کارشناسان")
staff_login_intro_eyebrow = models.CharField(max_length=200, default="پنل کارشناسان فروش", verbose_name="بالانویس ورود کارشناسان")
staff_login_intro_heading = models.TextField(default="پاسخ‌های بهتر،\nفروش هوشمندتر.", verbose_name="عنوان ورود کارشناسان")
staff_login_intro_body = models.TextField(default="صف درخواست‌ها، SLA، اولویت‌ها و سوابق مشتری را از یک میز کار متمرکز مدیریت کنید.", verbose_name="متن ورود کارشناسان")
customer_tickets_hero_enabled = models.BooleanField(default=True, verbose_name="نمایش معرفی صفحه تیکت‌های مشتری")
customer_tickets_hero_eyebrow = models.CharField(max_length=200, default="مرکز ارتباط با فروش", verbose_name="بالانویس صفحه تیکت‌های مشتری")
customer_tickets_hero_heading = models.TextField(default="هر درخواست، یک مسیر روشن\nتا رسیدن به پاسخ.", verbose_name="عنوان صفحه تیکت‌های مشتری")
customer_tickets_hero_body = models.TextField(default="موضوع جدیدی دارید؟ آن را جدا ثبت کنید تا تیم فروش دقیق‌تر و سریع‌تر پیگیری کند.", verbose_name="متن صفحه تیکت‌های مشتری")
developer_credit_text = models.CharField(
max_length=500,
default="برنامه نویسی و توسعه: محمد جواد فصاحت | 09202202129",
verbose_name="متن امضای توسعه‌دهنده",
)
developer_credit_html = models.TextField(
blank=True,
verbose_name="HTML سفارشی امضای توسعه‌دهنده",
help_text="فقط برای HTML مورد اعتماد؛ لینک، آیکون و style خطی مجاز است.",
)
use_developer_credit_html = models.BooleanField(
default=False,
verbose_name="استفاده از HTML سفارشی",
help_text="با فعال‌سازی، HTML بدون escape نمایش داده می‌شود؛ فقط مدیر ارشد باید آن را ویرایش کند.",
)
staff_sidebar_credit_text = models.CharField(
max_length=500,
default="برنامه نویسی و توسعه: محمد جواد فصاحت",
verbose_name="متن امضای پایین sidebar کارکنان",
)
staff_sidebar_credit_html = models.TextField(
blank=True,
verbose_name="HTML سفارشی امضای sidebar کارکنان",
help_text="مستقل از امضای عمومی؛ فقط برای HTML مورد اعتماد.",
)
use_staff_sidebar_credit_html = models.BooleanField(
default=False,
verbose_name="استفاده از HTML سفارشی در sidebar",
)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = "تنظیمات عنوان و برند"
verbose_name_plural = "تنظیمات عنوان و برند"
def __str__(self):
return self.site_title
class VoipConfiguration(SingletonConfiguration):
"""Singleton configuration for the backend click-to-call service."""
voip_call_url = models.URLField(
"VOIP_CALL_URL",
blank=True,
help_text="نشانی کامل endpoint تماس، مانند http://127.0.0.1:8000/v1/click2call",
)
announcement_url = models.URLField("ANNOUNCEMENT_URL", blank=True)
announcement_id = models.CharField("ANNOUNCEMENT_ID", max_length=200, blank=True)
announcement_api_key = models.CharField("ANNOUNCEMENT_API_KEY", max_length=500, blank=True)
announcement_delay_minutes = models.PositiveIntegerField("تأخیر تماس اطلاع‌رسانی (دقیقه)", default=90)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = "تنظیمات تماس VOIP"
verbose_name_plural = "تنظیمات تماس VOIP"
def __str__(self):
return "تنظیمات تماس VOIP"
class PageTitle(models.Model):
"""Editable browser title for a named Django route."""
route_name = models.CharField(max_length=160, unique=True, help_text="مانند accounts:login")
title = models.CharField(max_length=240)
is_active = models.BooleanField(default=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ("route_name",)
verbose_name = "عنوان صفحه"
verbose_name_plural = "عنوان صفحات"
def __str__(self):
return f"{self.route_name}: {self.title}"
class ProductionSeedState(models.Model):
"""Audit marker that makes production seed imports idempotent."""
bundle_sha256 = models.CharField(max_length=64, unique=True)
manifest = models.JSONField(default=dict)
applied_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ("-applied_at",)
verbose_name = "وضعیت بذر Production"
verbose_name_plural = "وضعیت بذرهای Production"
def __str__(self):
return f"{self.bundle_sha256[:12]} @ {self.applied_at}"