some improve
This commit is contained in:
1 parent
fa73a734a0
commit
2e5d0bcdd3
17 files changed
+1512
-226
No files matched your search
+79
-13
@@ -17,6 +17,7 @@ from ..sms.parts import ENCODING_LABELS, describe, parts_count
|
||||
from ..sms.phones import (customer_phone_entries, save_primary_normalized,
|
||||
stored_primary_normalized)
|
||||
from ..sms.render import format_jalali, render_template
|
||||
from ..sms.rules import customer_toggle_map
|
||||
from ..sms.timing import apply_timing, current_timing_value, timing_for
|
||||
|
||||
_manage_required = permission_required("admin_panel.ManageSMS", raise_exception=True)
|
||||
@@ -58,6 +59,16 @@ def _notice(url, code, **extra):
|
||||
def _customer_url(c_id):
|
||||
return reverse("admin_panel:customer", kwargs={"c_id": c_id})
|
||||
|
||||
|
||||
def _customer_sms_url(c_id):
|
||||
"""صفحهٔ «سوابق و تنظیمات پیامک» مشتری — مقصد بازگشتِ عملیات پیامکی.
|
||||
|
||||
تنظیمات پیامک از صفحهٔ فاکتور به این صفحه منتقل شده؛ پس بعد از هر تغییر
|
||||
(تیک نوع پیامک، شمارهٔ اصلی، ارسال سفارشی) کاربر باید همینجا بماند، نه به
|
||||
صفحهٔ اصلی مشتری پرتاب شود.
|
||||
"""
|
||||
return reverse("admin_panel:customerSmsHistory", kwargs={"c_id": c_id})
|
||||
|
||||
# متغیرهای قابل استفاده در متن قالبها (برای راهنمای پنل).
|
||||
TEMPLATE_VARIABLES = [
|
||||
("{name}", "نام مشتری"),
|
||||
@@ -79,6 +90,7 @@ SMS_TYPE_UI = {
|
||||
SMSType.CHEQUE_REGISTRATION: {"icon": "🧾", "when": "چند روز پیش از سررسید چک"},
|
||||
SMSType.VISIT: {"icon": "🔧", "when": "پس از گذشت دورهٔ بازدید دستگاه"},
|
||||
SMSType.VISIT_CREATED: {"icon": "📅", "when": "پس از ثبت اقدام بازدید"},
|
||||
SMSType.INSTALL_CREATED: {"icon": "🛠️", "when": "پس از ثبت اقدام نصب"},
|
||||
SMSType.PAYMENT_THANKS: {"icon": "🙏", "when": "پس از ثبت پرداخت"},
|
||||
SMSType.NESIYE_DUE: {"icon": "💳", "when": "پیش از سررسید نسیه"},
|
||||
}
|
||||
@@ -313,8 +325,12 @@ def sms_customer_toggle(request, c_id):
|
||||
)
|
||||
setting.is_enabled = enabled
|
||||
setting.save()
|
||||
return _notice(_customer_url(c_id), "customer_sms_saved")
|
||||
return redirect("admin_panel:customer", c_id=c_id)
|
||||
# ``open=settings`` ⇒ صفحه با همان مودال تنظیمات باز میشود تا کاربر
|
||||
# چند نوع را پشتسرهم روشن/خاموش کند و هر بار مودال را باز نکند.
|
||||
return _notice(
|
||||
_customer_sms_url(c_id), "customer_sms_saved", open="settings",
|
||||
)
|
||||
return redirect("admin_panel:customerSmsHistory", c_id=c_id)
|
||||
|
||||
|
||||
@login_required(login_url='/panel/p_login/')
|
||||
@@ -336,13 +352,13 @@ def sms_primary_toggle(request, c_id):
|
||||
"",
|
||||
)
|
||||
if not normalized:
|
||||
return _notice(_customer_url(c_id), "primary_not_found")
|
||||
return _notice(_customer_sms_url(c_id), "primary_not_found")
|
||||
current = [n for n in stored_primary_normalized(customer) if n != normalized]
|
||||
if enabled:
|
||||
current.append(normalized)
|
||||
save_primary_normalized(customer, current)
|
||||
return _notice(_customer_url(c_id), "primary_updated")
|
||||
return redirect("admin_panel:customer", c_id=c_id)
|
||||
return _notice(_customer_sms_url(c_id), "primary_updated")
|
||||
return redirect("admin_panel:customerSmsHistory", c_id=c_id)
|
||||
|
||||
|
||||
@login_required(login_url='/panel/p_login/')
|
||||
@@ -358,15 +374,16 @@ def sms_send_custom(request, c_id):
|
||||
text = (request.POST.get("text", "") or "").strip()
|
||||
rendered = render_template(text, {"name": customer.name_family}).strip()
|
||||
if not rendered:
|
||||
return _notice(_customer_url(c_id), "custom_empty")
|
||||
# خطا ⇒ همان مودال ارسال سفارشی دوباره باز شود تا متن از دست نرود.
|
||||
return _notice(_customer_sms_url(c_id), "custom_empty", open="custom")
|
||||
created = enqueue_sms(
|
||||
customer=customer, sms_type="", message=rendered,
|
||||
created_by=request.user,
|
||||
approved=True, # متن را خودِ مدیر نوشته و ارسال را خواسته است.
|
||||
)
|
||||
code = "custom_queued" if created else "custom_no_phone"
|
||||
return _notice(_customer_url(c_id), code)
|
||||
return redirect("admin_panel:customer", c_id=c_id)
|
||||
return _notice(_customer_sms_url(c_id), code)
|
||||
return redirect("admin_panel:customerSmsHistory", c_id=c_id)
|
||||
|
||||
|
||||
@login_required(login_url='/panel/p_login/')
|
||||
@@ -428,11 +445,17 @@ def sms_queue(request):
|
||||
@login_required(login_url='/panel/p_login/')
|
||||
@_manage_required
|
||||
def customer_sms_history(request, c_id):
|
||||
"""سوابق پیامکهای یک مشتری روی صفحهٔ خود مشتری.
|
||||
"""سوابق + تنظیمات پیامک یک مشتری (همه در یک صفحه).
|
||||
|
||||
همهٔ پیامکهایی که برای شمارههای این مشتری در صف قرار گرفته/ارسال شدهاند
|
||||
(هر وضعیتی) با متن دقیق، تاریخ شمسی و وضعیت تحویل (دلیوری) نمایش داده میشوند و
|
||||
فهرست صفحهبندی میشود. با ``?status=`` و ``?delivery=`` میتوان فیلتر کرد.
|
||||
ساختار صفحه عمداً دو لایه است تا **تنظیمات، سوابق را شلوغ نکند**:
|
||||
|
||||
* لایهٔ اول (همیشه دیده میشود): آمار کوتاه، فیلترها و فهرست سوابق.
|
||||
* لایهٔ دوم (فقط با کلیک): تنظیمات پیامک، شمارههای اصلی و ارسال سفارشی،
|
||||
همه داخل مودالهای Bulma.
|
||||
|
||||
تنظیماتی که قبلاً داخل باکس «پیامک» صفحهٔ ایجاد فاکتور بود
|
||||
(``_customer_sms.html``) حالا اینجاست؛ آن باکس حذف شد تا فاکتور و سوابق هر
|
||||
دو تمامعرض بمانند.
|
||||
"""
|
||||
customer = get_object_or_404(Customer, id=c_id)
|
||||
status = request.GET.get("status", "")
|
||||
@@ -446,6 +469,34 @@ def customer_sms_history(request, c_id):
|
||||
qs = qs.filter(delivery_status=delivery)
|
||||
paginator = Paginator(qs, 20)
|
||||
page = paginator.get_page(request.GET.get("page"))
|
||||
|
||||
# --- آمار کارتهای بالای صفحه (کل تاریخچهٔ همین مشتری، بدون فیلتر) ---
|
||||
all_items = SMSQueue.objects.filter(customer=customer)
|
||||
status_counts = dict(
|
||||
all_items.order_by().values_list("status").annotate(n=Count("id"))
|
||||
)
|
||||
total_count = all_items.count()
|
||||
|
||||
# --- تنظیمات: نوعهای پیامک با وضعیت مؤثر همین مشتری + «ارسال خودکار» قالب ---
|
||||
# ``auto_send`` تنظیمی سراسری است (صفحهٔ قالبها)، ولی دانستنش اینجا کمک
|
||||
# میکند کاربر بداند پیامکِ این نوع بیدرنگ میرود یا منتظر تأیید میماند.
|
||||
auto_send = dict(
|
||||
SMSTemplate.objects.values_list("sms_type", "auto_send")
|
||||
)
|
||||
type_labels = dict(SMSType.choices)
|
||||
sms_toggles = [
|
||||
{
|
||||
"code": item["code"],
|
||||
"label": item["label"],
|
||||
"enabled": item["enabled"],
|
||||
"auto_send": bool(auto_send.get(item["code"])),
|
||||
"icon": SMS_TYPE_UI.get(item["code"], {}).get("icon", "✉️"),
|
||||
"when": SMS_TYPE_UI.get(item["code"], {}).get("when", ""),
|
||||
}
|
||||
for item in customer_toggle_map(customer)
|
||||
]
|
||||
enabled_count = sum(1 for item in sms_toggles if item["enabled"])
|
||||
|
||||
data = {
|
||||
"customer": customer,
|
||||
"items": page,
|
||||
@@ -453,8 +504,23 @@ def customer_sms_history(request, c_id):
|
||||
"delivery": delivery,
|
||||
"status_choices": SMSQueue.Status.choices,
|
||||
"delivery_choices": SMSQueue.DeliveryStatus.choices,
|
||||
"total_count": SMSQueue.objects.filter(customer=customer).count(),
|
||||
"total_count": total_count,
|
||||
"filtered_count": paginator.count,
|
||||
# تنظیمات (مودال)
|
||||
"sms_toggles": sms_toggles,
|
||||
"sms_enabled_count": enabled_count,
|
||||
"sms_type_total": len(type_labels),
|
||||
"phone_entries": customer_phone_entries(customer),
|
||||
# آمار (نوار بالای صفحه)
|
||||
"status_counts": status_counts,
|
||||
"stat_sent": status_counts.get(SMSQueue.Status.SENT, 0),
|
||||
# «در صف» = هر چیزی که هنوز ارسال نشده و منتظر اقدام/worker است.
|
||||
"stat_pending": (
|
||||
status_counts.get(SMSQueue.Status.PENDING, 0)
|
||||
+ status_counts.get(SMSQueue.Status.APPROVED, 0)
|
||||
+ status_counts.get(SMSQueue.Status.PROCESSING, 0)
|
||||
),
|
||||
"stat_failed": status_counts.get(SMSQueue.Status.FAILED, 0),
|
||||
}
|
||||
return render(request, "admin_panel/customerSmsHistory.html", data)
|
||||
|
||||
|
||||
Reference in new issue
Block a user