gozaresh vosol motalebat
This commit is contained in:
1 parent
0d867367f9
commit
99a86c7073
4 files changed
+404
-1
No files matched your search
@@ -94,6 +94,9 @@
|
||||
گزارش
|
||||
</a>
|
||||
|
||||
<a href="{% url 'admin_panel:report_debtors_no_vosol' %}" style="padding-right: 10px;" class="navbar-item">
|
||||
گزارش وصول مطالبات
|
||||
</a>
|
||||
|
||||
<a href="{% url 'admin_panel:inventory_movements_report' %}" style="padding-right: 10px;" class="navbar-item">
|
||||
گزارش جامع انبار
|
||||
|
||||
@@ -0,0 +1,288 @@
|
||||
{% extends "./base.html" %}
|
||||
{% load custom_tags %}
|
||||
|
||||
{% if perms.admin_panel.ViewReportMali %}
|
||||
{% block bodyPage %}
|
||||
<style>
|
||||
/* استایل پایه کارتها */
|
||||
.risk-card {
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #eee;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
.risk-card:hover { transform: translateY(-3px); box-shadow: 0 4px 15px rgba(0,0,0,0.05); }
|
||||
.border-danger { border-bottom: 4px solid #f14668 !important; }
|
||||
.border-warning { border-bottom: 4px solid #ffdd57 !important; }
|
||||
|
||||
/* استایل جدول و باکس توضیحات */
|
||||
.table-responsive-wrapper { width: 100%; overflow-x: auto; border-radius: 8px; border: 1px solid #eaeaea; }
|
||||
.table th, .table td { vertical-align: middle !important; }
|
||||
.desc-box {
|
||||
max-width: 250px;
|
||||
white-space: normal;
|
||||
background: #f8fafc;
|
||||
padding: 6px 8px;
|
||||
border-radius: 4px;
|
||||
border-right: 3px solid #3273dc;
|
||||
font-size: 0.8rem;
|
||||
line-height: 1.5;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
/* استایل هدرهای قابل سورت */
|
||||
.sortable-th { cursor: pointer; color: #363636; text-decoration: none !important; display: inline-flex; align-items: center; gap: 5px; }
|
||||
.sortable-th:hover { color: #3273dc; }
|
||||
.sort-icon { font-size: 0.8rem; }
|
||||
.sort-icon.active { color: #3273dc; }
|
||||
.sort-icon.inactive { color: #dbdbdb; }
|
||||
|
||||
/* ========================================= */
|
||||
/* تنظیمات اختصاصی موبایل (Responsive Design) */
|
||||
/* ========================================= */
|
||||
@media screen and (max-width: 768px) {
|
||||
.risk-card { padding: 1rem 0.5rem !important; }
|
||||
.risk-card .title { font-size: 1.1rem !important; margin-bottom: 0.2rem !important; }
|
||||
.risk-card .heading { font-size: 0.65rem !important; margin-bottom: 0.5rem !important; letter-spacing: 0; }
|
||||
|
||||
/* بهبود نمایش کنترل پنل در موبایل */
|
||||
.control-panel-box { padding: 1rem !important; }
|
||||
.control-panel-box .label { font-size: 0.8rem; margin-bottom: 0.3rem; }
|
||||
|
||||
/* کوچک کردن فونتهای جدول برای جا شدن بهتر در موبایل */
|
||||
.table th, .table td { font-size: 0.8rem; padding: 0.5rem 0.4rem !important; }
|
||||
.desc-box { font-size: 0.75rem; min-width: 180px; }
|
||||
.sortable-th { font-size: 0.75rem; }
|
||||
|
||||
/* دکمههای جدول در موبایل */
|
||||
.action-btn span:last-child { display: none; } /* مخفی کردن متن دکمه، فقط آیکون میماند */
|
||||
.action-btn { padding: 0.5rem !important; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="container is-fluid mt-4 mb-6">
|
||||
<div class="columns m-0 is-multiline">
|
||||
<div class="column is-12">
|
||||
|
||||
<div class="mb-5">
|
||||
<h2 class="title is-5 is-size-6-mobile mb-4 has-text-grey-dark">
|
||||
<i class="fas fa-chart-line has-text-info"></i>
|
||||
داشبورد هوشمند وصول مطالبات (رها شده)
|
||||
</h2>
|
||||
|
||||
<!-- داشبورد آماری دقیق و تفکیک شده -->
|
||||
<div class="columns is-multiline is-mobile">
|
||||
<div class="column is-6-mobile is-3-desktop">
|
||||
<div class="box risk-card border-danger has-text-centered p-4">
|
||||
<p class="heading has-text-grey">مشتریان هرگز پیگیری نشده</p>
|
||||
<p class="title is-size-4 has-text-dark">{{ stats.never_count }} <span class="is-size-6 has-text-grey">نفر</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-6-mobile is-3-desktop">
|
||||
<div class="box risk-card border-danger has-text-centered p-4">
|
||||
<p class="heading has-text-grey">مبلغ (هرگز پیگیری نشده)</p>
|
||||
<p class="title is-size-4 has-text-danger-dark toCurrency">{{ stats.never_amount }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-6-mobile is-3-desktop">
|
||||
<div class="box risk-card border-warning has-text-centered p-4">
|
||||
<p class="heading has-text-grey">رها شده (بیش از {{ current_days }} روز)</p>
|
||||
<p class="title is-size-4 has-text-dark">{{ stats.expired_count }} <span class="is-size-6 has-text-grey">نفر</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-6-mobile is-3-desktop">
|
||||
<div class="box risk-card border-warning has-text-centered p-4">
|
||||
<p class="heading has-text-grey">مبلغ (پیگیری رها شده)</p>
|
||||
<p class="title is-size-4 has-text-danger-dark toCurrency">{{ stats.expired_amount }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- کنترل پنل فیلترها و زمان (بهینهشده برای موبایل) -->
|
||||
<div class="box mb-4 has-background-white-ter control-panel-box">
|
||||
<form method="GET" action="">
|
||||
<input type="hidden" name="sort" value="{{ current_sort }}">
|
||||
<input type="hidden" name="dir" value="{{ current_dir }}">
|
||||
|
||||
<div class="columns is-multiline is-vcentered">
|
||||
<div class="column is-12-mobile is-5-tablet">
|
||||
<div class="field">
|
||||
<label class="label">وضعیت پیگیری:</label>
|
||||
<div class="control">
|
||||
<div class="select is-fullwidth">
|
||||
<select name="filter_type" class="is-size-7-mobile">
|
||||
<option value="all" {% if current_filter == 'all' %}selected{% endif %}>همه نیازمند پیگیری</option>
|
||||
<option value="never" {% if current_filter == 'never' %}selected{% endif %}>فقط هرگز پیگیری نشدهها</option>
|
||||
<option value="expired" {% if current_filter == 'expired' %}selected{% endif %}>فقط پیگیریهای رها شده</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-12-mobile is-4-tablet">
|
||||
<div class="field">
|
||||
<label class="label">بدون اقدام بیش از (روز):</label>
|
||||
<div class="control">
|
||||
<input type="number" name="days" class="input is-size-7-mobile" value="{{ current_days }}" min="1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-12-mobile is-3-tablet">
|
||||
<div class="field">
|
||||
<!-- در موبایل لیبل خالی میگذاریم تا ارتفاع حفظ شود، در دسکتاپ مخفی است -->
|
||||
<label class="label is-hidden-mobile"> </label>
|
||||
<div class="control">
|
||||
<button type="submit" class="button is-info is-fullwidth">
|
||||
<i class="fas fa-filter ml-2"></i> اعمال فیلتر
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- جدول دادهها -->
|
||||
<div class="table-responsive-wrapper shadow-sm mb-4">
|
||||
<table class="table m-auto has-text-centered is-bordered is-striped is-narrow is-fullwidth is-hoverable mb-0">
|
||||
<thead class="has-background-light">
|
||||
<tr>
|
||||
<th style="width: 40px;">#</th>
|
||||
<th>نام مشتری</th>
|
||||
<th>شماره تماس</th>
|
||||
<th>مانده کل حساب</th>
|
||||
|
||||
<!-- قابلیت Sort دوطرفه -->
|
||||
<th>
|
||||
<a class="sortable-th" href="?filter_type={{ current_filter }}&days={{ current_days }}&sort=amount&dir={% if current_sort == 'amount' and current_dir == 'desc' %}asc{% else %}desc{% endif %}">
|
||||
مبلغ معوقه
|
||||
<i class="fas fa-sort{% if current_sort == 'amount' %}-{{ current_dir }}{% endif %} sort-icon {% if current_sort == 'amount' %}active{% else %}inactive{% endif %}"></i>
|
||||
</a>
|
||||
</th>
|
||||
|
||||
<th>
|
||||
<a class="sortable-th" href="?filter_type={{ current_filter }}&days={{ current_days }}&sort=oldest_date&dir={% if current_sort == 'oldest_date' and current_dir == 'desc' %}asc{% else %}desc{% endif %}">
|
||||
تاریخ قدیمیترین تاخیر
|
||||
<i class="fas fa-sort{% if current_sort == 'oldest_date' %}-{{ current_dir }}{% endif %} sort-icon {% if current_sort == 'oldest_date' %}active{% else %}inactive{% endif %}"></i>
|
||||
</a>
|
||||
</th>
|
||||
|
||||
<th style="min-width: 230px;">
|
||||
<a class="sortable-th" href="?filter_type={{ current_filter }}&days={{ current_days }}&sort=last_action&dir={% if current_sort == 'last_action' and current_dir == 'desc' %}asc{% else %}desc{% endif %}">
|
||||
آخرین سابقه وصول
|
||||
<i class="fas fa-sort{% if current_sort == 'last_action' %}-{{ current_dir }}{% endif %} sort-icon {% if current_sort == 'last_action' %}active{% else %}inactive{% endif %}"></i>
|
||||
</a>
|
||||
</th>
|
||||
|
||||
<th>عملیات</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for customer in page_obj %}
|
||||
<tr>
|
||||
<td>{{ forloop.counter0|add:page_obj.start_index }}</td>
|
||||
|
||||
<td>
|
||||
<a target="_blank" href="{% url 'admin_panel:customerNew' customer.id %}" class="has-text-link has-text-weight-bold">
|
||||
{{ customer.name_family }}
|
||||
</a>
|
||||
</td>
|
||||
|
||||
<td dir="ltr" class="has-text-weight-medium">
|
||||
<div class="is-size-7-mobile">{{ customer.phone1|default:"-" }}</div>
|
||||
{% if customer.phone2 %}<div class="is-size-7-mobile">{{ customer.phone2 }}</div>{% endif %}
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<span class="toCurrency has-text-grey-dark is-size-7">{{ customer.net_debt }}</span>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<span class="toCurrency has-text-danger has-text-weight-bold is-size-6 is-size-7-mobile">
|
||||
{{ customer.overdue_amount }}
|
||||
</span>
|
||||
<br>
|
||||
<span class="tag is-light is-danger is-small mt-1">{{ customer.overdue_count }} فقره</span>
|
||||
</td>
|
||||
|
||||
<td class="has-text-danger-dark has-text-weight-semibold is-size-7">
|
||||
{{ customer.oldest_overdue_date | toJalali }}
|
||||
</td>
|
||||
|
||||
<td style="text-align: right;">
|
||||
{% if customer.last_vosol_date %}
|
||||
<div class="has-text-grey-dark is-size-7 has-text-weight-semibold">
|
||||
<i class="far fa-calendar-alt ml-1"></i> {{ customer.last_vosol_date | toJalali }}
|
||||
</div>
|
||||
<div class="desc-box has-text-grey">
|
||||
{{ customer.last_vosol_desc|default:"(بدون توضیحات ثبت شده)" }}
|
||||
</div>
|
||||
{% else %}
|
||||
<span class="tag is-danger is-light has-text-weight-bold is-size-7-mobile">هرگز پیگیری نشده</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<!-- استفاده از کلاس action-btn برای ریسپانسیو شدن دکمه در موبایل -->
|
||||
<a target="_blank" href="{% url 'admin_panel:customerAction' customer.id %}" class="button is-small is-info is-light has-text-weight-bold action-btn">
|
||||
<span class="icon is-small"><i class="fas fa-phone-alt"></i></span>
|
||||
<span>ثبت تماس</span>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="8" class="has-text-grey p-5">
|
||||
<i class="fas fa-check-circle has-text-success is-size-4 mb-2"></i><br>
|
||||
با این تنظیمات و فیلترها، هیچ مشتری بدهکار رها شدهای یافت نشد.
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- صفحهبندی -->
|
||||
<div class="mt-4 is-flex is-justify-content-center">
|
||||
<div class="buttons">
|
||||
{% if page_obj.has_previous %}
|
||||
<a href="?page={{ page_obj.previous_page_number }}&filter_type={{ current_filter }}&days={{ current_days }}&sort={{ current_sort }}&dir={{ current_dir }}" class="button is-light is-small-mobile">صفحه قبل</a>
|
||||
{% endif %}
|
||||
{% if page_obj.has_next %}
|
||||
<a href="?page={{ page_obj.next_page_number }}&filter_type={{ current_filter }}&days={{ current_days }}&sort={{ current_sort }}&dir={{ current_dir }}" class="button is-light is-small-mobile">صفحه بعد</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block jsEnd %}
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// فرمتکننده ایمن و رفع خطای NaN
|
||||
$('.toCurrency').each(function() {
|
||||
var rawValue = $(this).text().trim();
|
||||
// بررسی میکنیم که متغیر خالی، نال یا None رشتهای نباشد
|
||||
if(rawValue && rawValue !== 'None' && rawValue !== '') {
|
||||
// حذف هر کاراکتری به جز اعداد و علامت منفی
|
||||
var cleanValue = rawValue.replace(/[^\d-]/g, '');
|
||||
if(!isNaN(cleanValue) && cleanValue !== '') {
|
||||
$(this).text(Number(cleanValue).toLocaleString('en-US'));
|
||||
} else {
|
||||
$(this).text('0');
|
||||
}
|
||||
} else {
|
||||
$(this).text('0');
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endif %}
|
||||
Reference in new issue
Block a user