improve bazdid 4
This commit is contained in:
1 parent
8fd881e0ea
commit
825f2a5973
13 files changed
+585
-15
No files matched your search
@@ -12,9 +12,10 @@ from django.utils import timezone
|
||||
from ..models import Action, Customer
|
||||
from ..sms.triggers import notify_visit_scheduled
|
||||
from ..visit_policy import (REASON_CANCEL, REASON_CREATE, REASON_EDIT,
|
||||
as_aware, clear_visit_hold, default_hold_until,
|
||||
normalize_finish_datetime, normalize_hold_until,
|
||||
refresh_last_bazdid, set_visit_hold,
|
||||
ActionDeleteError, as_aware, clear_visit_hold,
|
||||
default_hold_until, normalize_finish_datetime,
|
||||
normalize_hold_until, refresh_last_bazdid,
|
||||
set_visit_hold, soft_delete_action,
|
||||
validate_action_payload)
|
||||
|
||||
|
||||
@@ -27,8 +28,12 @@ def customerAction(request, c_id):
|
||||
filter_type = request.GET.get('actionType', 'all')
|
||||
filter_status = request.GET.get('status', 'all')
|
||||
|
||||
# کوئری پایه برای اقدامات این مشتری
|
||||
# کوئری پایه برای اقدامات این مشتری.
|
||||
# اقدامهای حذفشده (soft-delete) بهطور پیشفرض نمایش داده نمیشوند؛ با
|
||||
# فیلتر صریح `status=cancell` میتوان آنها را دید (برای حسابرسی).
|
||||
actions = Action.objects.filter(customer=customer)
|
||||
if filter_status != 'cancell':
|
||||
actions = actions.filter(deleted_at__isnull=True)
|
||||
|
||||
# اعمال فیلتر بر اساس نوع اقدام (بازدید، نصب و ...)
|
||||
if filter_type != 'all':
|
||||
@@ -57,7 +62,9 @@ def customerAction(request, c_id):
|
||||
@login_required(login_url='/panel/p_login/')
|
||||
def customerNazar(request ,c_id):
|
||||
customer = Customer.objects.get(id=c_id)
|
||||
actions = Action.objects.filter(customer=customer, nazarDT__isnull=False).order_by("-createdDT")
|
||||
actions = Action.objects.filter(
|
||||
customer=customer, nazarDT__isnull=False, deleted_at__isnull=True,
|
||||
).order_by("-createdDT")
|
||||
allUsers = User.objects.filter().exclude(username='admin').exclude(username='fesahat')
|
||||
data = {'customer': customer, 'actions': actions, 'allUsers': allUsers}
|
||||
return render(request, 'admin_panel/customer/nazar.html', data)
|
||||
@@ -224,9 +231,43 @@ def customerEditAction(request ,c_id, action_id):
|
||||
return redirect(request.META.get('HTTP_REFERER', 'redirect_if_referer_not_found'))
|
||||
|
||||
|
||||
@login_required(login_url='/panel/p_login/')
|
||||
def customerActionDelete(request, c_id, action_id):
|
||||
"""«حذف» یک اقدام (soft-delete).
|
||||
|
||||
فقط POST مجاز است (تغییر وضعیت داده). اقدام فیزیکی حذف **نمیشود**؛
|
||||
``status`` به ``cancell`` میرود و ``deleted_at``/``deleted_by`` پر میشوند.
|
||||
|
||||
چرا soft-delete؟ چون سابقهٔ CRM باید قابل حسابرسی بماند و منطق محاسبات
|
||||
(entezar / ActionList / داشبورد) از قبل فقط ``waite``/``finish`` را میبیند؛
|
||||
پس همین مقدار، اقدام را از همهٔ خروجیها کنار میگذارد.
|
||||
"""
|
||||
customer_obj = get_object_or_404(Customer, id=c_id)
|
||||
action = get_object_or_404(Action, id=action_id, customer=customer_obj)
|
||||
back_url = '/panel/customer/%d/action' % customer_obj.pk
|
||||
|
||||
if request.method != 'POST':
|
||||
# دسترسی مستقیم با GET نباید چیزی را عوض کند.
|
||||
return redirect(back_url)
|
||||
|
||||
try:
|
||||
cancelled = soft_delete_action(action, user=request.user)
|
||||
except ActionDeleteError as error:
|
||||
messages.error(request, str(error))
|
||||
return redirect(back_url)
|
||||
|
||||
label = action.get_actionType_display() or 'اقدام'
|
||||
message = '«%s» حذف شد و از فهرستهای انتظار/وظایف خارج شد.' % label
|
||||
if cancelled:
|
||||
message += ' %d پیامک ارسالنشدهٔ مرتبط هم لغو شد.' % cancelled
|
||||
messages.success(request, message)
|
||||
return redirect(back_url)
|
||||
|
||||
|
||||
@login_required(login_url='/panel/p_login/')
|
||||
def ActionListAll(request):
|
||||
actions = Action.objects.filter().order_by("-id")
|
||||
# اقدامهای حذفشده (soft-delete) در فهرست «همه» نمایش داده نمیشوند.
|
||||
actions = Action.objects.filter(deleted_at__isnull=True).order_by("-id")
|
||||
allUsers = User.objects.filter().exclude(username='admin').exclude(username='fesahat')
|
||||
data = {'actions': actions, 'allUsers': allUsers}
|
||||
return render(request, 'admin_panel/ActionList.html', data)
|
||||
|
||||
@@ -156,9 +156,10 @@ def report_debtors_no_vosol(request):
|
||||
).values('customer').annotate(c=Count('id')).values('c')
|
||||
|
||||
# استخراج آخرین سابقه اکشن وصول
|
||||
# نکته: اقدام حذفشده/لغوشده نباید «آخرین اقدام وصول» بهحساب بیاید.
|
||||
latest_vosol_qs = Action.objects.filter(
|
||||
customer=OuterRef('pk'), actionType='vosol'
|
||||
).order_by('-createdDT')
|
||||
customer=OuterRef('pk'), actionType='vosol',
|
||||
).exclude(status='cancell').order_by('-createdDT')
|
||||
|
||||
# --- ۳. ساخت کوئری پایه (فقط مشتریان فعال و دارای معوقه) ---
|
||||
base_qs = Customer.objects.filter(is_active=True).annotate(
|
||||
@@ -939,7 +940,8 @@ def rezayat_dashboard(request):
|
||||
# دریافت تمامی نظرسنجیهای ثبت شده معتبر (که تاریخ نظرسنجی دارند)
|
||||
valid_surveys = Action.objects.filter(
|
||||
nazarDT__isnull=False,
|
||||
grade__in=[5, 10, 15, 20]
|
||||
grade__in=[5, 10, 15, 20],
|
||||
deleted_at__isnull=True,
|
||||
)
|
||||
|
||||
# ۱. محاسبات شاخصهای کلیدی (KPIs)
|
||||
@@ -965,7 +967,8 @@ def rezayat_dashboard(request):
|
||||
# ۴. واکشی لیست مشتریان نیازمند پیگیری (شامل متوسط ۱۵، ضعیف ۱۰ و بد ۵)
|
||||
low_score_actions = Action.objects.filter(
|
||||
nazarDT__isnull=False,
|
||||
grade__in=[5, 10, 15] # <-- امتیاز ۱۵ (متوسط) نیز اضافه شد
|
||||
grade__in=[5, 10, 15], # <-- امتیاز ۱۵ (متوسط) نیز اضافه شد
|
||||
deleted_at__isnull=True,
|
||||
).order_by('-nazarDT')
|
||||
|
||||
allUsers = User.objects.filter().exclude(username='admin').exclude(username='fesahat')
|
||||
|
||||
@@ -40,7 +40,9 @@ SENT_STATUSES = [
|
||||
# نیازمند اقدام را نشان میدهد؛ پیامکِ ارسالشده از صف بیرون میرود و سابقهاش در
|
||||
# صفحهٔ «گزارش ارسال» (/panel/sms/report) دیده میشود. این کار جلوی حذفِ ناخواستهٔ
|
||||
# تاریخچه از روی صف را میگیرد و صف را به فهرستِ کاریِ تمیز تبدیل میکند.
|
||||
DONE_STATUSES = [SMSQueue.Status.SENT]
|
||||
# پیامک «لغوشده» هم دیگر کاری برای انجام ندارد (مثلاً چون اقدامِ مرتبطش حذف شده)،
|
||||
# پس از صف بیرون میرود.
|
||||
DONE_STATUSES = [SMSQueue.Status.SENT, SMSQueue.Status.CANCELLED]
|
||||
|
||||
|
||||
def _notice(url, code, **extra):
|
||||
|
||||
Reference in new issue
Block a user