add payment_link and banks

This commit is contained in:
javad committed 2026-10-06 10:03:22 +03:30
1 parent 2ae79b1ca4
commit de3135b588
100 files changed
+6167 -47

No files matched your search

+1 -1
View File
@@ -4,5 +4,5 @@
<section class="operations-hero"><div><p class="eyebrow">مرکز یکپارچه ارتباطات</p><h1>مخاطبان و مشتریان</h1><p>پرونده ارتباطی مشتریان، تأمین‌کنندگان و باربری‌ها همراه با گروه‌ها و وضعیت تیکت‌های قابل مشاهده در یک نما.</p></div><div class="operations-hero-actions">{% if staff_operations_access.contact_groups %}<a class="button button-secondary" href="{% url 'staff_tickets:groups' %}">مدیریت گروه‌ها</a>{% endif %}{% if can_add_contact %}<a class="button" href="{% url 'staff_tickets:contact_create' %}">+ مخاطب جدید</a>{% endif %}</div></section>
<section class="operations-kpis" aria-label="آمار مخاطبان"><article><span class="kpi-icon">◎</span><div><b>{{ metrics.total }}</b><small>نتیجه قابل مشاهده</small></div></article><article><span class="kpi-icon is-blue">♙</span><div><b>{{ metrics.customers }}</b><small>مشتری</small></div></article><article><span class="kpi-icon is-amber">▣</span><div><b>{{ metrics.suppliers }}</b><small>تأمین‌کننده</small></div></article><article><span class="kpi-icon is-violet">⇄</span><div><b>{{ metrics.carriers }}</b><small>باربری</small></div></article><article><span class="kpi-icon is-coral">!</span><div><b>{{ metrics.needs_action }}</b><small>تیکت نیازمند اقدام</small></div></article></section>
<section class="operations-panel"><form method="get" class="contact-filter-bar"><label class="contact-search"><span>⌕</span><input type="search" name="q" value="{{ request.GET.q }}" placeholder="جست‌وجو با نام، سازمان یا شماره همراه"></label><label><span>نوع مخاطب</span><select name="stakeholder_type"><option value="">همه انواع</option>{% for value,label in stakeholder_types %}<option value="{{ value }}" {% if request.GET.stakeholder_type == value %}selected{% endif %}>{{ label }}</option>{% endfor %}</select></label><label><span>گروه</span><select name="group"><option value="">همه گروه‌ها</option>{% for group in groups %}<option value="{{ group.pk }}" {% if request.GET.group == group.pk|stringformat:'s' %}selected{% endif %}>{{ group.name }} ({{ group.contact_count }})</option>{% endfor %}</select></label><label><span>استان</span><select name="province"><option value="">همه استان‌ها</option>{% for province in provinces %}<option value="{{ province }}" {% if request.GET.province == province %}selected{% endif %}>{{ province }}</option>{% endfor %}</select></label><button type="submit">اعمال فیلتر</button>{% if request.GET %}<a class="filter-reset" href="{% url 'staff_tickets:contacts' %}">پاک‌کردن</a>{% endif %}</form>
<div class="contact-result-meta"><div><b>{{ contacts|length }} مخاطب</b><small>مرتب‌شده بر اساس تیکت‌های قابل مشاهده و آخرین ثبت</small></div>{% if staff_operations_access.contact_groups %}<a href="{% url 'staff_tickets:groups' %}">{{ groups|length }} گروه قابل دسترس ←</a>{% endif %}</div><div class="contact-card-grid">{% for contact in contacts %}{% with profile=contact.customer_profile %}<article class="contact-card"><header><span class="contact-avatar">{{ profile.first_name|default:contact.local_mobile|slice:':1' }}</span><div class="contact-identity"><h2>{{ profile|default:contact.local_mobile }}</h2><p>{% if profile %}{{ profile.organization_name|default:profile.get_stakeholder_type_display }}{% else %}مشتری{% endif %}</p></div><span class="contact-type type-{{ profile.stakeholder_type|lower }}">{{ profile.get_stakeholder_type_display }}</span></header><div class="contact-meta"><span dir="ltr">☎ {{ contact.local_mobile }}</span><span>⌖ {% if profile.city or profile.province %}{{ profile.city }}{% if profile.city and profile.province %}، {% endif %}{{ profile.province }}{% else %}موقعیت ثبت نشده{% endif %}</span></div><div class="contact-groups">{% for group in profile.visible_groups %}<span>{{ group.name }}</span>{% empty %}<small>بدون گروه قابل مشاهده</small>{% endfor %}</div><div class="contact-ticket-stats"><span><b>{{ contact.ticket_count }}</b>تیکت قابل مشاهده</span><span><b>{{ contact.active_count }}</b>فعال</span><span class="needs-action"><b>{{ contact.waiting_staff_count }}</b>نیازمند اقدام</span><span><b>{{ contact.resolved_count }}</b>مختومه</span></div><footer><a href="{% url 'staff_tickets:contact_edit' contact.public_id %}">ویرایش پرونده</a>{% include 'tickets/_voip_call_button.html' with customer=contact %}{% if staff_operations_access.create_ticket %}<a class="primary-action" href="{% url 'staff_tickets:create' %}?customer={{ contact.public_id }}">+ ثبت تیکت</a>{% endif %}</footer></article>{% endwith %}{% empty %}<div class="empty-state contact-empty"><span>⌕</span><h2>مخاطبی پیدا نشد</h2><p>فیلترها را تغییر دهید یا مخاطب جدیدی ثبت کنید.</p>{% if can_add_contact %}<a class="button" href="{% url 'staff_tickets:contact_create' %}">ثبت مخاطب جدید</a>{% endif %}</div>{% endfor %}</div></section></div>{% endblock %}
<div class="contact-result-meta"><div><b>{{ contacts|length }} مخاطب</b><small>مرتب‌شده بر اساس تیکت‌های قابل مشاهده و آخرین ثبت</small></div>{% if staff_operations_access.contact_groups %}<a href="{% url 'staff_tickets:groups' %}">{{ groups|length }} گروه قابل دسترس ←</a>{% endif %}</div><div class="contact-card-grid">{% for contact in contacts %}{% with profile=contact.customer_profile %}<article class="contact-card"><header><span class="contact-avatar">{{ profile.first_name|default:contact.local_mobile|slice:':1' }}</span><div class="contact-identity"><h2>{{ profile|default:contact.local_mobile }}</h2><p>{% if profile %}{{ profile.organization_name|default:profile.get_stakeholder_type_display }}{% else %}مشتری{% endif %}</p></div><span class="contact-type type-{{ profile.stakeholder_type|lower }}">{{ profile.get_stakeholder_type_display }}</span></header><div class="contact-meta"><span dir="ltr">☎ {{ contact.local_mobile }}</span><span>⌖ {% if profile.city or profile.province %}{{ profile.city }}{% if profile.city and profile.province %}، {% endif %}{{ profile.province }}{% else %}موقعیت ثبت نشده{% endif %}</span></div><div class="contact-groups">{% for group in profile.visible_groups %}<span>{{ group.name }}</span>{% empty %}<small>بدون گروه قابل مشاهده</small>{% endfor %}</div><div class="contact-ticket-stats"><span><b>{{ contact.ticket_count }}</b>تیکت قابل مشاهده</span><span><b>{{ contact.active_count }}</b>فعال</span><span class="needs-action"><b>{{ contact.waiting_staff_count }}</b>نیازمند اقدام</span><span><b>{{ contact.resolved_count }}</b>مختومه</span></div><footer><a href="{% url 'staff_tickets:contact_edit' contact.public_id %}">ویرایش پرونده</a>{% include 'tickets/_voip_call_button.html' with customer=contact %}{% if staff_operations_access.create_ticket %}<a class="primary-action" href="{% url 'staff_tickets:create' %}?customer={{ contact.public_id }}">+ ثبت تیکت</a>{% endif %}{% include 'payments/_notice_shortcut.html' with customer=contact %}</footer></article>{% endwith %}{% empty %}<div class="empty-state contact-empty"><span>⌕</span><h2>مخاطبی پیدا نشد</h2><p>فیلترها را تغییر دهید یا مخاطب جدیدی ثبت کنید.</p>{% if can_add_contact %}<a class="button" href="{% url 'staff_tickets:contact_create' %}">ثبت مخاطب جدید</a>{% endif %}</div>{% endfor %}</div></section></div>{% endblock %}
{% block extra_scripts %}<script defer src="{% static 'js/voip-call.js' %}?v={{ static_asset_version }}"></script>{% endblock %}
+1 -1
View File
@@ -42,7 +42,7 @@
</section>
{% endif %}
</main>
<aside class="workspace-side customer-side" id="customer-info" data-workspace-panel><button type="button" class="panel-close" data-close-panel>×</button><section class="info-card"><p class="panel-index">مشتری</p><h3>{{ ticket.customer.customer_profile|default:'بدون نام' }}</h3><p class="mt-1 text-sm font-bold" dir="ltr">{{ ticket.customer.mobile }}</p><div class="info-list"><div class="info-row"><span>نوع مشتری</span><b>{{ ticket.customer.customer_profile.get_customer_type_display|default:'ثبت نشده' }}</b></div><div class="info-row"><span>موقعیت تیکت</span><b>{% if ticket.city %}{{ ticket.city }}، {{ ticket.province }}{% elif ticket.province %}{{ ticket.province }}{% else %}ثبت نشده{% endif %}</b></div><div class="info-row"><span>تعداد تیکت</span><b>{{ ticket.customer.customer_tickets.count }}</b></div></div></section><section class="info-card"><h3>سابقه اخیر</h3>{% for old in customer_history %}<div class="history-item"><b>{{ old.subject }}</b><span class="ticket-number">{{ old.ticket_number }}</span>{% if perms.tickets.view_all_tickets or old.owner == user or old.selected_expert == user %}<a class="back-link" href="{% url 'staff_tickets:detail' old.public_id %}">مشاهده ←</a>{% else %}<small class="muted">محتوای خصوصی</small>{% endif %}</div>{% empty %}<p class="muted mt-3">تیکت دیگری ثبت نشده است.</p>{% endfor %}</section></aside>
<aside class="workspace-side customer-side" id="customer-info" data-workspace-panel><button type="button" class="panel-close" data-close-panel>×</button><section class="info-card"><p class="panel-index">مشتری</p><h3>{{ ticket.customer.customer_profile|default:'بدون نام' }}</h3><p class="mt-1 text-sm font-bold" dir="ltr">{{ ticket.customer.mobile }}</p><div class="info-list"><div class="info-row"><span>نوع مشتری</span><b>{{ ticket.customer.customer_profile.get_customer_type_display|default:'ثبت نشده' }}</b></div><div class="info-row"><span>موقعیت تیکت</span><b>{% if ticket.city %}{{ ticket.city }}، {{ ticket.province }}{% elif ticket.province %}{{ ticket.province }}{% else %}ثبت نشده{% endif %}</b></div><div class="info-row"><span>تعداد تیکت</span><b>{{ ticket.customer.customer_tickets.count }}</b></div></div>{% include 'payments/_ticket_notices.html' with customer=ticket.customer %}</section><section class="info-card"><h3>سابقه اخیر</h3>{% for old in customer_history %}<div class="history-item"><b>{{ old.subject }}</b><span class="ticket-number">{{ old.ticket_number }}</span>{% if perms.tickets.view_all_tickets or old.owner == user or old.selected_expert == user %}<a class="back-link" href="{% url 'staff_tickets:detail' old.public_id %}">مشاهده ←</a>{% else %}<small class="muted">محتوای خصوصی</small>{% endif %}</div>{% empty %}<p class="muted mt-3">تیکت دیگری ثبت نشده است.</p>{% endfor %}</section></aside>
</div>
</div>
{% include 'tickets/_image_viewer.html' %}
+3 -2
View File
@@ -1,7 +1,8 @@
{% extends 'base.html' %}
{% extends 'base.html' %}{% load static %}
{% block title %}{{ page_title }}{% endblock %}
{% block content %}<div class="operations-page operations-templates">
<section class="operations-hero"><div><p class="eyebrow">پیام‌رسانی استاندارد</p><h1>الگوهای پیامک واحدها</h1><p>متن‌های تاییدشده دیتابیس را برای هر رویداد مدیریت و با متغیرهای امن شخصی‌سازی کنید.</p></div><div class="template-guide"><span>✦</span><div><b>متغیرهای کنترل‌شده</b><small>از ورود تگ نامعتبر جلوگیری می‌شود</small></div></div></section>
<div class="template-layout"><section class="operations-panel"><header class="panel-heading"><div><small>کتابخانه واحدهای شما و fallback سیستم</small><h2>الگوهای موجود در دیتابیس</h2></div><span>{{ templates|length }} الگو</span></header><div class="template-card-list">{% for item in templates %}<article class="template-card"><span class="template-icon">✉</span><div><h3>{{ item.title }}</h3><p>{{ item.get_event_type_display }}</p><small>{% if item.unit %}{{ item.unit }}{% else %}الگوی عمومی سیستم{% endif %} · بروزرسانی {{ item.updated_at|date:'Y/m/d' }}</small></div><span class="status-pill {% if item.is_active %}is-success{% else %}is-muted{% endif %}">{% if item.is_active %}فعال{% else %}غیرفعال{% endif %}</span>{% if item.unit %}<a href="{% url 'staff_tickets:sms_template_edit' item.pk %}">ویرایش</a>{% else %}<span class="status-pill is-muted">Fallback</span>{% endif %}</article>{% empty %}<div class="empty-state"><span>✉</span><h3>هیچ الگویی در دیتابیس وجود ندارد</h3><p>migration داده‌های پیش‌فرض را ایجاد می‌کند؛ وضعیت migration را بررسی کنید.</p></div>{% endfor %}</div></section>
<aside class="operations-form-side template-editor"><header><span>{% if form.instance.pk %}✎{% else %}+{% endif %}</span><div><small>{% if form.instance.pk %}ویرایش الگو{% else %}ساخت الگو{% endif %}</small><h2>{% if form.instance.pk %}{{ form.instance.title }}{% else %}الگوی اختصاصی جدید{% endif %}</h2></div></header><form method="post" class="operations-form">{% csrf_token %}{% if form.non_field_errors %}<div class="create-form-errors">{{ form.non_field_errors }}</div>{% endif %}{% for field in form %}<label><span>{{ field.label }}</span>{{ field }}{{ field.errors }}</label>{% endfor %}<div class="variable-box"><b>متغیرهای قابل استفاده</b><div dir="ltr">{% for tag in allowed_tags %}<code>{% templatetag openvariable %}{{ tag }}{% templatetag closevariable %}</code>{% endfor %}</div></div><button type="submit">ذخیره الگوی پیامک</button>{% if form.instance.pk %}<a href="{% url 'staff_tickets:sms_templates' %}">انصراف از ویرایش</a>{% endif %}</form></aside></div>
<aside class="operations-form-side template-editor"><header><span>{% if form.instance.pk %}✎{% else %}+{% endif %}</span><div><small>{% if form.instance.pk %}ویرایش الگو{% else %}ساخت الگو{% endif %}</small><h2>{% if form.instance.pk %}{{ form.instance.title }}{% else %}الگوی اختصاصی جدید{% endif %}</h2></div></header><form method="post" class="operations-form">{% csrf_token %}{% if form.non_field_errors %}<div class="create-form-errors">{{ form.non_field_errors }}</div>{% endif %}{% for field in form %}<label><span>{{ field.label }}</span>{{ field }}{{ field.errors }}</label>{% endfor %}<div class="variable-box"><b>متغیرهای قابل استفاده</b><div id="sms-allowed-tags" dir="ltr">{% for tag in allowed_tags %}<code>{% templatetag openbrace %}{{ tag }}{% templatetag closebrace %}</code>{% endfor %}</div></div><div class="variable-box"><b>متغیرهای رویدادهای مالی</b><div dir="ltr"><code>{recipient_name}</code> <code>{company_name}</code> <code>{notice_number}</code> <code>{receipt_number}</code> <code>{amount_rial}</code> <code>{invoice_number}</code> <code>{payment_link}</code></div></div><button type="submit">ذخیره الگوی پیامک</button>{% if form.instance.pk %}<a href="{% url 'staff_tickets:sms_templates' %}">انصراف از ویرایش</a>{% endif %}</form></aside></div>
</div>{% endblock %}
{% block extra_scripts %}{{ block.super }}{{ sms_tag_map|json_script:'sms-tag-map' }}<script defer src="{% static 'js/sms-template-tags.js' %}?v={{ static_asset_version }}"></script>{% endblock %}