104 lines
5.1 KiB
Python
104 lines
5.1 KiB
Python
from django.db import models
|
|
from django.contrib.auth.models import User
|
|
|
|
|
|
class VisitQueue(models.Model):
|
|
customer = models.ForeignKey('Customer', on_delete=models.CASCADE)
|
|
dateTime = models.DateTimeField()
|
|
statusList = [
|
|
('waiting', 'waiting'),
|
|
('finish', 'finish'),
|
|
('cancell', 'cancell'),
|
|
]
|
|
status = models.CharField(max_length=10, choices=statusList, default='waiting')
|
|
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
|
|
GradeList = [
|
|
('3', '3'),
|
|
('2', '2'),
|
|
('1', '1'),
|
|
]
|
|
grade = models.CharField(max_length=1, choices=GradeList)
|
|
grade_desc = models.CharField(max_length=200)
|
|
|
|
|
|
class Action(models.Model):
|
|
"""اقدام/وظیفهٔ CRM مرتبط با مشتری.
|
|
|
|
``actionType`` فقط این شش مقدار را میپذیرد؛ همین شش مقدار در فرمهای پنل
|
|
(``customerActionInc.html``) و در تمام مقایسههای کد (``== 'bazdid'``،
|
|
``exclude(actionType='vosol')`` و …) استفاده میشوند.
|
|
|
|
``status`` نیز همان سه مقدارِ بهکاررفته در کد و قالبها را میپذیرد.
|
|
املای ``'waite'`` عمداً حفظ شده است: ۲٫۷۱۹ رکورد موجودِ دیتابیس و تمام
|
|
مقایسههای کد/قالب (``== 'waite'``، ``if filter_status == 'waite'``) با
|
|
همین املا کار میکنند و تغییرش داده را از موتور جستوجو جدا میکند.
|
|
"""
|
|
|
|
class ActionType(models.TextChoices):
|
|
BAZDID = 'bazdid', 'بازدید'
|
|
NASB = 'nasb', 'نصب'
|
|
VOSOL = 'vosol', 'وصول مطالبات'
|
|
HAMAHANGI = 'hamahangi', 'هماهنگی'
|
|
IN_CALL = 'inCall', 'تماس ورودی'
|
|
OUT_CALL = 'outCall', 'تماس خروجی'
|
|
|
|
class Status(models.TextChoices):
|
|
# املا عیناً مطابق داده و کد فعلی است (نه 'wait').
|
|
WAITE = 'waite', 'در انتظار (باز)'
|
|
FINISH = 'finish', 'انجام شده'
|
|
CANCELL = 'cancell', 'لغو شده'
|
|
|
|
actionType = models.CharField(
|
|
max_length=50, null=True, blank=True, choices=ActionType.choices,
|
|
)
|
|
forUser = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
|
|
customer = models.ForeignKey('Customer', related_name='actionrlt', on_delete=models.CASCADE, null=True, blank=True)
|
|
createdDT = models.DateTimeField(auto_now_add=True)
|
|
changeDT = models.DateTimeField(auto_now=True)
|
|
actionDateTime = models.DateTimeField(null=True, blank=True)
|
|
finishDateTime = models.DateTimeField(null=True, blank=True)
|
|
desc = models.CharField(max_length=500, null=True, blank=True)
|
|
result = models.CharField(max_length=500, null=True, blank=True)
|
|
status = models.CharField(
|
|
max_length=50, null=True, blank=True, choices=Status.choices,
|
|
)
|
|
nazar = models.CharField(max_length=50, null=True, blank=True, default=None)
|
|
nextNazarDT = models.DateTimeField(null=True, blank=True, default=None)
|
|
nazarDT = models.DateTimeField(null=True, blank=True, default=None)
|
|
grade = models.IntegerField(null=True, blank=True, default=None)
|
|
|
|
# --- حسابرسی «حذف» (soft-delete از طریق status='cancell') ---
|
|
# چرا ستون جدا؟ چون ``cancell`` از قبل معنای «لغو برنامه» را دارد و در گزارشها
|
|
# هم استفاده میشود. با این دو ستون میتوانیم «حذف توسط کاربر» را از «لغو
|
|
# عادی» تفکیک کنیم، بدون آنکه منطق محاسبات (که فقط waite/finish را میبیند)
|
|
# دست بخورد.
|
|
deleted_at = models.DateTimeField(
|
|
null=True, blank=True, default=None, verbose_name="زمان حذف",
|
|
help_text="اگر پر باشد، این اقدام توسط کاربر حذف شده است (status=cancell).",
|
|
)
|
|
deleted_by = models.ForeignKey(
|
|
User, on_delete=models.SET_NULL, null=True, blank=True,
|
|
related_name="deleted_actions", verbose_name="حذفکننده",
|
|
)
|
|
|
|
@property
|
|
def is_deleted(self):
|
|
"""آیا این اقدام با «حذف» کنار گذاشته شده است؟ (نه لغو عادی)"""
|
|
return self.deleted_at is not None
|
|
|
|
class Meta:
|
|
# ایندکس **پوشا (covering)** برای محاسبهٔ «آخرین بازدید»: هر سه ستونِ لازم
|
|
# برای فیلتر و تجمیع داخل خودِ ایندکس است، پس دیتابیس به جدول اصلی مراجعه
|
|
# نمیکند. اندازهگیری روی دادهٔ واقعی: ۱۶۴ms → ۲۹ms.
|
|
# ستونهای actionType/status/customer_id برای فیلتر و group، و دو ستون
|
|
# تاریخ برای محاسبهٔ «تاریخ مؤثر» در همان ایندکس.
|
|
indexes = [
|
|
models.Index(
|
|
fields=[
|
|
'actionType', 'status', 'customer_id',
|
|
'actionDateTime', 'finishDateTime',
|
|
],
|
|
name='act_visit_idx',
|
|
),
|
|
]
|