hesabdari va dashboard
This commit is contained in:
1 parent
767e84cbcf
commit
bd775d6249
5 files changed
+808
-244
No files matched your search
@@ -99,6 +99,10 @@
|
|||||||
<a href="{% url 'admin_panel:inventory_movements_report' %}" style="padding-right: 10px;" class="navbar-item">
|
<a href="{% url 'admin_panel:inventory_movements_report' %}" style="padding-right: 10px;" class="navbar-item">
|
||||||
گزارش جامع انبار
|
گزارش جامع انبار
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
<a href="{% url 'admin_panel:financial_dashboard' %}" style="padding-right: 10px;" class="navbar-item">
|
||||||
|
داشبورد مدیریتی
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,372 @@
|
|||||||
|
{% extends "./base.html" %}
|
||||||
|
{% load custom_tags %}
|
||||||
|
|
||||||
|
{% block bodyPage %}
|
||||||
|
<div class="container is-fluid mt-4 mb-6">
|
||||||
|
|
||||||
|
<div class="columns is-vcentered pb-4 mb-5" style="border-bottom: 2px solid #f5f5f5;">
|
||||||
|
<div class="column is-12-mobile is-half-tablet has-text-centered-mobile">
|
||||||
|
<h1 class="title is-4 is-size-5-mobile has-text-dark">اتاق فرمان مالی و داشبورد تجاری</h1>
|
||||||
|
<p class="subtitle is-7 has-text-grey mt-1">رصد زنده شریان نقدینگی، سن مطالبات و مانیتورینگ پویای انبار | <span class="tag is-dark is-light">سال مالی</span></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column is-12-mobile is-half-tablet">
|
||||||
|
<form method="GET" class="box shadow-sm bg-light-filter p-3 m-0">
|
||||||
|
<div class="columns is-mobile is-multiline is-vcentered m-0">
|
||||||
|
<div class="column is-6-mobile is-narrow-tablet p-1">
|
||||||
|
<label class="label is-size-7 pr-1 mb-1">از تاریخ</label>
|
||||||
|
<input type="text" class="input is-small date-picker-az pwt-datepicker-input-element w-100" placeholder="آغاز دوره">
|
||||||
|
<input type="hidden" name="azTarikh" id="azTarikhAlt" value="{{ filters.azTarikh }}">
|
||||||
|
</div>
|
||||||
|
<div class="column is-6-mobile is-narrow-tablet p-1">
|
||||||
|
<label class="label is-size-7 pr-1 mb-1">تا تاریخ</label>
|
||||||
|
<input type="text" class="input is-small date-picker-ta pwt-datepicker-input-element w-100" placeholder="پایان دوره">
|
||||||
|
<input type="hidden" name="taTarikh" id="taTarikhAlt" value="{{ filters.taTarikh }}">
|
||||||
|
</div>
|
||||||
|
<div class="column is-12-mobile is-narrow-tablet p-1 mt-auto has-text-centered-mobile">
|
||||||
|
<button type="submit" class="button is-danger is-small is-fullwidth-mobile px-4">
|
||||||
|
<span>اعمال گزارش پویا</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{% if filters.azTarikh or filters.taTarikh %}
|
||||||
|
<div class="column is-12-mobile is-narrow-tablet p-1 mt-auto has-text-centered-mobile">
|
||||||
|
<a href="{% url 'admin_panel:financial_dashboard' %}" class="button is-light is-small is-fullwidth-mobile has-text-grey">حذف فیلتر</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns is-multiline">
|
||||||
|
<div class="column is-12-mobile is-4-tablet">
|
||||||
|
<div class="box p-4 has-background-white shadow-card border-left-danger h-100">
|
||||||
|
<div class="level is-mobile m-0">
|
||||||
|
<div class="level-left">
|
||||||
|
<div>
|
||||||
|
<p class="heading has-text-grey-dark is-size-7 mb-1">کل مطالبات معوق بازار (سرمایه قفل شده)</p>
|
||||||
|
<p class="title is-size-4 is-size-5-mobile toCurrency has-text-danger-dark">{{ kpi_total_unpaid }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-12-mobile is-4-tablet">
|
||||||
|
<div class="box p-4 has-background-white shadow-card border-left-success h-100">
|
||||||
|
<div class="level is-mobile m-0">
|
||||||
|
<div class="level-left">
|
||||||
|
<div>
|
||||||
|
<p class="heading has-text-grey-dark is-size-7 mb-1">حجم ناخالص فروش در بازه انتخابی</p>
|
||||||
|
<p class="title is-size-4 is-size-5-mobile toCurrency has-text-success-dark">{{ kpi_total_sales }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-12-mobile is-4-tablet">
|
||||||
|
<div class="box p-4 has-background-white shadow-card border-left-warning h-100">
|
||||||
|
<div class="level is-mobile m-0">
|
||||||
|
<div class="level-left">
|
||||||
|
<div>
|
||||||
|
<p class="heading has-text-grey-dark is-size-7 mb-1">تعداد چکهای سررسیدگذشته و برگشتی</p>
|
||||||
|
<p class="title is-size-4 is-size-5-mobile has-text-warning-dark">{{ kpi_overdue_checks }} <span class="is-size-6">فقره</span></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns is-multiline mt-1">
|
||||||
|
<div class="column is-12">
|
||||||
|
<div class="box p-4 shadow-card">
|
||||||
|
<div class="level is-mobile is-flex-wrap-wrap mb-4 pb-2" style="border-bottom: 1px solid #f0f0f0;">
|
||||||
|
<div class="level-left mb-2-mobile">
|
||||||
|
<p class="title is-6 is-size-7-mobile mb-0">📈 روند جمع کل بدهی مشتریان در ۱۲ ماه گذشته</p>
|
||||||
|
</div>
|
||||||
|
<div class="level-right">
|
||||||
|
<span class="tag is-info is-light is-rounded is-small-mobile">محاسبه بر اساس ماهیانه</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="chart-wrapper-medium">
|
||||||
|
<canvas id="debtTrendChart"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns is-multiline mt-1">
|
||||||
|
|
||||||
|
<div class="column is-12-mobile is-7-desktop">
|
||||||
|
<div class="box p-4 shadow-card h-100">
|
||||||
|
<p class="title is-6 is-size-7-mobile pb-2 mb-4" style="border-bottom: 1px solid #f0f0f0;">
|
||||||
|
📊 ماتریس ریسک و تحلیل سنی مطالبات
|
||||||
|
</p>
|
||||||
|
<div class="chart-wrapper-tall">
|
||||||
|
<canvas id="agingChart"></canvas>
|
||||||
|
</div>
|
||||||
|
<div class="notification is-light is-danger is-paddingless p-3 mt-4 is-size-7" style="line-height: 1.6;">
|
||||||
|
📌 <strong>سیگنال مدیریتی:</strong> مبالغ انباشته شده در بخش <strong>بحرانی (قرمز)</strong> پتانسیل بالایی برای تبدیل شدن به سوخت سرمایه دارند.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column is-12-mobile is-5-desktop">
|
||||||
|
<div class="box p-4 shadow-card h-100">
|
||||||
|
<p class="title is-6 is-size-7-mobile pb-2 mb-4" style="border-bottom: 1px solid #f0f0f0;">
|
||||||
|
💳 آنالیز کانالهای مالی ورودی نقدینگی
|
||||||
|
</p>
|
||||||
|
<div class="chart-wrapper-tall">
|
||||||
|
<canvas id="paymentMethodsChart"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column is-12">
|
||||||
|
<div class="box p-4 shadow-card">
|
||||||
|
<p class="title is-6 is-size-7-mobile pb-2 mb-4" style="border-bottom: 1px solid #f0f0f0;">
|
||||||
|
📦 رادار هزینههای جانبی کالا (مقایسه آماری خروج گارانتی در برابر اشانتیون)
|
||||||
|
</p>
|
||||||
|
<div class="columns is-vcentered is-multiline">
|
||||||
|
<div class="column is-12-mobile is-7-desktop">
|
||||||
|
<div class="chart-wrapper-short">
|
||||||
|
<canvas id="inventoryLeakChart"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-12-mobile is-5-desktop responsive-border-between">
|
||||||
|
<div class="px-3">
|
||||||
|
<h4 class="title is-7 mb-3 has-text-grey-dark">🔍 خلاصه وضعیت خروجیهای غیردرآمدی انبار:</h4>
|
||||||
|
<div class="tags has-addons mb-2 is-justify-content-space-between-mobile">
|
||||||
|
<span class="tag is-warning is-light is-flex-grow-1-mobile">حجم کالای مصرفی در تعهدات گارانتی</span>
|
||||||
|
<span class="tag is-warning">{{ warranty_qty }} عدد</span>
|
||||||
|
</div>
|
||||||
|
<div class="tags has-addons mb-4 is-justify-content-space-between-mobile">
|
||||||
|
<span class="tag is-info is-light is-flex-grow-1-mobile">حجم کالای مصرفی در جوایز و اشانتیون</span>
|
||||||
|
<span class="tag is-info">{{ promotion_qty }} عدد</span>
|
||||||
|
</div>
|
||||||
|
<p class="is-size-7 has-text-grey" style="text-align: justify; line-height: 1.8;">
|
||||||
|
💡 <strong>راهبرد نظارتی انبار:</strong> نوسان شدید در این دو نمودار به مدیر کمک میکند تا متوجه افت کیفیت در محصولات تامینشده (جهش گارانتی) یا توزیع بیضابطه و خارج از فاکتور توسط اپراتورها (جهش اشانتیون) شود.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* استایلهای پایه کارتها */
|
||||||
|
.shadow-card { box-shadow: 0 4px 12px rgba(0, 0, 0, 0.04); border: 1px solid #f0f0f0; border-radius: 8px; }
|
||||||
|
.border-left-danger { border-right: 4px solid #f14668 !important; }
|
||||||
|
.border-left-success { border-right: 4px solid #48c78e !important; }
|
||||||
|
.border-left-warning { border-right: 4px solid #ffdd57 !important; }
|
||||||
|
.bg-light-filter { background-color: #fafafa; border-radius: 6px; }
|
||||||
|
.h-100 { height: 100%; }
|
||||||
|
.w-100 { width: 100%; }
|
||||||
|
|
||||||
|
/* کانتینرهای ریسپانسیو برای نمودارها */
|
||||||
|
.chart-wrapper-tall { position: relative; height: 320px; width: 100%; }
|
||||||
|
.chart-wrapper-medium { position: relative; height: 280px; width: 100%; }
|
||||||
|
.chart-wrapper-short { position: relative; height: 220px; width: 100%; }
|
||||||
|
|
||||||
|
/* کلاس سفارشی برای جداکننده خطی بین متن و نمودار انبار */
|
||||||
|
.responsive-border-between {
|
||||||
|
border-right: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* مدیا کوئری اختصاصی برای موبایل و تبلتهای کوچک */
|
||||||
|
@media screen and (max-width: 1023px) {
|
||||||
|
/* در موبایل خط عمودی به خط افقی بالا تغییر میکند */
|
||||||
|
.responsive-border-between {
|
||||||
|
border-right: none;
|
||||||
|
border-top: 1px solid #f0f0f0;
|
||||||
|
padding-top: 1.5rem !important;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* کاهش ارتفاع نمودارها در موبایل برای جلوگیری از اسکرول طولانی */
|
||||||
|
.chart-wrapper-tall { height: 260px; }
|
||||||
|
.chart-wrapper-medium { height: 240px; }
|
||||||
|
.chart-wrapper-short { height: 200px; }
|
||||||
|
|
||||||
|
.mb-2-mobile { margin-bottom: 0.5rem !important; }
|
||||||
|
|
||||||
|
/* برای بهتر دیده شدن تگها در صفحه کوچک */
|
||||||
|
.is-flex-grow-1-mobile { flex-grow: 1; text-align: right; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block jsEnd %}
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
// ۱. پیادهسازی موتور تقویم جلالی
|
||||||
|
var pickerAz = $(".date-picker-az").pDatepicker({
|
||||||
|
altField: '#azTarikhAlt',
|
||||||
|
initialValue: false,
|
||||||
|
autoClose: true,
|
||||||
|
format: 'YYYY/MM/DD',
|
||||||
|
calendar: { persian: { locale: 'fa' } },
|
||||||
|
responsive: true // قابلیت تقویم برای موبایل
|
||||||
|
});
|
||||||
|
|
||||||
|
var pickerTa = $(".date-picker-ta").pDatepicker({
|
||||||
|
altField: '#taTarikhAlt',
|
||||||
|
initialValue: false,
|
||||||
|
autoClose: true,
|
||||||
|
format: 'YYYY/MM/DD',
|
||||||
|
calendar: { persian: { locale: 'fa' } },
|
||||||
|
responsive: true
|
||||||
|
});
|
||||||
|
|
||||||
|
{% if filters.azTarikh %} pickerAz.setDate({{ filters.azTarikh }}); {% endif %}
|
||||||
|
{% if filters.taTarikh %} pickerTa.setDate({{ filters.taTarikh }}); {% endif %}
|
||||||
|
|
||||||
|
// 🌟 نمودار روند خطی مانده حساب کل مشتریان در ۱۲ ماه
|
||||||
|
var ctxTrend = document.getElementById('debtTrendChart').getContext('2d');
|
||||||
|
|
||||||
|
var gradientTrend = ctxTrend.createLinearGradient(0, 0, 0, 300);
|
||||||
|
gradientTrend.addColorStop(0, 'rgba(62, 142, 208, 0.45)');
|
||||||
|
gradientTrend.addColorStop(1, 'rgba(62, 142, 208, 0.0)');
|
||||||
|
|
||||||
|
new Chart(ctxTrend, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: {{ trend_labels|safe }},
|
||||||
|
datasets: [{
|
||||||
|
label: 'جمع کل بدهی (ریال)',
|
||||||
|
data: {{ trend_data|safe }},
|
||||||
|
backgroundColor: gradientTrend,
|
||||||
|
borderColor: '#3e8ed0',
|
||||||
|
borderWidth: 2.5,
|
||||||
|
pointBackgroundColor: '#ffffff',
|
||||||
|
pointBorderColor: '#3e8ed0',
|
||||||
|
pointBorderWidth: 2,
|
||||||
|
pointRadius: 4,
|
||||||
|
pointHoverRadius: 6,
|
||||||
|
fill: true,
|
||||||
|
tension: 0.35
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
plugins: {
|
||||||
|
legend: { display: false },
|
||||||
|
tooltip: {
|
||||||
|
titleFont: { family: 'Tahoma, Vazir', size: 12 },
|
||||||
|
bodyFont: { family: 'Tahoma, Vazir', size: 12 },
|
||||||
|
callbacks: {
|
||||||
|
label: function(context) {
|
||||||
|
let label = context.dataset.label || '';
|
||||||
|
if (label) { label += ': '; }
|
||||||
|
if (context.parsed.y !== null) {
|
||||||
|
label += new Intl.NumberFormat('fa-IR').format(context.parsed.y);
|
||||||
|
}
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
beginAtZero: true,
|
||||||
|
ticks: { font: { family: 'Tahoma, Vazir', size: 10 } }
|
||||||
|
},
|
||||||
|
x: {
|
||||||
|
ticks: {
|
||||||
|
font: { family: 'Tahoma, Vazir', size: 10 },
|
||||||
|
maxRotation: 45, // چرخش لیبلها برای جا شدن در موبایل
|
||||||
|
minRotation: 0
|
||||||
|
},
|
||||||
|
grid: { display: false }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
interaction: {
|
||||||
|
intersect: false,
|
||||||
|
mode: 'index',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ۲. نمودار سنی مطالبات
|
||||||
|
var ctxAging = document.getElementById('agingChart').getContext('2d');
|
||||||
|
new Chart(ctxAging, {
|
||||||
|
type: 'bar',
|
||||||
|
data: {
|
||||||
|
labels: ['سررسید آینده', 'تاخیر ناچیز', 'تاخیر پرخطر', 'بحرانی'],
|
||||||
|
datasets: [{
|
||||||
|
data: {{ aging_data|safe }},
|
||||||
|
backgroundColor: ['rgba(72, 199, 142, 0.85)', 'rgba(255, 221, 87, 0.85)', 'rgba(241, 70, 104, 0.5)', 'rgba(241, 70, 104, 0.95)'],
|
||||||
|
borderColor: ['#48c78e', '#ffdd57', '#f14668', '#f14668'],
|
||||||
|
borderWidth: 1.5,
|
||||||
|
barThickness: 30 // ضخامت میلهها برای موبایل بهتر است
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
plugins: { legend: { display: false } },
|
||||||
|
scales: {
|
||||||
|
y: { beginAtZero: true, ticks: { font: { family: 'Tahoma, Vazir', size: 10 } } },
|
||||||
|
x: { ticks: { font: { family: 'Tahoma, Vazir', size: 10 } } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ۳. نمودار دایرهای تسویه مبالغ
|
||||||
|
var ctxPay = document.getElementById('paymentMethodsChart').getContext('2d');
|
||||||
|
new Chart(ctxPay, {
|
||||||
|
type: 'doughnut',
|
||||||
|
data: {
|
||||||
|
labels: {{ pay_labels|safe }},
|
||||||
|
datasets: [{
|
||||||
|
data: {{ pay_data|safe }},
|
||||||
|
backgroundColor: ['#485fc7', '#48c78e', '#ffdd57', '#f14668', '#3e8ed0', '#7a7a7a'],
|
||||||
|
borderWidth: 2
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
position: window.innerWidth < 768 ? 'bottom' : 'right', // در موبایل لجند پایین میرود
|
||||||
|
labels: { font: { family: 'Tahoma, Vazir', size: 11 }, boxWidth: 12 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ۴. نمودار افقی انبار
|
||||||
|
var ctxLeak = document.getElementById('inventoryLeakChart').getContext('2d');
|
||||||
|
new Chart(ctxLeak, {
|
||||||
|
type: 'bar',
|
||||||
|
data: {
|
||||||
|
labels: ['گارانتی', 'اشانتیون'],
|
||||||
|
datasets: [{
|
||||||
|
data: [{{ warranty_qty }}, {{ promotion_qty }}],
|
||||||
|
backgroundColor: ['rgba(255, 221, 87, 0.8)', 'rgba(62, 142, 208, 0.8)'],
|
||||||
|
borderColor: ['#ffdd57', '#3e8ed0'],
|
||||||
|
borderWidth: 1.2,
|
||||||
|
barThickness: 25
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
indexAxis: 'y',
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
plugins: { legend: { display: false } },
|
||||||
|
scales: {
|
||||||
|
x: { beginAtZero: true, ticks: { font: { family: 'Tahoma, Vazir', size: 10 } } },
|
||||||
|
y: { ticks: { font: { family: 'Tahoma, Vazir', size: 10 } } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
@@ -3,236 +3,248 @@
|
|||||||
|
|
||||||
{% if perms.admin_panel.ViewReportMali %}
|
{% if perms.admin_panel.ViewReportMali %}
|
||||||
{% block bodyPage %}
|
{% block bodyPage %}
|
||||||
<div class="">
|
<style>
|
||||||
<div class="columns m-0">
|
.risk-card { transition: transform 0.2s, box-shadow 0.2s; border-radius: 8px; border: 1px solid #eee; }
|
||||||
<div class="column is-12">
|
.risk-card:hover { transform: translateY(-3px); box-shadow: 0 4px 15px rgba(0,0,0,0.05); }
|
||||||
<div class="buttons">
|
.border-danger { border-bottom: 4px solid #f14668 !important; }
|
||||||
|
.border-warning { border-bottom: 4px solid #ffdd57 !important; }
|
||||||
|
.border-info { border-bottom: 4px solid #3e8ed0 !important; }
|
||||||
|
.border-dark { border-bottom: 4px solid #363636 !important; }
|
||||||
|
.filter-tabs { margin-bottom: 1.5rem; }
|
||||||
|
</style>
|
||||||
|
|
||||||
</div>
|
<div class="container is-fluid mt-4 mb-6">
|
||||||
|
<div class="columns m-0 is-multiline">
|
||||||
<p class="has-text-centered">
|
<div class="column is-12">
|
||||||
{% if page_name == 'sarresidShodeha' %}
|
|
||||||
<p class="toCurrency has-text-centered">
|
|
||||||
سررسید شده ها
|
|
||||||
{{ sum_sarresid }}
|
|
||||||
</p>
|
|
||||||
{% endif %}
|
|
||||||
{% if page_name == 'sarresidNaShodeha' %}
|
|
||||||
<p class="toCurrency has-text-centered">
|
|
||||||
سررسید نشده ها
|
|
||||||
{{ sarresidNashode }}
|
|
||||||
</p>
|
|
||||||
{% endif %}
|
|
||||||
{% if page_name == 'checkPass' %}
|
|
||||||
چک های پاس شده
|
|
||||||
{% endif %}
|
|
||||||
{% if page_name == 'checkPassFaild' %}
|
|
||||||
|
|
||||||
<p class="toCurrency has-text-centered">
|
|
||||||
چک های پاس نشده
|
|
||||||
{{ count_check_passfail }}
|
|
||||||
عدد | جمع مبلغ:
|
|
||||||
{{ sum_check_passfail }}
|
|
||||||
</p>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<span class="toCurrency">{{ mandehesabKP }}</span>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="table-container column is-12-desktop is-12-mobile m-0">
|
|
||||||
<table class="table m-auto has-text-centered table is-bordered is-striped is-narrow is-fullwidth mojodiTB ">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th style="min-width: 150px;">مبلغ</th>
|
|
||||||
<th style="min-width: 150px;">نام</th>
|
|
||||||
<th style="min-width: 100px;">نوع</th>
|
|
||||||
<th style="min-width: 100px;">سررسید</th>
|
|
||||||
<th style="min-width: 100px;">ثبت</th>
|
|
||||||
<th style="min-width: 400px;">توضیحات</th>
|
|
||||||
<th style="min-width: 100;">وضعیت</th>
|
|
||||||
<th style="min-width: 100;">وصول شد</th>
|
|
||||||
|
|
||||||
|
{% if page_name == 'sarresidShodeha' %}
|
||||||
</tr>
|
<div class="mb-5">
|
||||||
</thead>
|
<h2 class="title is-5 mb-4 has-text-grey-dark">📊 حسابداری | سررسید شده ها</h2>
|
||||||
<tfoot>
|
|
||||||
<tr>
|
<div class="columns is-multiline is-mobile">
|
||||||
<th class="toCurrency" style="min-width: 150px;">مبلغ</th>
|
|
||||||
<th style="min-width: 150px;">نام</th>
|
<div class="column is-6-mobile is-3-desktop">
|
||||||
<th style="min-width: 100px;">نوع</th>
|
<div class="box risk-card border-dark has-text-centered p-4 h-100">
|
||||||
<th style="min-width: 100px;">سررسید</th>
|
<p class="heading has-text-grey">کل مطالبات سررسید شده</p>
|
||||||
<th style="min-width: 100px;">ثبت</th>
|
<p class="title is-size-4 is-size-5-mobile has-text-dark toCurrency">{{ sum_sarresid }}</p>
|
||||||
<th style="min-width: 400px;">توضیحات</th>
|
</div>
|
||||||
<th style="min-width: 100;">وضعیت</th>
|
</div>
|
||||||
<th style="min-width: 100;">وصول شد</th>
|
|
||||||
|
|
||||||
</tr>
|
|
||||||
</tfoot>
|
|
||||||
<tbody id="listPrds">
|
|
||||||
|
|
||||||
{% for sarresid in all_sarresid %}
|
|
||||||
<tr class="IteamPrd">
|
|
||||||
<td>
|
|
||||||
<span class="bedehkar toCurrency">{{ sarresid.price }}</span>
|
|
||||||
<span class=" is-hidden"></span>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td class="">
|
|
||||||
<a target="_blank" href="{% url 'admin_panel:customerNew' sarresid.customer.id %}">
|
|
||||||
{{ sarresid.customer.name_family }}
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
|
|
||||||
<td class="">
|
|
||||||
{% if sarresid.type == 'check' %}چک{% endif %}
|
|
||||||
{% if sarresid.type == 'nesiye' %}نسیه{% endif %}
|
|
||||||
|
|
||||||
</td>
|
|
||||||
<td class="">{{ sarresid.sarresidDT | toJalali }}</td>
|
|
||||||
<td class="">{{ sarresid.createdDT | toJalali }}</td>
|
|
||||||
<td class="desc">
|
|
||||||
{% if sarresid.desc %}
|
|
||||||
{{ sarresid.desc }}
|
|
||||||
{% endif %}
|
|
||||||
{% if sarresid.factor %}
|
|
||||||
فاکتور شماره {{ sarresid.factor.id }}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td class="">
|
|
||||||
{% if sarresid.payStatus == "true" %}پرداخت شده{% endif %}
|
|
||||||
{% if sarresid.payStatus == "false" %}پرداخت نشده{% endif %}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td class="">
|
|
||||||
{% if sarresid.payStatus == "false" %}
|
|
||||||
<span class="button is-danger VosolButton" csid= {{ sarresid.customer.id }} price={{ sarresid.price }} type={{ sarresid.type }} articlID={{ sarresid.id }}>وصول شد</span>
|
|
||||||
{% else %}
|
|
||||||
<span>بله</span>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
{% if all_sarresid.has_previous %}
|
<div class="column is-6-mobile is-3-desktop">
|
||||||
<a href="?page={{ all_sarresid.previous_page_number }}" class="button is-danger">صفحه قبل</a>
|
<div class="box risk-card border-info has-text-centered p-4 h-100">
|
||||||
{% endif %}
|
<p class="heading has-text-grey">تأخیر ناچیز (۱ تا ۳۰ روز)</p>
|
||||||
{% if all_sarresid.has_next %}
|
<p class="title is-size-4 is-size-5-mobile has-text-info toCurrency">{{ minor_sum }}</p>
|
||||||
<a href="?page={{ all_sarresid.next_page_number }}" class="button is-danger">صفحه بعد</a>
|
</div>
|
||||||
{% endif %}
|
</div>
|
||||||
|
|
||||||
</div>
|
<div class="column is-6-mobile is-3-desktop">
|
||||||
|
<div class="box risk-card border-warning has-text-centered p-4 h-100">
|
||||||
|
<p class="heading has-text-grey">پرخطر (۳۱ تا ۹۰ روز)</p>
|
||||||
|
<p class="title is-size-4 is-size-5-mobile has-text-warning-dark toCurrency">{{ high_sum }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
<div class="column is-6-mobile is-3-desktop">
|
||||||
</div>
|
<div class="box risk-card border-danger has-text-centered p-4 h-100">
|
||||||
</div>
|
<p class="heading has-text-grey">بحرانی (بیش از ۹۰ روز)</p>
|
||||||
|
<p class="title is-size-4 is-size-5-mobile has-text-danger toCurrency">{{ critical_sum }}</p>
|
||||||
<div class="modal">
|
</div>
|
||||||
<div class="modal-background"></div>
|
</div>
|
||||||
<div class="modal-card">
|
|
||||||
<header class="modal-card-head">
|
|
||||||
<p class="modal-card-title">Modal title</p>
|
|
||||||
<button class="delete" aria-label="close"></button>
|
|
||||||
</header>
|
|
||||||
<section class="modal-card-body">
|
|
||||||
<form>
|
|
||||||
<input type="text" class="is-hidden" name="articlID">
|
|
||||||
</form>
|
|
||||||
</section>
|
|
||||||
<footer class="modal-card-foot">
|
|
||||||
<button class="button is-success">Save changes</button>
|
|
||||||
<button class="button">Cancel</button>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form class="modal nesiyeModal" method="post" action="{% url 'admin_panel:pardakhtNesiye' 9101010101 %}">
|
|
||||||
{% csrf_token %}
|
|
||||||
<div class="modal-background"></div>
|
|
||||||
<div class="modal-card">
|
|
||||||
<header class="modal-card-head">
|
|
||||||
<p class="modal-card-title">نسیه</p>
|
|
||||||
<button class="delete" aria-label="close"></button>
|
|
||||||
</header>
|
|
||||||
<section class="modal-card-body">
|
|
||||||
|
|
||||||
|
|
||||||
<div class="field">
|
|
||||||
<label class="label">مانده از این نسیه</label>
|
|
||||||
<div class="control">
|
|
||||||
<input id="priceBedehi" disabled class="input reset_input" type="text" placeholder="واحد تومان">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<input name="sr_articlID" id="sr_articlID" class="input reset_input is-hidden" type="text">
|
|
||||||
|
|
||||||
|
|
||||||
<div class="field">
|
|
||||||
<label class="label">مبلغ پرداختی فعلی</label>
|
|
||||||
<div class="control">
|
|
||||||
<input name="next_sarresidPrice" required class="input reset_input" type="text" placeholder="واحد تومان">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field">
|
|
||||||
<label class="label">نحوه پرداخت</label>
|
|
||||||
<div class="control">
|
|
||||||
<div class="select is-fullwidth">
|
|
||||||
<select required class="reset_input" name="nahvePardakht">
|
|
||||||
<option></option>
|
|
||||||
<option value="check">چک</option>
|
|
||||||
<option value="naghd">نقد</option>
|
|
||||||
<option value="kartbekart">کارت به کارت</option>
|
|
||||||
<option value="kartkhan">کارت خوان</option>
|
|
||||||
<option value="takhfif">تخفیف</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="tabs is-toggle is-fullwidth filter-tabs is-small-mobile">
|
||||||
|
<ul>
|
||||||
|
<li class="{% if current_filter == 'all' or not current_filter %}is-active{% endif %}">
|
||||||
|
<a href="?risk_level=all">همه معوقات</a>
|
||||||
|
</li>
|
||||||
|
<li class="{% if current_filter == 'minor' %}is-active{% endif %}">
|
||||||
|
<a href="?risk_level=minor" class="has-text-info">تأخیر ناچیز</a>
|
||||||
|
</li>
|
||||||
|
<li class="{% if current_filter == 'high' %}is-active{% endif %}">
|
||||||
|
<a href="?risk_level=high" class="has-text-warning-dark">پرخطر</a>
|
||||||
|
</li>
|
||||||
|
<li class="{% if current_filter == 'critical' %}is-active{% endif %}">
|
||||||
|
<a href="?risk_level=critical" class="has-text-danger">بحرانی</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if page_name == 'sarresidNaShodeha' %}
|
||||||
|
<p class="toCurrency has-text-centered is-size-5 mb-4">سررسید نشده ها: {{ sarresidNashode }}</p>
|
||||||
|
{% endif %}
|
||||||
|
{% if page_name == 'checkPass' %}
|
||||||
|
<p class="has-text-centered is-size-5 mb-4">چک های پاس شده</p>
|
||||||
|
{% endif %}
|
||||||
|
{% if page_name == 'checkPassFaild' %}
|
||||||
|
<p class="has-text-centered is-size-5 mb-4">
|
||||||
|
چک های برگشتی: {{ count_check_passfail }} فقره | جمع مبلغ: <span class="toCurrency">{{ sum_check_passfail }}</span>
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="table-container column is-12-desktop is-12-mobile m-0 shadow-sm" style="border-radius: 8px; border: 1px solid #eaeaea;">
|
||||||
|
<table class="table m-auto has-text-centered is-bordered is-striped is-narrow is-fullwidth is-hoverable mojodiTB">
|
||||||
|
<thead class="has-background-light">
|
||||||
|
<tr>
|
||||||
|
<th style="min-width: 150px;">مبلغ (تومان)</th>
|
||||||
|
<th style="min-width: 150px;">نام مشتری</th>
|
||||||
|
<th style="min-width: 100px;">نوع سند</th>
|
||||||
|
<th style="min-width: 120px;">سررسید</th>
|
||||||
|
<th style="min-width: 120px;">تاریخ ثبت</th>
|
||||||
|
<th style="min-width: 300px;">توضیحات</th>
|
||||||
|
<th style="min-width: 100px;">وضعیت</th>
|
||||||
|
<th style="min-width: 100px;">عملیات</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="listPrds">
|
||||||
|
{% for sarresid in all_sarresid %}
|
||||||
|
<tr class="IteamPrd">
|
||||||
|
<td>
|
||||||
|
<span class="bedehkar toCurrency has-text-weight-bold">{{ sarresid.price }}</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a target="_blank" href="{% url 'admin_panel:customerNew' sarresid.customer.id %}" class="has-text-link">
|
||||||
|
{{ sarresid.customer.name_family }}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if sarresid.type == 'check' %}<span class="tag is-light is-rounded">چک</span>{% endif %}
|
||||||
|
{% if sarresid.type == 'nesiye' %}<span class="tag is-white is-rounded border">نسیه</span>{% endif %}
|
||||||
|
</td>
|
||||||
|
<td class="has-text-danger-dark">{{ sarresid.sarresidDT | toJalali }}</td>
|
||||||
|
<td class="has-text-grey is-size-7">{{ sarresid.createdDT | toJalali }}</td>
|
||||||
|
<td class="desc is-size-7" style="text-align: right;">
|
||||||
|
{% if sarresid.desc %}{{ sarresid.desc }}<br>{% endif %}
|
||||||
|
{% if sarresid.factor %}
|
||||||
|
<span class="tag is-light is-info">فاکتور شماره {{ sarresid.factor.id }}</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if sarresid.payStatus == "true" %}<span class="has-text-success">پرداخت شده</span>{% endif %}
|
||||||
|
{% if sarresid.payStatus == "false" %}<span class="has-text-danger">پرداخت نشده</span>{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if sarresid.payStatus == "false" %}
|
||||||
|
<button class="button is-danger is-small is-outlined VosolButton" csid="{{ sarresid.customer.id }}" price="{{ sarresid.price }}" type="{{ sarresid.type }}" articlID="{{ sarresid.id }}">وصول شد</button>
|
||||||
|
{% else %}
|
||||||
|
<span class="icon has-text-success"><i class="fas fa-check-circle"></i> بله</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% empty %}
|
||||||
|
<tr>
|
||||||
|
<td colspan="8" class="has-text-grey p-5">هیچ سندی در این محدوده یافت نشد.</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field">
|
<div class="mt-4 is-flex is-justify-content-center">
|
||||||
<label class="label">سررسید بعدی</label>
|
<div class="buttons">
|
||||||
<div class="control">
|
{% if all_sarresid.has_previous %}
|
||||||
<input name="xxxxxaaaa" required class="input reset_input" id="NextSarResid" type="text">
|
<a href="?page={{ all_sarresid.previous_page_number }}&risk_level={{ current_filter|default:'all' }}" class="button is-light">صفحه قبل</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if all_sarresid.has_next %}
|
||||||
|
<a href="?page={{ all_sarresid.next_page_number }}&risk_level={{ current_filter|default:'all' }}" class="button is-light">صفحه بعد</a>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field">
|
</div>
|
||||||
<label class="label">توضیحات</label>
|
</div>
|
||||||
<div class="control">
|
</div>
|
||||||
<textarea name="desc_sar" class="input"></textarea>
|
|
||||||
</div>
|
<div class="modal">
|
||||||
|
<div class="modal-background"></div>
|
||||||
|
<div class="modal-card">
|
||||||
|
<header class="modal-card-head">
|
||||||
|
<p class="modal-card-title">Modal title</p>
|
||||||
|
<button class="delete" aria-label="close"></button>
|
||||||
|
</header>
|
||||||
|
<section class="modal-card-body">
|
||||||
|
<form>
|
||||||
|
<input type="text" class="is-hidden" name="articlID">
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
<footer class="modal-card-foot">
|
||||||
|
<button class="button is-success">Save changes</button>
|
||||||
|
<button class="button">Cancel</button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form class="modal nesiyeModal" method="post" action="{% url 'admin_panel:pardakhtNesiye' 9101010101 %}">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="modal-background"></div>
|
||||||
|
<div class="modal-card">
|
||||||
|
<header class="modal-card-head">
|
||||||
|
<p class="modal-card-title">نسیه</p>
|
||||||
|
<button class="delete" aria-label="close"></button>
|
||||||
|
</header>
|
||||||
|
<section class="modal-card-body">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">مانده از این نسیه</label>
|
||||||
|
<div class="control">
|
||||||
|
<input id="priceBedehi" disabled class="input reset_input" type="text" placeholder="واحد تومان">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input name="sr_articlID" id="sr_articlID" class="input reset_input is-hidden" type="text">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">مبلغ پرداختی فعلی</label>
|
||||||
|
<div class="control">
|
||||||
|
<input name="next_sarresidPrice" required class="input reset_input" type="text" placeholder="واحد تومان">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">نحوه پرداخت</label>
|
||||||
|
<div class="control">
|
||||||
|
<div class="select is-fullwidth">
|
||||||
|
<select required class="reset_input" name="nahvePardakht">
|
||||||
|
<option></option>
|
||||||
|
<option value="check">چک</option>
|
||||||
|
<option value="naghd">نقد</option>
|
||||||
|
<option value="kartbekart">کارت به کارت</option>
|
||||||
|
<option value="kartkhan">کارت خوان</option>
|
||||||
|
<option value="takhfif">تخفیف</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">سررسید بعدی</label>
|
||||||
|
<div class="control">
|
||||||
|
<input name="xxxxxaaaa" required class="input reset_input" id="NextSarResid" type="text">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">توضیحات</label>
|
||||||
|
<div class="control">
|
||||||
|
<textarea name="desc_sar" class="input"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input required type="text" class="is-hidden reset_input" name="next_sarresidDateTime" id="NextSarResidVal">
|
||||||
|
</section>
|
||||||
|
<footer class="modal-card-foot">
|
||||||
|
<input type="submit" class="button is-success" value="ثبت">
|
||||||
|
<span class="button cancellBTNSR">لغو</span>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
<input required type="text" class="is-hidden reset_input" name="next_sarresidDateTime" id="NextSarResidVal">
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</section>
|
|
||||||
<footer class="modal-card-foot">
|
|
||||||
<input type="submit" class="button is-success" value="ثبت">
|
|
||||||
<span class="button cancellBTNSR">لغو</span>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
{% csrf_token %}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block jsEnd %}
|
{% block jsEnd %}
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
var csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();
|
||||||
|
|
||||||
var csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();
|
|
||||||
|
|
||||||
|
|
||||||
$(".modal-background").click(function(){
|
$(".modal-background").click(function(){
|
||||||
$(".modal").removeClass("is-active");
|
$(".modal").removeClass("is-active");
|
||||||
$('.reset_input').val('');
|
$('.reset_input').val('');
|
||||||
})
|
})
|
||||||
|
|
||||||
$(".cancellBTNSR").click(function(){
|
$(".cancellBTNSR").click(function(){
|
||||||
$(".modal").removeClass("is-active");
|
$(".modal").removeClass("is-active");
|
||||||
$('.reset_input').val('');
|
$('.reset_input').val('');
|
||||||
@@ -244,9 +256,7 @@ $(document).ready(function() {
|
|||||||
format: 'YYYY/MM/DD',
|
format: 'YYYY/MM/DD',
|
||||||
autoClose: true,
|
autoClose: true,
|
||||||
calendar:{
|
calendar:{
|
||||||
persian: {
|
persian: { locale: 'fa' }
|
||||||
locale: 'fa'
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onSelect: (unix)=>{
|
onSelect: (unix)=>{
|
||||||
$("#NextSarResidVal").val(unix)
|
$("#NextSarResidVal").val(unix)
|
||||||
@@ -255,7 +265,6 @@ $(document).ready(function() {
|
|||||||
|
|
||||||
var csid = null
|
var csid = null
|
||||||
$(".VosolButton").click(function(){
|
$(".VosolButton").click(function(){
|
||||||
|
|
||||||
var articlID = $(this).attr("articlID")
|
var articlID = $(this).attr("articlID")
|
||||||
var type = $(this).attr("type")
|
var type = $(this).attr("type")
|
||||||
var price = $(this).attr("price")
|
var price = $(this).attr("price")
|
||||||
@@ -265,26 +274,20 @@ $(document).ready(function() {
|
|||||||
if( confirm('چک وصول شده؟') ){
|
if( confirm('چک وصول شده؟') ){
|
||||||
var prompt_desc = prompt('لطفا توضیحات را وارد کنید و تایید کنید')
|
var prompt_desc = prompt('لطفا توضیحات را وارد کنید و تایید کنید')
|
||||||
var url_check = '/panel/passCheck/'+articlID;
|
var url_check = '/panel/passCheck/'+articlID;
|
||||||
if( prompt_desc ){
|
if( prompt_desc ){
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: url_check,
|
url: url_check,
|
||||||
type: 'post',
|
type: 'post',
|
||||||
data: {
|
data: { message: prompt_desc },
|
||||||
message: prompt_desc
|
headers: {'X-CSRFToken': csrftoken },
|
||||||
},
|
success: function (data) {
|
||||||
headers: {'X-CSRFToken': csrftoken },
|
window.location.reload();
|
||||||
success: function (data) {
|
}
|
||||||
console.log("ok ok");
|
});
|
||||||
window.location.reload();
|
}
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var actionUrl = $(".nesiyeModal").attr('action')
|
var actionUrl = $(".nesiyeModal").attr('action')
|
||||||
//alert(actionUrl.replace("9101010101", csid ))
|
|
||||||
//$(".nesiyeModal").attr('action', actionUrl.replace("9101010101", csid ) )
|
|
||||||
$(".nesiyeModal").addClass("is-active");
|
$(".nesiyeModal").addClass("is-active");
|
||||||
$("#priceBedehi").val(price);
|
$("#priceBedehi").val(price);
|
||||||
$("#sr_articlID").val(articlID)
|
$("#sr_articlID").val(articlID)
|
||||||
@@ -294,10 +297,8 @@ $(document).ready(function() {
|
|||||||
$("form").submit(function(e){
|
$("form").submit(function(e){
|
||||||
var actionUrl = $(".nesiyeModal").attr('action')
|
var actionUrl = $(".nesiyeModal").attr('action')
|
||||||
$(".nesiyeModal").attr('action', actionUrl.replace("9101010101", csid ) )
|
$(".nesiyeModal").attr('action', actionUrl.replace("9101010101", csid ) )
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -12,6 +12,7 @@ urlpatterns = [
|
|||||||
path('bedehi/', views.bedehi, name='bedehi'),
|
path('bedehi/', views.bedehi, name='bedehi'),
|
||||||
path('report/', views.report, name='report'),
|
path('report/', views.report, name='report'),
|
||||||
path('report/inventory-movements/', views.inventory_movements_report, name='inventory_movements_report'),
|
path('report/inventory-movements/', views.inventory_movements_report, name='inventory_movements_report'),
|
||||||
|
path('report/dashboard/', views.financial_dashboard, name='financial_dashboard'),
|
||||||
path('hesabdari/', views.hesabdari, name='hesabdari'),
|
path('hesabdari/', views.hesabdari, name='hesabdari'),
|
||||||
path('hesabdari/sarresid_nashode/', views.hesabdari_residnashone, name='hesabdari_residnashone'),
|
path('hesabdari/sarresid_nashode/', views.hesabdari_residnashone, name='hesabdari_residnashone'),
|
||||||
path('hesabdari/check/pass/', views.hesabdari_checkPass, name='hesabdari_check_pass'),
|
path('hesabdari/check/pass/', views.hesabdari_checkPass, name='hesabdari_check_pass'),
|
||||||
|
|||||||
+196
-10
@@ -266,6 +266,159 @@ def inventory_movements_report(request):
|
|||||||
|
|
||||||
return render(request, 'admin_panel/report_inventory_movements.html', context)
|
return render(request, 'admin_panel/report_inventory_movements.html', context)
|
||||||
|
|
||||||
|
@login_required(login_url='/panel/p_login/')
|
||||||
|
@permission_required('admin_panel.ViewReportMali', raise_exception=True)
|
||||||
|
def financial_dashboard(request):
|
||||||
|
"""
|
||||||
|
داشبورد هوش مالی و مدیریت نقدینگی سطح Enterprise (BI Dashboard)
|
||||||
|
همراه با روند ۱۲ ماهه جمع کل بدهی مشتریان (تفاضل debit و credit).
|
||||||
|
"""
|
||||||
|
# دریافت تایماستمپهای ارسالی از تقویم شمسی فرانتاند
|
||||||
|
az_tarikh = request.GET.get('azTarikh', '')
|
||||||
|
ta_tarikh = request.GET.get('taTarikh', '')
|
||||||
|
|
||||||
|
az_dt = None
|
||||||
|
ta_dt = None
|
||||||
|
|
||||||
|
if az_tarikh:
|
||||||
|
try:
|
||||||
|
az_dt = datetime.datetime.fromtimestamp(int(az_tarikh) / 1000.0)
|
||||||
|
az_dt = az_dt.replace(hour=0, minute=0, second=0)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
if ta_tarikh:
|
||||||
|
try:
|
||||||
|
ta_dt = datetime.datetime.fromtimestamp(int(ta_tarikh) / 1000.0)
|
||||||
|
ta_dt = ta_dt.replace(hour=23, minute=59, second=59)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# نقطه اتکا (لنگر) محاسبات: زمان انتخابی کاربر یا زمان حال
|
||||||
|
anchor_dt = ta_dt if ta_dt else datetime.datetime.now()
|
||||||
|
|
||||||
|
# =========================================================================
|
||||||
|
# محاسبه روند ۱۲ ماهه غلتان (TTM) - نسخه اصلاحشده و امن
|
||||||
|
# =========================================================================
|
||||||
|
jalali_months = ['فروردین', 'اردیبهشت', 'خرداد', 'تیر', 'مرداد', 'شهریور', 'مهر', 'آبان', 'آذر', 'دی', 'بهمن', 'اسفند']
|
||||||
|
trend_labels = []
|
||||||
|
trend_data = []
|
||||||
|
|
||||||
|
# روش استاندارد و بدون باگ برای تبدیل تاریخ میلادی به شمسی در persiantools
|
||||||
|
# با این کار مطمئن میشویم مقادیر حتماً عدد صحیح و مجاز هستند
|
||||||
|
anchor_jd = JalaliDate(anchor_dt.date())
|
||||||
|
curr_y = int(anchor_jd.year)
|
||||||
|
curr_m = int(anchor_jd.month)
|
||||||
|
|
||||||
|
# حلقه استخراج داده برای ۱۲ ماه گذشته
|
||||||
|
for _ in range(12):
|
||||||
|
if curr_m < 1:
|
||||||
|
curr_m = 12
|
||||||
|
curr_y -= 1
|
||||||
|
|
||||||
|
label = f"{jalali_months[curr_m - 1]} {curr_y}"
|
||||||
|
trend_labels.insert(0, label)
|
||||||
|
|
||||||
|
# ❗️ اصلاح اساسی: ترتیب آرگومانها در این تابع باید (month, year) باشد
|
||||||
|
last_day = JalaliDate.days_in_month(curr_m, curr_y)
|
||||||
|
|
||||||
|
# اما ترتیب سازنده کلاس اصلی همچنان (year, month, day) است
|
||||||
|
end_of_month_jd = JalaliDate(curr_y, curr_m, last_day)
|
||||||
|
end_of_month_gd = end_of_month_jd.to_gregorian()
|
||||||
|
|
||||||
|
# ساخت رشته زمان برای پایان آن ماه (تا ساعت 23:59:59)
|
||||||
|
end_dt_str = end_of_month_gd.strftime('%Y-%m-%d 23:59:59')
|
||||||
|
|
||||||
|
# محاسبه مانده حساب کل تا پایان این ماه
|
||||||
|
totals = Article.objects.filter(createdDT__lte=end_dt_str).aggregate(
|
||||||
|
t_deb=Sum('debit'),
|
||||||
|
t_cred=Sum('credit')
|
||||||
|
)
|
||||||
|
t_deb = totals['t_deb'] or 0
|
||||||
|
t_cred = totals['t_cred'] or 0
|
||||||
|
month_total_debt = t_deb - t_cred
|
||||||
|
|
||||||
|
trend_data.insert(0, month_total_debt)
|
||||||
|
|
||||||
|
# شیفت به ماه قبل برای دور بعدی حلقه
|
||||||
|
curr_m -= 1
|
||||||
|
|
||||||
|
# =========================================================================
|
||||||
|
# قرنطینه دادههای سایر بخشهای داشبورد بر اساس سال مالی ۱۴۰۰
|
||||||
|
# =========================================================================
|
||||||
|
|
||||||
|
sarresid_qs = Sarresid.objects.filter(factor__productinstore__year=1400).distinct()
|
||||||
|
factor_qs = Factor.objects.filter(productinstore__year=1400, is_active=True).distinct()
|
||||||
|
article_qs = Article.objects.filter(factor__productinstore__year=1400).distinct()
|
||||||
|
store_qs = ProductInStore.objects.filter(year=1400)
|
||||||
|
|
||||||
|
if az_dt:
|
||||||
|
sarresid_qs = sarresid_qs.filter(createdDT__gte=az_dt)
|
||||||
|
factor_qs = factor_qs.filter(create_date__gte=az_dt)
|
||||||
|
article_qs = article_qs.filter(createdDT__gte=az_dt)
|
||||||
|
store_qs = store_qs.filter(createdDT__gte=az_dt)
|
||||||
|
|
||||||
|
if ta_dt:
|
||||||
|
sarresid_qs = sarresid_qs.filter(createdDT__lte=ta_dt)
|
||||||
|
factor_qs = factor_qs.filter(create_date__lte=ta_dt)
|
||||||
|
article_qs = article_qs.filter(createdDT__lte=ta_dt)
|
||||||
|
store_qs = store_qs.filter(createdDT__lte=ta_dt)
|
||||||
|
|
||||||
|
# ۱. محاسبات کارتهای شاخص عملکرد (KPI Cards)
|
||||||
|
total_unpaid_debts = sarresid_qs.filter(payStatus="false").aggregate(Sum('price'))['price__sum'] or 0
|
||||||
|
total_sales_period = factor_qs.aggregate(Sum('amount'))['amount__sum'] or 0
|
||||||
|
overdue_checks_count = sarresid_qs.filter(payStatus="false", type="check", sarresidDT__lte=anchor_dt).count()
|
||||||
|
|
||||||
|
# ۲. نمودار اول: تحلیل پویای سن مطالبات
|
||||||
|
debt_future = sarresid_qs.filter(payStatus="false", sarresidDT__gt=anchor_dt).aggregate(Sum('price'))['price__sum'] or 0
|
||||||
|
dt_30_days_ago = anchor_dt - datetime.timedelta(days=30)
|
||||||
|
debt_0_30 = sarresid_qs.filter(payStatus="false", sarresidDT__lte=anchor_dt, sarresidDT__gte=dt_30_days_ago).aggregate(Sum('price'))['price__sum'] or 0
|
||||||
|
dt_90_days_ago = anchor_dt - datetime.timedelta(days=90)
|
||||||
|
debt_30_90 = sarresid_qs.filter(payStatus="false", sarresidDT__lt=dt_30_days_ago, sarresidDT__gte=dt_90_days_ago).aggregate(Sum('price'))['price__sum'] or 0
|
||||||
|
debt_above_90 = sarresid_qs.filter(payStatus="false", sarresidDT__lt=dt_90_days_ago).aggregate(Sum('price'))['price__sum'] or 0
|
||||||
|
|
||||||
|
# ۳. نمودار دوم: ماتریس کیفیت ورودی پول
|
||||||
|
payment_articles = article_qs.filter(credit__gt=0).values('nahvePardakht').annotate(total_amount=Sum('credit')).order_by('-total_amount')
|
||||||
|
pay_labels = []
|
||||||
|
pay_data = []
|
||||||
|
for item in payment_articles:
|
||||||
|
label = item['nahvePardakht'] or 'سایر/نامشخص'
|
||||||
|
pay_labels.append(label)
|
||||||
|
pay_data.append(item['total_amount'])
|
||||||
|
|
||||||
|
# ۴. نمودار سوم: مانیتورینگ نشت انبار
|
||||||
|
store_leaks = store_qs.filter(
|
||||||
|
movement_type__in=[ProductInStore.MovementType.WARRANTY, ProductInStore.MovementType.PROMOTION]
|
||||||
|
).values('movement_type').annotate(total_qty=Sum('s_out'))
|
||||||
|
|
||||||
|
leak_data = {'WARRANTY': 0, 'PROMOTION': 0}
|
||||||
|
for leak in store_leaks:
|
||||||
|
leak_data[leak['movement_type']] = leak['total_qty']
|
||||||
|
|
||||||
|
# ارسال ساختار داده به تمپلیت
|
||||||
|
context = {
|
||||||
|
'kpi_total_unpaid': total_unpaid_debts,
|
||||||
|
'kpi_total_sales': total_sales_period,
|
||||||
|
'kpi_overdue_checks': overdue_checks_count,
|
||||||
|
|
||||||
|
'trend_labels': trend_labels, # نام ماهها برای محور X
|
||||||
|
'trend_data': trend_data, # جمع کل بدهیها برای محور Y
|
||||||
|
|
||||||
|
'aging_data': [debt_future, debt_0_30, debt_30_90, debt_above_90],
|
||||||
|
'pay_labels': pay_labels,
|
||||||
|
'pay_data': pay_data,
|
||||||
|
'warranty_qty': leak_data['WARRANTY'],
|
||||||
|
'promotion_qty': leak_data['PROMOTION'],
|
||||||
|
|
||||||
|
'filters': {
|
||||||
|
'azTarikh': az_tarikh,
|
||||||
|
'taTarikh': ta_tarikh,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return render(request, 'admin_panel/financial_dashboard.html', context)
|
||||||
|
|
||||||
|
|
||||||
@login_required(login_url='/panel/p_login/')
|
@login_required(login_url='/panel/p_login/')
|
||||||
@permission_required('admin_panel.ViewReportMali', raise_exception=True)
|
@permission_required('admin_panel.ViewReportMali', raise_exception=True)
|
||||||
def bedehi(request):
|
def bedehi(request):
|
||||||
@@ -288,25 +441,58 @@ def bedehi(request):
|
|||||||
@login_required(login_url='/panel/p_login/')
|
@login_required(login_url='/panel/p_login/')
|
||||||
@permission_required('admin_panel.ViewReportMali', raise_exception=True)
|
@permission_required('admin_panel.ViewReportMali', raise_exception=True)
|
||||||
def hesabdari(request):
|
def hesabdari(request):
|
||||||
|
|
||||||
page_name = 'sarresidShodeha'
|
page_name = 'sarresidShodeha'
|
||||||
|
anchor_dt = datetime.datetime.now()
|
||||||
|
|
||||||
dateNow = datetime.datetime.today().strftime('%Y-%m-%d 23:59:59')
|
# تعریف محدودههای زمانی ماتریس ریسک (سن مطالبات)
|
||||||
all_sarresid = Sarresid.objects.filter(payStatus="false", sarresidDT__lte=dateNow).order_by('-sarresidDT')
|
dt_30_days_ago = anchor_dt - datetime.timedelta(days=30)
|
||||||
|
dt_90_days_ago = anchor_dt - datetime.timedelta(days=90)
|
||||||
|
|
||||||
sum_sarresid = all_sarresid.aggregate(Sum('price'))['price__sum']
|
# کوئری پایه: تمام سررسیدهای پرداختنشده که از موعد آنها گذشته است
|
||||||
|
base_qs = Sarresid.objects.filter(payStatus="false", sarresidDT__lte=anchor_dt)
|
||||||
|
|
||||||
page_number = request.GET.get('page', None)
|
# محاسبه پرسرعت مجموع مبالغ در هر دسته (توسط موتور دیتابیس)
|
||||||
paginator = Paginator(all_sarresid, per_page=50)
|
matrix_totals = base_qs.aggregate(
|
||||||
|
minor_delay=Sum('price', filter=Q(sarresidDT__gte=dt_30_days_ago)),
|
||||||
|
high_risk=Sum('price', filter=Q(sarresidDT__lt=dt_30_days_ago, sarresidDT__gte=dt_90_days_ago)),
|
||||||
|
critical=Sum('price', filter=Q(sarresidDT__lt=dt_90_days_ago)),
|
||||||
|
total_sum=Sum('price')
|
||||||
|
)
|
||||||
|
|
||||||
|
# دریافت فیلتر انتخابی کاربر از URL
|
||||||
|
risk_filter = request.GET.get('risk_level', 'all')
|
||||||
|
|
||||||
|
# اعمال فیلتر روی لیست نمایش داده شده
|
||||||
|
if risk_filter == 'minor':
|
||||||
|
filtered_qs = base_qs.filter(sarresidDT__gte=dt_30_days_ago)
|
||||||
|
elif risk_filter == 'high':
|
||||||
|
filtered_qs = base_qs.filter(sarresidDT__lt=dt_30_days_ago, sarresidDT__gte=dt_90_days_ago)
|
||||||
|
elif risk_filter == 'critical':
|
||||||
|
filtered_qs = base_qs.filter(sarresidDT__lt=dt_90_days_ago)
|
||||||
|
else:
|
||||||
|
filtered_qs = base_qs
|
||||||
|
|
||||||
|
# مرتبسازی: قدیمیترینها (خطرناکترینها) در صدر جدول قرار میگیرند
|
||||||
|
filtered_qs = filtered_qs.order_by('sarresidDT')
|
||||||
|
|
||||||
|
page_number = request.GET.get('page', 1)
|
||||||
|
paginator = Paginator(filtered_qs, per_page=50)
|
||||||
all_sarresid_pgi = paginator.get_page(page_number)
|
all_sarresid_pgi = paginator.get_page(page_number)
|
||||||
|
|
||||||
#paginator = Paginator(all_sarresid, per_page=200)
|
data = {
|
||||||
|
'page_name': page_name,
|
||||||
data = {'sum_sarresid': sum_sarresid,'all_sarresid': all_sarresid_pgi, "dateNow":dateNow, "page_name": page_name}
|
'dateNow': anchor_dt.strftime('%Y-%m-%d %H:%M:%S'),
|
||||||
|
'all_sarresid': all_sarresid_pgi,
|
||||||
|
'current_filter': risk_filter,
|
||||||
|
# متغیرهای ماتریس ریسک
|
||||||
|
'sum_sarresid': matrix_totals['total_sum'] or 0,
|
||||||
|
'minor_sum': matrix_totals['minor_delay'] or 0,
|
||||||
|
'high_sum': matrix_totals['high_risk'] or 0,
|
||||||
|
'critical_sum': matrix_totals['critical'] or 0,
|
||||||
|
}
|
||||||
|
|
||||||
return render(request, 'admin_panel/hesabdari.html', data)
|
return render(request, 'admin_panel/hesabdari.html', data)
|
||||||
|
|
||||||
|
|
||||||
@login_required(login_url='/panel/p_login/')
|
@login_required(login_url='/panel/p_login/')
|
||||||
@permission_required('admin_panel.ViewReportMali', raise_exception=True)
|
@permission_required('admin_panel.ViewReportMali', raise_exception=True)
|
||||||
def hesabdari_checkPass(request):
|
def hesabdari_checkPass(request):
|
||||||
|
|||||||
Reference in new issue
Block a user