167 lines
9.0 KiB
Python
167 lines
9.0 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",
|
|
)
|
|
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}"
|