fix payment

This commit is contained in:
javad committed 2026-10-07 13:39:29 +03:30
1 parent 4f7486152c
commit bb3ddd1610
47 files changed
+2137 -51

No files matched your search

@@ -29,6 +29,12 @@
<div class="pay-field customer-upload-field" data-field="receipt_image"><div class="customer-upload-zone">{% include 'payments/_icon.html' with name='upload' %}<label class="customer-file-button" for="{{ form.receipt_image.id_for_label }}">انتخاب تصویر فیش <span>الزامی</span></label>{{ form.receipt_image }}<p>تصویر JPG، PNG، GIF یا WEBP؛ حداکثر {{ max_bytes|filesizeformat }}.</p></div><p class="field-error" id="error-receipt_image" {% if not form.receipt_image.errors %}hidden{% endif %}>{% for error in form.receipt_image.errors %}{{ error }}{% endfor %}</p><div class="customer-file-selection" id="receipt-file-selection" hidden><p id="receipt-file-status" role="status" aria-live="polite"></p><button type="button" id="receipt-clear-file">حذف انتخاب</button></div><img id="receipt-file-preview" alt="پیش‌نمایش تصویر فیش" hidden></div>
{% include 'payments/_receipt_field.html' with field=form.description %}
<p id="receipt-summary" class="finance-guidance" role="status"></p><p role="status" id="form-status"></p>
<section id="receipt-upload" class="receipt-upload" aria-labelledby="receipt-upload-title" hidden>
<strong id="receipt-upload-title" role="status">در حال ارسال فیش…</strong>
<div class="receipt-upload-meter"><progress id="receipt-upload-progress" max="100" value="0" aria-label="پیشرفت ارسال فیش"></progress><span id="receipt-upload-percent">۰٪</span></div>
<p id="receipt-upload-bytes"></p><p id="receipt-upload-time"></p>
<small>لطفاً تا پایان ثبت، این صفحه را باز نگه دارید.</small>
</section>
<div class="customer-receipt-actions"><button class="pay-button" id="submit-receipt" type="submit">ثبت و ارسال فیش</button>{% if staff_form %}<a class="pay-text-link" href="{% url 'staff_payments:desk' %}">انصراف و بازگشت</a>{% elif payment_link_access.notice_id %}<a class="pay-text-link" href="{% url 'payments:notice_detail' payment_link_access.notice.public_id %}">مشاهده اعلامیه واریز</a>{% elif not payment_link_access %}<a class="pay-text-link" href="{% url 'payments:notices' %}">مشاهده واریزهای اعلام‌شده {% include 'payments/_icon.html' with name='arrow' %}</a>{% endif %}</div>
</section>
</form>
+5
View File
@@ -14,6 +14,11 @@
<!-- Supplementary details stay visible within the expanded notice. Complete copy uses ALL snapshot fields. -->
<p>تأییدشده: {{ notice.approved_amount|intcomma:False }} ریال · باقی‌مانده: {{ notice.remaining_amount|intcomma:False }} ریال</p><section class="pay-supplement"><h3>اطلاعات تکمیلی و توضیحات</h3>{% if notice.invoice_number %}<p>فاکتور: {{ notice.invoice_number }}</p>{% endif %}<p>{{ notice.description|default:'توضیح دیگری اعلام نشده است.'|linebreaksbr }}</p></section>
<div class="pay-actions">{% if not notice.cancelled_at and not staff_notice and notice.remaining_amount > 0 %}<a class="pay-button" href="{% url 'payments:receipt_new' %}?notice={{ notice.public_id }}">ارسال فیش این واریز</a>{% endif %}<button type="button" class="pay-button secondary" data-copy="{{ notice.copy_text }}">کپی کامل اطلاعات</button></div>
{% if notice_page and not staff_notice %}<form class="pay-page-share" method="post" action="{% url 'payments:notice_share_link' notice.public_id %}" data-share-link>
{% csrf_token %}<button type="submit" class="pay-button secondary">{% include 'payments/_icon.html' with name='copy' %}<span>کپی لینک کوتاه این صفحه برای ارسال به دیگران</span></button>
<p>دارندهٔ لینک به اطلاعات همین واریز دسترسی دارد؛ لینک تا پایان اعتبار آن قابل استفاده است.</p>
<p class="pay-share-feedback" role="status" aria-live="polite"></p>
</form>{% endif %}
{% if not staff_notice %}{% include 'payments/_linked_receipts.html' %}{% endif %}
</div></details>
{% if not staff_notice and not notice_page %}<a class="pay-notice-permalink pay-text-link" href="{% url 'payments:notice_detail' notice.public_id %}">جزئیات واریز و فیش‌ها {% include 'payments/_icon.html' with name='arrow' %}</a></article>{% endif %}
+39
View File
@@ -0,0 +1,39 @@
{% load jalali %}
<section class="pay-card pay-sms-panel" aria-labelledby="notice-sms-title">
<h2 id="notice-sms-title">پیامک اعلامیه و سوابق ارسال</h2>
<p class="pay-section-help">پذیرش پیامک توسط سرویس به معنی رسیدن به گوشی نیست؛ وضعیت دریافت بر اساس استعلام سرویس پیامک نمایش داده می‌شود.</p>
{% if can_resend_notice_sms %}
<form method="post" action="{% url 'staff_payments:notice_resend_sms' notice.public_id %}" data-notice-sms>
{% csrf_token %}<input type="hidden" name="request_key" value="{{ notice_sms_request_key }}">
<button class="pay-button secondary" type="submit" {% if notice_sms_pending %}disabled{% endif %}>ارسال مجدد پیامک اعلامیه واریز</button>
</form>
{% if notice_sms_pending %}<p class="pay-section-help" role="status">پیامک در صف یا در حال ارسال است. نتیجه پس از پردازش سامانه و با تازه‌سازی این صفحه نمایش داده می‌شود.</p>{% endif %}
{% endif %}
<div class="pay-sms-history">
{% for notification in notifications %}
<article class="pay-sms-record">
<header><strong>{% if notification.manual_resend %}ارسال مجدد{% else %}ارسال اولیه{% endif %}</strong><time>{{ notification.job.created_at|jalali_datetime }}</time></header>
<p>وضعیت ارسال: <strong>{% if notification.job.status == 'SENT' %}پذیرفته‌شده توسط سرویس پیامک{% else %}{{ notification.job.get_status_display }}{% endif %}</strong></p>
<p>وضعیت دریافت: <span class="pay-status {{ notification.delivery_class }}">{{ notification.delivery_label }}</span></p>
{% if notification.job.staff_user %}<p>درخواست‌کننده: {{ notification.job.staff_user.staff_profile.display_name|default:notification.job.staff_user.get_full_name|default:notification.job.staff_user.username }}</p>{% endif %}
{% if notification.job.sent_at %}<p>زمان ارسال: {{ notification.job.sent_at|jalali_datetime }}</p>{% endif %}
{% if notification.job.status == 'RETRYING' %}<p>تلاش خودکار بعدی: {{ notification.job.next_attempt_at|jalali_datetime }}</p>{% endif %}
{% if notification.job.last_error %}<p class="pay-sms-error">علت خطای ارسال: {{ notification.job.last_error }}</p>{% endif %}
{% if notification.job.delivery_checked_at %}<p>آخرین استعلام: {{ notification.job.delivery_checked_at|jalali_datetime }} · {{ notification.job.delivery_check_attempts }} نوبت</p>{% endif %}
{% if notification.job.delivery_next_check_at %}<p>استعلام بعدی: {{ notification.job.delivery_next_check_at|jalali_datetime }}</p>{% endif %}
{% if notification.job.delivery_description %}<p>شرح نتیجه دریافت: {{ notification.job.delivery_description }}</p>{% endif %}
{% if notification.job.delivery_status_code %}<p>کد وضعیت دریافت: <bdi>{{ notification.job.delivery_status_code }}</bdi></p>{% endif %}
<details><summary>جزئیات پیام و تلاش‌های ارسال ({{ notification.send_attempts|length }})</summary>
{% if notification.rendered_body %}<div class="pay-sms-body">{{ notification.rendered_body|linebreaksbr }}</div>{% endif %}
{% for attempt in notification.send_attempts %}<div class="pay-sms-attempt">
<p><time>{{ attempt.started_at|jalali_datetime }}</time> · {% if attempt.success == True %}پذیرفته‌شده توسط سرویس{% elif attempt.success == False %}تلاش ناموفق{% else %}نتیجه تلاش هنوز ثبت نشده است{% endif %}</p>
{% if attempt.finished_at %}<p>پایان تلاش: {{ attempt.finished_at|jalali_datetime }}</p>{% endif %}
{% if attempt.provider_id %}<p>شناسه رهگیری: <bdi>{{ attempt.provider_id }}</bdi></p>{% endif %}
{% if attempt.provider_code %}<p>کد پاسخ سرویس: <bdi>{{ attempt.provider_code }}</bdi></p>{% endif %}
{% if attempt.error %}<p class="pay-sms-error">{{ attempt.error }}</p>{% endif %}
{% empty %}<p>هنوز تلاشی برای ارسال ثبت نشده است.</p>{% endfor %}
</details>
</article>
{% empty %}<p class="pay-section-help">اطلاع‌رسانی ثبت‌شده‌ای وجود ندارد.</p>{% endfor %}
</div>
</section>
+1 -1
View File
@@ -1 +1 @@
{% if page_obj.paginator.num_pages > 1 %}<nav class="pay-pagination" aria-label="صفحه‌بندی">{% if page_obj.has_previous %}<a href="?{{ query_string }}&page={{ page_obj.previous_page_number }}">قبلی</a>{% endif %}<span>{{ page_obj.number }} از {{ page_obj.paginator.num_pages }}</span>{% if page_obj.has_next %}<a href="?{{ query_string }}&page={{ page_obj.next_page_number }}">بعدی</a>{% endif %}</nav>{% endif %}
{% if always or page_obj.paginator.num_pages > 1 %}<nav class="pay-pagination" aria-label="صفحه‌بندی">{% if page_obj.has_previous %}<a href="?{{ query_string }}&page={{ page_obj.previous_page_number }}">قبلی</a>{% endif %}<span>صفحه {{ page_obj.number }} از {{ page_obj.paginator.num_pages }}</span>{% if page_obj.has_next %}<a href="?{{ query_string }}&page={{ page_obj.next_page_number }}">بعدی</a>{% endif %}{% if always and page_obj.paginator.count %}<small>نمایش {{ page_obj.start_index }} تا {{ page_obj.end_index }} از {{ page_obj.paginator.count }} مورد</small>{% endif %}</nav>{% endif %}
+7 -7
View File
@@ -8,13 +8,13 @@
{% if can_create %}<div class="pay-head-actions"><a class="pay-button" href="{% url 'staff_payments:notice_new' %}">{% include 'payments/_icon.html' with name='plus' %}ایجاد اعلامیه واریز</a><a class="pay-button secondary" href="{% url 'staff_payments:receipt_customer' %}">ثبت فیش برای مشتری</a></div>{% endif %}
</header>
<nav class="pay-tabs pay-desk-tabs" aria-label="نمای پرداخت‌ها">
<a href="?tab=receipts" {% if tab != 'notices' and not request.GET.mine %}aria-current="page"{% endif %}>فیش‌های واریزی</a>
<a href="?tab=receipts" {% if tab != 'notices' and not mine %}aria-current="page"{% endif %}>فیش‌های واریزی</a>
<a href="?tab=notices" {% if tab == 'notices' %}aria-current="page"{% endif %}>اعلامیه‌های واریز</a>
<a href="?tab=receipts&mine=1" {% if tab != 'notices' and request.GET.mine %}aria-current="page"{% endif %}>نیازمند اقدام من</a>
<a href="?tab=receipts&mine=1" {% if mine %}aria-current="page"{% endif %}>نیازمند اقدام من</a>
</nav>
<form method="get" class="pay-card pay-filter-panel">
<input type="hidden" name="tab" value="{{ tab }}">
{% if request.GET.mine %}<input type="hidden" name="mine" value="1">{% endif %}
{% if mine %}<input type="hidden" name="mine" value="1">{% endif %}
<div class="pay-filter-grid">
<label class="pay-search-field">جست‌وجو<span class="pay-search-input">{% include 'payments/_icon.html' with name='search' %}<input type="search" name="q" value="{{ request.GET.q }}" placeholder="نام، موبایل، استان، شهر یا فاکتور"></span></label>
<label>حساب مقصد<select name="account"><option value="">همه حساب‌ها</option>{% for bank in banks %}<option value="{{ bank.pk }}" {% if request.GET.account == bank.pk|stringformat:'s' %}selected{% endif %}>{{ bank.name }}</option>{% endfor %}</select></label>
@@ -30,11 +30,11 @@
<label>تا تاریخ <small>(میلادی)</small><input type="date" name="to" value="{{ request.GET.to }}"></label>
</div>
</details>
<a class="pay-reset" href="?tab={{ tab }}{% if request.GET.mine %}&mine=1{% endif %}">پاک کردن فیلترها</a>
<a class="pay-reset" href="?tab={{ tab }}{% if mine %}&mine=1{% endif %}">پاک کردن فیلترها</a>
</div>
</form>
<section class="pay-records" aria-labelledby="pay-results-title">
<div class="pay-list-head"><h2 id="pay-results-title">{% if tab == 'notices' %}اعلامیه‌های واریز{% elif request.GET.mine %}فیش‌های نیازمند اقدام شما{% else %}فیش‌های واریزی{% endif %}</h2><span>{{ page_obj.paginator.count }} مورد</span></div>
<div class="pay-list-head"><h2 id="pay-results-title">{% if tab == 'notices' %}اعلامیه‌های واریز{% elif mine %}فیش‌های نیازمند اقدام شما{% else %}فیش‌های واریزی{% endif %}</h2><span>{{ page_obj.paginator.count }} مورد</span></div>
{% for item in page_obj %}
<article class="pay-record">
<span class="pay-record-icon">{% include 'payments/_icon.html' with name='receipt' %}</span>
@@ -51,9 +51,9 @@
</div>
</article>
{% empty %}
<div class="pay-empty"><span class="pay-empty-icon">{% include 'payments/_icon.html' with name='search' %}</span><h3>موردی پیدا نشد</h3><p>{% if request.GET.q or request.GET.status or request.GET.notice_state or request.GET.account or request.GET.kind or request.GET.from or request.GET.to %}فیلترها را تغییر دهید یا پاک کنید.{% elif request.GET.mine %}در حال حاضر فیشی برای اقدام شما وجود ندارد.{% elif tab == 'notices' %}اعلامیه‌های صادرشده برای مشتریان اینجا نمایش داده می‌شوند.{% else %}فیش‌های ارسالی مشتریان برای بررسی اینجا نمایش داده می‌شوند.{% endif %}</p></div>
<div class="pay-empty"><span class="pay-empty-icon">{% include 'payments/_icon.html' with name='search' %}</span><h3>موردی پیدا نشد</h3><p>{% if request.GET.q or request.GET.status or request.GET.notice_state or request.GET.account or request.GET.kind or request.GET.from or request.GET.to %}فیلترها را تغییر دهید یا پاک کنید.{% elif mine %}در حال حاضر فیشی برای اقدام شما وجود ندارد.{% elif tab == 'notices' %}اعلامیه‌های صادرشده برای مشتریان اینجا نمایش داده می‌شوند.{% else %}فیش‌های ارسالی مشتریان برای بررسی اینجا نمایش داده می‌شوند.{% endif %}</p></div>
{% endfor %}
</section>
{% include 'payments/_pagination.html' %}
{% include 'payments/_pagination.html' with always=True %}
</section>
{% endblock %}
+1 -1
View File
@@ -10,7 +10,7 @@
<aside class="pay-detail-aside">
{% if can_share_payment_link %}<section class="pay-card"><h2>لینک موقت پرداخت</h2><p class="pay-section-help">برای مشاهده اعلامیه و ارسال فیش، مشتری نیازی به ورود ندارد. پس از پایان اعتبار، می‌توانید لینک تازه بسازید.</p>{% if payment_share_url %}<p class="pay-share-url" dir="ltr">{{ payment_share_url }}</p><button type="button" class="pay-button secondary" data-copy="{{ payment_share_url }}">کپی لینک پرداخت</button>{% endif %}<form method="post" action="{% url 'staff_payments:notice_payment_link' notice.public_id %}">{% csrf_token %}<button class="pay-button" type="submit">ساخت لینک تازه پرداخت</button></form></section>{% endif %}
<section class="pay-card"><h2>فیش‌های این اعلامیه</h2>{% for receipt in receipts %}<a class="pay-linked-record" href="{% url 'staff_payments:receipt_detail' receipt.public_id %}"><bdi>{{ receipt.number }}</bdi><span class="pay-status {{ receipt.status|lower }}">{{ receipt.get_status_display }}</span>{% include 'payments/_icon.html' with name='arrow' %}</a>{% empty %}<p class="pay-section-help">هنوز فیشی برای این اعلامیه ثبت نشده است.</p>{% endfor %}</section>
<section class="pay-card"><h2>اطلاع‌رسانی پیامک</h2>{% for notification in notifications %}<div class="pay-notification-row"><strong>{{ notification.job.get_status_display }}</strong><span>وضعیت دریافت: {{ notification.job.get_delivery_status_display }}</span></div>{% empty %}<p class="pay-section-help">اطلاع‌رسانی ثبت‌شده‌ای وجود ندارد.</p>{% endfor %}</section>
{% include 'payments/_notice_sms.html' %}
{% if can_delete_notice %}<details class="pay-delete-panel"><summary>حذف اعلامیه واریز</summary><form method="post" action="{% url 'staff_payments:notice_delete' notice.public_id %}">{% csrf_token %}<p role="note">آیا از حذف اعلامیه «{{ notice.subject }}» با شماره {{ notice.number }} مطمئن هستید؟ پس از حذف، اعلامیه از فهرست‌ها خارج می‌شود و لینک‌های آن دیگر قابل استفاده نیستند. این اقدام از پنل قابل بازگشت نیست.</p><p>حذف فقط تا زمانی مجاز است که هیچ فیشی برای این اعلامیه ثبت نشده باشد.</p><label><input type="checkbox" name="confirm" value="delete" required> هشدار را خواندم و حذف این اعلامیه را تأیید می‌کنم.</label><div class="pay-actions"><button class="pay-button pay-danger" type="submit">تأیید و حذف اعلامیه</button></div></form></details>{% elif may_delete_notice %}<section class="pay-card"><h2>حذف اعلامیه</h2><p class="pay-section-help">برای این اعلامیه فیش ثبت شده است؛ حذف آن امکان‌پذیر نیست، حتی اگر فیش رد شده باشد.</p></section>{% endif %}
</aside>
</div>
+1 -1
View File
@@ -1,3 +1,3 @@
{% extends 'payments/base.html' %}{% load static %}{% block title %}{% if staff_form %}ثبت فیش برای مشتری{% else %}ارسال فیش واریزی{% endif %}{% endblock %}
{% block content %}{% include 'payments/_customer_receipt_form.html' %}{% endblock %}
{% block extra_scripts %}{{ block.super }}<script defer src="{% static 'js/chaharsu/receipt-amount.js' %}?v={{ static_asset_version }}"></script><script defer src="{% static 'js/chaharsu/receipt-form.js' %}?v={{ static_asset_version }}"></script><script defer src="{% static 'js/chaharsu/customer-receipt.js' %}?v={{ static_asset_version }}"></script>{% endblock %}
{% block extra_scripts %}{{ block.super }}<script defer src="{% static 'js/chaharsu/receipt-amount.js' %}?v={{ static_asset_version }}"></script><script defer src="{% static 'js/chaharsu/receipt-upload.js' %}?v={{ static_asset_version }}"></script><script defer src="{% static 'js/chaharsu/receipt-form.js' %}?v={{ static_asset_version }}"></script><script defer src="{% static 'js/chaharsu/customer-receipt.js' %}?v={{ static_asset_version }}"></script>{% endblock %}