387 lines
17 KiB
HTML
387 lines
17 KiB
HTML
{% extends '_base.html' %}
|
|
{% load static %}
|
|
{% load processes_tags %}
|
|
{% load common_tags %}
|
|
{% load accounts_tags %}
|
|
{% load humanize %}
|
|
|
|
{% block sidebar %}
|
|
{% include 'sidebars/admin.html' %}
|
|
{% endblock sidebar %}
|
|
|
|
{% block navbar %}
|
|
{% include 'navbars/admin.html' %}
|
|
{% endblock navbar %}
|
|
|
|
{% block title %}{{ step.name }} - درخواست {{ instance.code }}{% endblock %}
|
|
|
|
{% block style %}
|
|
<link rel="stylesheet" href="{% static 'assets/vendor/libs/bs-stepper/bs-stepper.css' %}">
|
|
<!-- Persian Date Picker CSS -->
|
|
<link rel="stylesheet" href="https://unpkg.com/persian-datepicker@latest/dist/css/persian-datepicker.min.css">
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
{% include '_toasts.html' %}
|
|
{% csrf_token %}
|
|
<div class="container-xxl flex-grow-1 container-p-y">
|
|
<div class="row">
|
|
<div class="col-12 mb-4">
|
|
<div class="d-flex align-items-center justify-content-between mb-3 no-print">
|
|
<div>
|
|
<h4 class="mb-1">{{ step.name }}: {{ instance.process.name }}</h4>
|
|
<small class="text-muted d-block">
|
|
اشتراک آب: {{ instance.well.water_subscription_number|default:"-" }}
|
|
| نماینده: {{ instance.representative.profile.national_code|default:"-" }}
|
|
</small>
|
|
</div>
|
|
<div class="d-flex gap-2">
|
|
<a href="{% url 'invoices:final_invoice_print' instance.id %}" target="_blank" class="btn btn-outline-secondary"><i class="bx bx-printer"></i> پرینت</a>
|
|
|
|
<a href="{% url 'processes:request_list' %}" class="btn btn-outline-secondary">بازگشت</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bs-stepper wizard-vertical vertical mt-2 no-print">
|
|
{% stepper_header instance step %}
|
|
<div class="bs-stepper-content">
|
|
|
|
<div class="row g-3">
|
|
{% if is_broker %}
|
|
<div class="col-12 col-lg-5">
|
|
<div class="card border h-100">
|
|
<div class="card-header"><h5 class="mb-0">ثبت تراکنش تسویه</h5></div>
|
|
<div class="card-body">
|
|
<form id="formFinalPayment" enctype="multipart/form-data" onsubmit="return false;">
|
|
{% csrf_token %}
|
|
<div class="mb-3">
|
|
<label class="form-label">نوع تراکنش</label>
|
|
<select class="form-select" name="direction" id="id_direction" required>
|
|
<option value="in">دریافتی از مشتری</option>
|
|
<option value="out">پرداخت به مشتری</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">مبلغ (تومان)</label>
|
|
<input type="number" min="1" class="form-control" name="amount" id="id_amount" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">تاریخ</label>
|
|
<input type="text" class="form-control" id="id_payment_date" name="payment_date" placeholder="انتخاب تاریخ" readonly required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">روش پرداخت</label>
|
|
<select class="form-select" name="payment_method" id="id_payment_method" required>
|
|
<option value="bank_transfer">انتقال بانکی</option>
|
|
<option value="card">کارت بانکی</option>
|
|
<option value="cash">نقدی</option>
|
|
<option value="check">چک</option>
|
|
<option value="other">سایر</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">شماره مرجع/چک</label>
|
|
<input type="text" class="form-control" name="reference_number" id="id_reference_number" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">تصویر فیش/چک</label>
|
|
<input type="file" class="form-control" name="receipt_image" id="id_receipt_image" accept="image/*" required>
|
|
</div>
|
|
<div class="d-flex justify-content-end">
|
|
<button type="button" id="btnAddFinalPayment" class="btn btn-primary">افزودن</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
<div class="col-12 {% if is_broker %}col-lg-7{% else %}col-lg-12{% endif %}">
|
|
<div class="card mb-3 border">
|
|
<div class="card-header d-flex justify-content-between">
|
|
<h5 class="mb-0">وضعیت فاکتور</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-6 col-md-4">
|
|
<div class="border rounded p-3 h-100">
|
|
<div class="small text-muted">مبلغ نهایی</div>
|
|
<div class="h5 mt-1">{{ invoice.final_amount|floatformat:0|intcomma:False }} تومان</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-6 col-md-4">
|
|
<div class="border rounded p-3 h-100">
|
|
<div class="small text-muted">پرداختیها</div>
|
|
<div class="h5 mt-1 text-success">{{ invoice.paid_amount|floatformat:0|intcomma:False }} تومان</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-6 col-md-4">
|
|
<div class="border rounded p-3 h-100">
|
|
<div class="small text-muted">مانده</div>
|
|
<div class="h5 mt-1 {% if invoice.remaining_amount <= 0 %}text-success{% else %}text-danger{% endif %}">{{ invoice.remaining_amount|floatformat:0|intcomma:False }} تومان</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-6 d-flex align-items-center">
|
|
{% if invoice.remaining_amount <= 0 %}
|
|
<span class="badge bg-success">تسویه کامل</span>
|
|
{% else %}
|
|
<span class="badge bg-warning text-dark">باقیمانده دارد</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card border">
|
|
<div class="card-header"><h5 class="mb-0">تراکنشها</h5></div>
|
|
<div class="table-responsive">
|
|
<table class="table table-striped mb-0">
|
|
<thead>
|
|
<tr>
|
|
<th>نوع</th>
|
|
<th>مبلغ</th>
|
|
<th>تاریخ</th>
|
|
<th>روش</th>
|
|
<th class="text-nowrap">شماره مرجع/چک</th>
|
|
<th>عملیات</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for p in payments %}
|
|
<tr>
|
|
<td>{% if p.direction == 'in' %}<span class="badge bg-success">دریافتی{% else %}<span class="badge bg-warning text-dark">پرداختی{% endif %}</span></td>
|
|
<td>{{ p.amount|floatformat:0|intcomma:False }} تومان</td>
|
|
<td>{{ p.payment_date|date:'Y/m/d' }}</td>
|
|
<td>{{ p.get_payment_method_display }}</td>
|
|
<td>{{ p.reference_number|default:'-' }}</td>
|
|
<td>
|
|
<div class="btn-group">
|
|
{% if p.receipt_image %}
|
|
<a href="{{ p.receipt_image.url }}" target="_blank" class="btn btn-sm btn-outline-secondary" title="مشاهده" aria-label="مشاهده">
|
|
<i class="bx bx-show"></i>
|
|
</a>
|
|
{% endif %}
|
|
{% if is_broker %}
|
|
<button type="button" class="btn btn-sm btn-outline-danger" onclick="openDeleteModal('{{ p.id }}')" title="حذف" aria-label="حذف"><i class="bx bx-trash"></i></button>
|
|
{% endif %}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% empty %}
|
|
<tr><td colspan="6" class="text-center text-muted">تراکنشی ندارد</td></tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% if approver_statuses %}
|
|
<div class="card border mt-2">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<h6 class="mb-0">وضعیت تاییدها</h6>
|
|
{% if can_approve_reject %}
|
|
<div class="d-flex gap-2">
|
|
<button type="button" class="btn btn-success btn-sm" data-bs-toggle="modal" data-bs-target="#approveFinalSettleModal" {% if step_instance.status == 'completed' %}disabled{% endif %}>تایید</button>
|
|
<button type="button" class="btn btn-danger btn-sm" data-bs-toggle="modal" data-bs-target="#rejectFinalSettleModal">رد</button>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
<div class="card-body py-3">
|
|
<div class="row g-2">
|
|
{% for st in approver_statuses %}
|
|
<div class="col-12 col-md-6 col-lg-4">
|
|
<div class="d-flex flex-column border rounded px-2 py-1">
|
|
<div class="d-flex align-items-center gap-2">
|
|
<span class="badge bg-light text-dark">{{ st.role.name }}</span>
|
|
{% if st.status == 'approved' %}
|
|
<span class="badge bg-success">تایید شد</span>
|
|
{% elif st.status == 'rejected' %}
|
|
<span class="badge bg-danger">رد شد</span>
|
|
{% else %}
|
|
<span class="badge bg-warning text-dark">در انتظار</span>
|
|
{% endif %}
|
|
</div>
|
|
{% if st.status == 'rejected' and st.reason %}
|
|
<div class="mt-1 small text-danger">علت: {{ st.reason }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
<div class="col-12 d-flex justify-content-between mt-3">
|
|
{% if previous_step %}
|
|
<a href="{% url 'processes:step_detail' instance.id previous_step.id %}" class="btn btn-label-secondary">قبلی</a>
|
|
{% else %}
|
|
<span></span>
|
|
{% endif %}
|
|
{% if step_instance.status == 'completed' %}
|
|
{% if next_step %}
|
|
<a href="{% url 'processes:step_detail' instance.id next_step.id %}" class="btn btn-primary">بعدی</a>
|
|
{% else %}
|
|
<a href="{% url 'processes:request_list' %}" class="btn btn-success">اتمام</a>
|
|
{% endif %}
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- Delete Confirmation Modal (final settlement payments) -->
|
|
<div class="modal fade" id="deletePaymentModal" tabindex="-1" aria-labelledby="deletePaymentModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="deletePaymentModalLabel">تایید حذف تراکنش</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
آیا از حذف این تراکنش مطمئن هستید؟ این عمل قابل بازگشت نیست.
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">انصراف</button>
|
|
<button type="button" class="btn btn-danger" onclick="confirmDeletePayment()" data-bs-dismiss="modal">حذف</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Approve Final Settlement Modal -->
|
|
<div class="modal fade" id="approveFinalSettleModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<form method="post">
|
|
{% csrf_token %}
|
|
<input type="hidden" name="action" value="approve">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">تایید تسویه نهایی</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
{% if invoice.remaining_amount != 0 %}
|
|
<div class="alert alert-warning" role="alert">
|
|
مانده فاکتور صفر نیست: <strong>{{ invoice.remaining_amount|floatformat:0|intcomma:False }} تومان</strong><br>
|
|
تا صفر نشود امکان تایید نیست.
|
|
</div>
|
|
{% else %}
|
|
آیا از تایید این مرحله اطمینان دارید؟
|
|
{% endif %}
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-label-secondary" data-bs-dismiss="modal">انصراف</button>
|
|
<button type="submit" class="btn btn-success" {% if invoice.remaining_amount != 0 %}disabled{% endif %}>تایید</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Reject Final Settlement Modal -->
|
|
<div class="modal fade" id="rejectFinalSettleModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<form method="post">
|
|
{% csrf_token %}
|
|
<input type="hidden" name="action" value="reject">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">رد تسویه نهایی</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="mb-2">
|
|
<label class="form-label">علت رد</label>
|
|
<textarea class="form-control" name="reject_reason" rows="3" required></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-label-secondary" data-bs-dismiss="modal">انصراف</button>
|
|
<button type="submit" class="btn btn-danger">ثبت رد</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
{% endblock %}
|
|
|
|
{% block script %}
|
|
<script src="https://unpkg.com/persian-date@latest/dist/persian-date.min.js"></script>
|
|
<script src="https://unpkg.com/persian-datepicker@latest/dist/js/persian-datepicker.min.js"></script>
|
|
<script>
|
|
(function initPersianDatePicker(){
|
|
if (window.$ && $.fn.persianDatepicker && $('#id_payment_date').length) {
|
|
$('#id_payment_date').persianDatepicker({
|
|
format: 'YYYY/MM/DD', initialValue: false, autoClose: true, persianDigit: false, observer: true,
|
|
calendar: { persian: { locale: 'fa', leapYearMode: 'astronomical' } },
|
|
onSelect: function(unix){
|
|
const g = new window.persianDate(unix).toCalendar('gregorian').format('YYYY-MM-DD');
|
|
$('#id_payment_date').attr('data-gregorian', g);
|
|
}
|
|
});
|
|
}
|
|
})();
|
|
|
|
function buildForm(){
|
|
const fd = new FormData(document.getElementById('formFinalPayment'));
|
|
const g = document.getElementById('id_payment_date').getAttribute('data-gregorian');
|
|
if (g) { fd.set('payment_date', g); }
|
|
return fd;
|
|
}
|
|
(function(){
|
|
const btn = document.getElementById('btnAddFinalPayment');
|
|
if (!btn) return;
|
|
btn.addEventListener('click', function(){
|
|
const fd = buildForm();
|
|
// Frontend validation
|
|
const amount = document.getElementById('id_amount').value.trim();
|
|
const payDate = document.getElementById('id_payment_date').value.trim();
|
|
const method = document.getElementById('id_payment_method').value.trim();
|
|
const ref = document.getElementById('id_reference_number').value.trim();
|
|
const img = document.getElementById('id_receipt_image').files[0];
|
|
const dir = document.getElementById('id_direction').value;
|
|
if (!amount || !payDate || !method || !ref || !img) {
|
|
showToast('همه فیلدها الزامی است', 'danger');
|
|
return;
|
|
}
|
|
fetch('{% url "invoices:add_final_payment" instance.id step.id %}', { method:'POST', body: fd })
|
|
.then(r=>r.json()).then(resp=>{
|
|
if (resp.success) {
|
|
showToast('تراکنش ثبت شد', 'success');
|
|
if (resp.redirect) setTimeout(()=>{ window.location.href = resp.redirect; }, 700);
|
|
} else {
|
|
showToast(resp.message || 'خطا در ثبت تراکنش', 'danger');
|
|
}
|
|
}).catch(()=> showToast('خطا در ارتباط با سرور', 'danger'));
|
|
});
|
|
})();
|
|
|
|
let deleteTargetId = null;
|
|
function openDeleteModal(id){
|
|
deleteTargetId = id;
|
|
const modal = new bootstrap.Modal(document.getElementById('deletePaymentModal'));
|
|
modal.show();
|
|
}
|
|
function confirmDeletePayment(){
|
|
if (!deleteTargetId) return;
|
|
const fd = new FormData();
|
|
fd.append('csrfmiddlewaretoken', document.querySelector('input[name=csrfmiddlewaretoken]').value);
|
|
fetch(`{% url "invoices:delete_final_payment" instance.id step.id 0 %}`.replace('/0/', `/${deleteTargetId}/`), { method:'POST', body: fd })
|
|
.then(r=>r.json()).then(resp=>{
|
|
if (resp.success) {
|
|
showToast('حذف شد', 'success');
|
|
if (resp.redirect) setTimeout(()=>{ window.location.href = resp.redirect; }, 500);
|
|
} else {
|
|
showToast(resp.message || 'خطا در حذف', 'danger');
|
|
}
|
|
}).catch(()=> showToast('خطا در ارتباط با سرور', 'danger'));
|
|
}
|
|
|
|
// Legacy approve button removed; using modal forms below
|
|
</script>
|
|
{% endblock %}
|
|
|