adding cashflow
This commit is contained in:
parent
7a5e1a7044
commit
0a02320029
Binary file not shown.
Binary file not shown.
@ -181,11 +181,11 @@
|
||||
|
||||
<!-- Reports Group -->
|
||||
<li class="sidebar-group-header mt-2">
|
||||
<a href="#reportsSubmenu" data-bs-toggle="collapse" aria-expanded="{% if url_name == 'reports' or url_name == 'customer_statement' or url_name == 'supplier_statement' %}true{% else %}false{% endif %}" class="dropdown-toggle-custom">
|
||||
<a href="#reportsSubmenu" data-bs-toggle="collapse" aria-expanded="{% if url_name == 'reports' or url_name == 'customer_statement' or url_name == 'supplier_statement' or url_name == 'cashflow_report' %}true{% else %}false{% endif %}" class="dropdown-toggle-custom">
|
||||
<span>{% trans "Reports" %}</span>
|
||||
<i class="bi bi-chevron-down chevron"></i>
|
||||
</a>
|
||||
<ul class="collapse list-unstyled sub-menu {% if url_name == 'reports' or url_name == 'customer_statement' or url_name == 'supplier_statement' %}show{% endif %}" id="reportsSubmenu">
|
||||
<ul class="collapse list-unstyled sub-menu {% if url_name == 'reports' or url_name == 'customer_statement' or url_name == 'supplier_statement' or url_name == 'cashflow_report' %}show{% endif %}" id="reportsSubmenu">
|
||||
<li>
|
||||
<a href="{% url 'reports' %}" class="{% if url_name == 'reports' %}active{% endif %}">
|
||||
<i class="bi bi-graph-up-arrow"></i> {% trans "Overview Reports" %}
|
||||
@ -201,6 +201,11 @@
|
||||
<i class="bi bi-truck-flatbed"></i> {% trans "Supplier Statement" %}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% url 'cashflow_report' %}" class="{% if url_name == 'cashflow_report' %}active{% endif %}">
|
||||
<i class="bi bi-cash-coin"></i> {% trans "Cashflow Report" %}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
||||
186
core/templates/core/cashflow_report.html
Normal file
186
core/templates/core/cashflow_report.html
Normal file
@ -0,0 +1,186 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans "Cashflow Report" %} | {{ settings.business_name }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid py-4">
|
||||
<!-- Print Header (Visible only when printing) -->
|
||||
<div class="d-none d-print-block mb-4">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-6">
|
||||
{% if settings.logo %}
|
||||
<img src="{{ settings.logo.url }}" alt="{{ settings.business_name }}" style="max-height: 80px;">
|
||||
{% else %}
|
||||
<h2 class="fw-bold mb-0">{{ settings.business_name }}</h2>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-6 text-end">
|
||||
<h1 class="h3 fw-bold mb-0">{% trans "Cashflow Report" %}</h1>
|
||||
<p class="text-muted mb-0">{{ start_date }} - {{ end_date }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4 align-items-center d-print-none">
|
||||
<div class="col-md-6">
|
||||
<h1 class="h3 fw-bold mb-0">{% trans "Cashflow Report" %}</h1>
|
||||
<p class="text-muted">{% trans "Detailed summary of all cash inflows and outflows." %}</p>
|
||||
</div>
|
||||
<div class="col-md-6 text-md-end">
|
||||
<button onclick="window.print()" class="btn btn-outline-primary">
|
||||
<i class="bi bi-printer"></i> {% trans "Print Report" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Filters -->
|
||||
<div class="card border-0 shadow-sm mb-4 d-print-none">
|
||||
<div class="card-body">
|
||||
<form method="get" class="row g-3">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small fw-bold">{% trans "Start Date" %}</label>
|
||||
<input type="date" name="start_date" class="form-control" value="{{ start_date }}">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small fw-bold">{% trans "End Date" %}</label>
|
||||
<input type="date" name="end_date" class="form-control" value="{{ end_date }}">
|
||||
</div>
|
||||
<div class="col-md-4 d-flex align-items-end">
|
||||
<button type="submit" class="btn btn-primary w-100">
|
||||
<i class="bi bi-filter"></i> {% trans "Filter" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Compact Summary for Printing -->
|
||||
<div class="d-none d-print-block mb-4">
|
||||
<div class="row text-center border py-3 rounded bg-light">
|
||||
<div class="col-4 border-end">
|
||||
<small class="text-uppercase text-muted d-block">{% trans "Total Inflow" %}</small>
|
||||
<h4 class="fw-bold mb-0 text-success">{{ settings.currency_symbol }}{{ total_inflow|floatformat:settings.decimal_places }}</h4>
|
||||
</div>
|
||||
<div class="col-4 border-end">
|
||||
<small class="text-uppercase text-muted d-block">{% trans "Total Outflow" %}</small>
|
||||
<h4 class="fw-bold mb-0 text-danger">{{ settings.currency_symbol }}{{ total_outflow|floatformat:settings.decimal_places }}</h4>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<small class="text-uppercase text-muted d-block">{% trans "Net Cashflow" %}</small>
|
||||
<h4 class="fw-bold mb-0 {% if net_cashflow >= 0 %}text-primary{% else %}text-warning{% endif %}">
|
||||
{{ settings.currency_symbol }}{{ net_cashflow|floatformat:settings.decimal_places }}
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Summary Cards (Screen Only) -->
|
||||
<div class="row g-4 mb-4 d-print-none">
|
||||
<div class="col-md-4">
|
||||
<div class="card border-0 shadow-sm bg-success text-white">
|
||||
<div class="card-body p-4">
|
||||
<h6 class="text-uppercase small fw-bold opacity-75">{% trans "Total Inflow" %}</h6>
|
||||
<h2 class="fw-bold mb-0">{{ settings.currency_symbol }}{{ total_inflow|floatformat:settings.decimal_places }}</h2>
|
||||
<i class="bi bi-arrow-up-right position-absolute top-0 end-0 p-3 opacity-25" style="font-size: 2rem;"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card border-0 shadow-sm bg-danger text-white">
|
||||
<div class="card-body p-4">
|
||||
<h6 class="text-uppercase small fw-bold opacity-75">{% trans "Total Outflow" %}</h6>
|
||||
<h2 class="fw-bold mb-0">{{ settings.currency_symbol }}{{ total_outflow|floatformat:settings.decimal_places }}</h2>
|
||||
<i class="bi bi-arrow-down-left position-absolute top-0 end-0 p-3 opacity-25" style="font-size: 2rem;"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card border-0 shadow-sm {% if net_cashflow >= 0 %}bg-primary{% else %}bg-warning{% endif %} text-white">
|
||||
<div class="card-body p-4">
|
||||
<h6 class="text-uppercase small fw-bold opacity-75">{% trans "Net Cashflow" %}</h6>
|
||||
<h2 class="fw-bold mb-0">{{ settings.currency_symbol }}{{ net_cashflow|floatformat:settings.decimal_places }}</h2>
|
||||
<i class="bi bi-cash-stack position-absolute top-0 end-0 p-3 opacity-25" style="font-size: 2rem;"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Detailed Transactions -->
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-header bg-white py-3 d-print-none">
|
||||
<h5 class="card-title mb-0 fw-bold">{% trans "Transaction History" %}</h5>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th class="ps-4">{% trans "Date" %}</th>
|
||||
<th>{% trans "Type" %}</th>
|
||||
<th>{% trans "Reference" %}</th>
|
||||
<th>{% trans "Contact" %}</th>
|
||||
<th>{% trans "Method" %}</th>
|
||||
<th class="text-end">{% trans "Inflow" %}</th>
|
||||
<th class="text-end pe-4">{% trans "Outflow" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in transactions %}
|
||||
<tr>
|
||||
<td class="ps-4">{{ item.date|date:"d M Y" }}</td>
|
||||
<td>
|
||||
<span class="badge {% if item.inflow > 0 %}bg-success-soft text-success{% else %}bg-danger-soft text-danger{% endif %} rounded-pill">
|
||||
{{ item.type }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="fw-bold text-dark">{{ item.reference }}</td>
|
||||
<td>{{ item.contact }}</td>
|
||||
<td><small class="text-muted">{{ item.method }}</small></td>
|
||||
<td class="text-end text-success fw-bold">
|
||||
{% if item.inflow > 0 %}+{{ item.inflow|floatformat:settings.decimal_places }}{% else %}-{% endif %}
|
||||
</td>
|
||||
<td class="text-end text-danger fw-bold pe-4">
|
||||
{% if item.outflow > 0 %}-{{ item.outflow|floatformat:settings.decimal_places }}{% else %}-{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="7" class="text-center py-5 text-muted">
|
||||
<i class="bi bi-inbox fs-1 d-block mb-3"></i>
|
||||
{% trans "No transactions found for this period." %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.bg-success-soft { background-color: rgba(25, 135, 84, 0.1); }
|
||||
.bg-danger-soft { background-color: rgba(220, 53, 69, 0.1); }
|
||||
|
||||
@media print {
|
||||
.d-print-none { display: none !important; }
|
||||
.d-print-block { display: block !important; }
|
||||
.card { border: none !important; box-shadow: none !important; }
|
||||
body { background: white !important; font-size: 12px; }
|
||||
.container-fluid { padding: 0 !important; }
|
||||
.table { border: 1px solid #dee2e6 !important; }
|
||||
.table thead th { background-color: #f8f9fa !important; border-bottom: 2px solid #dee2e6 !important; -webkit-print-color-adjust: exact; }
|
||||
.badge { border: 1px solid #ccc !important; color: black !important; background: none !important; }
|
||||
.text-success { color: #198754 !important; -webkit-print-color-adjust: exact; }
|
||||
.text-danger { color: #dc3545 !important; -webkit-print-color-adjust: exact; }
|
||||
hr { border-top: 1px solid #000 !important; opacity: 1 !important; }
|
||||
.bg-light { background-color: #f8f9fa !important; -webkit-print-color-adjust: exact; }
|
||||
|
||||
/* Ensure summary doesn't take too much vertical space */
|
||||
.h4 { font-size: 1.25rem !important; }
|
||||
.row.text-center.border { border-width: 2px !important; }
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
@ -11,6 +11,7 @@ urlpatterns = [
|
||||
path('reports/', views.reports, name='reports'),
|
||||
path('reports/customer-statement/', views.customer_statement, name='customer_statement'),
|
||||
path('reports/supplier-statement/', views.supplier_statement, name='supplier_statement'),
|
||||
path('reports/cashflow/', views.cashflow_report, name='cashflow_report'),
|
||||
path('settings/', views.settings_view, name='settings'),
|
||||
path('profile/', views.profile_view, name='profile'),
|
||||
path('users/', views.user_management, name='user_management'),
|
||||
|
||||
@ -2119,3 +2119,93 @@ def supplier_statement(request):
|
||||
'settings': settings
|
||||
}
|
||||
return render(request, 'core/supplier_statement.html', context)
|
||||
|
||||
@login_required
|
||||
def cashflow_report(request):
|
||||
"""
|
||||
Generate a Cashflow report summarizing income and expenses.
|
||||
"""
|
||||
start_date = request.GET.get('start_date')
|
||||
end_date = request.GET.get('end_date')
|
||||
|
||||
# Defaults to current month if no dates provided
|
||||
if not start_date:
|
||||
start_date = timezone.now().date().replace(day=1).strftime('%Y-%m-%d')
|
||||
if not end_date:
|
||||
end_date = timezone.now().date().strftime('%Y-%m-%d')
|
||||
|
||||
# Fetching Inflows (Sale Payments)
|
||||
sale_payments = SalePayment.objects.all().select_related('sale', 'sale__customer')
|
||||
if start_date:
|
||||
sale_payments = sale_payments.filter(payment_date__gte=start_date)
|
||||
if end_date:
|
||||
sale_payments = sale_payments.filter(payment_date__lte=end_date)
|
||||
|
||||
# Fetching Outflows (Purchase Payments)
|
||||
purchase_payments = PurchasePayment.objects.all().select_related('purchase', 'purchase__supplier')
|
||||
if start_date:
|
||||
purchase_payments = purchase_payments.filter(payment_date__gte=start_date)
|
||||
if end_date:
|
||||
purchase_payments = purchase_payments.filter(payment_date__lte=end_date)
|
||||
|
||||
# Fetching Outflows (Expenses)
|
||||
expenses = Expense.objects.all().select_related('category', 'payment_method')
|
||||
if start_date:
|
||||
expenses = expenses.filter(date__gte=start_date)
|
||||
if end_date:
|
||||
expenses = expenses.filter(date__lte=end_date)
|
||||
|
||||
# Prepare detailed transactions list
|
||||
transactions = []
|
||||
|
||||
for pay in sale_payments:
|
||||
transactions.append({
|
||||
'date': pay.payment_date,
|
||||
'type': _('Sale Payment'),
|
||||
'reference': pay.sale.invoice_number or f"Sale #{pay.sale.id}",
|
||||
'contact': pay.sale.customer.name if pay.sale.customer else _('Guest'),
|
||||
'inflow': float(pay.amount),
|
||||
'outflow': 0,
|
||||
'method': pay.payment_method_name
|
||||
})
|
||||
|
||||
for pay in purchase_payments:
|
||||
transactions.append({
|
||||
'date': pay.payment_date,
|
||||
'type': _('Purchase Payment'),
|
||||
'reference': pay.purchase.invoice_number or f"Purchase #{pay.purchase.id}",
|
||||
'contact': pay.purchase.supplier.name if pay.purchase.supplier else 'N/A',
|
||||
'inflow': 0,
|
||||
'outflow': float(pay.amount),
|
||||
'method': pay.payment_method_name
|
||||
})
|
||||
|
||||
for exp in expenses:
|
||||
transactions.append({
|
||||
'date': exp.date,
|
||||
'type': _('Expense'),
|
||||
'reference': exp.category.name_en,
|
||||
'contact': _('Various'),
|
||||
'inflow': 0,
|
||||
'outflow': float(exp.amount),
|
||||
'method': exp.payment_method.name_en if exp.payment_method else _('N/A')
|
||||
})
|
||||
|
||||
transactions.sort(key=lambda x: x['date'], reverse=True)
|
||||
|
||||
total_inflow = sum(item['inflow'] for item in transactions)
|
||||
total_outflow = sum(item['outflow'] for item in transactions)
|
||||
net_cashflow = total_inflow - total_outflow
|
||||
|
||||
settings = SystemSetting.objects.first()
|
||||
|
||||
context = {
|
||||
'transactions': transactions,
|
||||
'total_inflow': total_inflow,
|
||||
'total_outflow': total_outflow,
|
||||
'net_cashflow': net_cashflow,
|
||||
'start_date': start_date,
|
||||
'end_date': end_date,
|
||||
'settings': settings
|
||||
}
|
||||
return render(request, 'core/cashflow_report.html', context)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user