final final

This commit is contained in:
Flatlogic Bot 2026-02-10 17:53:28 +00:00
parent 1fe85ff3ce
commit cfa7d80ecc
10 changed files with 374 additions and 30 deletions

View File

@ -0,0 +1,121 @@
{% extends 'base.html' %}
{% load i18n %}
{% block content %}
<div class="container-fluid py-4">
<!-- Header -->
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h2 class="mb-0">{% trans "VAT Report" %} / تقرير ضريبة القيمة المضافة</h2>
<p class="text-muted">{% trans "Tax Declaration Summary" %} / ملخص الإقرار الضريبي</p>
</div>
<div>
<button onclick="window.print()" class="btn btn-outline-secondary">
<i class="bi bi-printer"></i> {% trans "Print" %} / طباعة
</button>
</div>
</div>
<!-- Filter Form -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-body">
<form method="get" class="row g-3 align-items-end">
<div class="col-md-4">
<label class="form-label">{% 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">{% trans "End Date" %} / تاريخ الانتهاء</label>
<input type="date" name="end_date" class="form-control" value="{{ end_date }}">
</div>
<div class="col-md-4">
<button type="submit" class="btn btn-primary w-100">
<i class="bi bi-filter"></i> {% trans "Generate Report" %} / إنشاء التقرير
</button>
</div>
</form>
</div>
</div>
<!-- Report Content -->
<div class="row">
<!-- Sales / Output Tax -->
<div class="col-md-6 mb-4">
<div class="card border-0 shadow-sm h-100">
<div class="card-header bg-light py-3">
<h5 class="card-title mb-0 text-primary">
1. {% trans "Sales (Output VAT)" %} / المبيعات (ضريبة المخرجات)
</h5>
</div>
<div class="card-body p-0">
<table class="table table-striped mb-0">
<tbody>
<tr>
<td class="py-3">{% trans "Total Sales (Excl. VAT)" %} <br> <small class="text-muted">إجمالي المبيعات (غير شامل الضريبة)</small></td>
<td class="text-end py-3 fw-bold">{{ total_sales_subtotal|floatformat:3 }} {{ global_settings.currency_symbol }}</td>
</tr>
<tr>
<td class="py-3">{% trans "Total VAT Collected" %} <br> <small class="text-muted">إجمالي الضريبة المحصلة</small></td>
<td class="text-end py-3 fw-bold text-danger">{{ total_output_vat|floatformat:3 }} {{ global_settings.currency_symbol }}</td>
</tr>
<tr class="table-primary">
<td class="py-3">{% trans "Total Gross Sales" %} <br> <small class="text-muted">إجمالي المبيعات (شامل الضريبة)</small></td>
<td class="text-end py-3 fw-bold">{{ total_sales_gross|floatformat:3 }} {{ global_settings.currency_symbol }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- Purchases / Input Tax -->
<div class="col-md-6 mb-4">
<div class="card border-0 shadow-sm h-100">
<div class="card-header bg-light py-3">
<h5 class="card-title mb-0 text-success">
2. {% trans "Purchases (Input VAT)" %} / المشتريات (ضريبة المدخلات)
</h5>
</div>
<div class="card-body p-0">
<table class="table table-striped mb-0">
<tbody>
<tr>
<td class="py-3">{% trans "Total Purchases (Excl. VAT)" %} <br> <small class="text-muted">إجمالي المشتريات (غير شامل الضريبة)</small></td>
<td class="text-end py-3 fw-bold">{{ total_purchases_subtotal|floatformat:3 }} {{ global_settings.currency_symbol }}</td>
</tr>
<tr>
<td class="py-3">{% trans "Total VAT Paid (Recoverable)" %} <br> <small class="text-muted">إجمالي الضريبة المدفوعة (القابلة للاسترداد)</small></td>
<td class="text-end py-3 fw-bold text-success">{{ total_input_vat|floatformat:3 }} {{ global_settings.currency_symbol }}</td>
</tr>
<tr class="table-success">
<td class="py-3">{% trans "Total Gross Purchases" %} <br> <small class="text-muted">إجمالي المشتريات (شامل الضريبة)</small></td>
<td class="text-end py-3 fw-bold">{{ total_purchases_gross|floatformat:3 }} {{ global_settings.currency_symbol }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- Net VAT Position -->
<div class="col-12">
<div class="card border-0 shadow-sm bg-primary text-white">
<div class="card-body text-center py-4">
<h4 class="mb-2">{% trans "Net VAT Payable / (Refundable)" %}</h4>
<h5 class="mb-3">صافي الضريبة المستحقة الدفع / (المستردة)</h5>
<h1 class="display-4 fw-bold mb-0">
{{ net_vat|floatformat:3 }} {{ global_settings.currency_symbol }}
</h1>
<p class="mt-2 mb-0 opacity-75">
{% if net_vat > 0 %}
{% trans "Amount to be paid to Tax Authority" %} / المبلغ المستحق للدفع للهيئة الضريبية
{% else %}
{% trans "Amount to be refunded from Tax Authority" %} / المبلغ المستحق للاسترداد من الهيئة الضريبية
{% endif %}
</p>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -9,6 +9,7 @@ urlpatterns = [
path('journal-entries/', views.journal_entries, name='journal_entries'), path('journal-entries/', views.journal_entries, name='journal_entries'),
path('journal-entries/manual/', views.manual_journal_entry, name='manual_journal_entry'), path('journal-entries/manual/', views.manual_journal_entry, name='manual_journal_entry'),
path('ledger/<int:account_id>/', views.account_ledger, name='account_ledger'), path('ledger/<int:account_id>/', views.account_ledger, name='account_ledger'),
path('reports/vat/', views.vat_report, name='vat_report'),
path('trial-balance/', views.trial_balance, name='trial_balance'), path('trial-balance/', views.trial_balance, name='trial_balance'),
path('balance-sheet/', views.balance_sheet, name='balance_sheet'), path('balance-sheet/', views.balance_sheet, name='balance_sheet'),
path('profit-loss/', views.profit_loss, name='profit_loss'), path('profit-loss/', views.profit_loss, name='profit_loss'),

View File

@ -4,13 +4,71 @@ from django.contrib.auth.decorators import login_required
from django.contrib import messages from django.contrib import messages
from .models import Account, JournalEntry, JournalItem from .models import Account, JournalEntry, JournalItem
from .forms import AccountForm, JournalEntryForm from .forms import AccountForm, JournalEntryForm
from django.db.models import Sum, Q, Value, DecimalField from core.models import Sale, Purchase, Product
from django.db.models import Sum, Q, Value, DecimalField, F
from django.db.models.functions import Coalesce from django.db.models.functions import Coalesce
from django.utils import timezone from django.utils import timezone
from datetime import datetime from datetime import datetime, date
from django.db import transaction from django.db import transaction
import json import json
@login_required
def vat_report(request):
start_date = request.GET.get('start_date')
end_date = request.GET.get('end_date')
if not start_date:
start_date = timezone.now().replace(day=1).strftime('%Y-%m-%d')
if not end_date:
end_date = timezone.now().strftime('%Y-%m-%d')
# Convert strings to date objects for filtering
# Note: We filter by the day inclusive
sales = Sale.objects.filter(created_at__date__gte=start_date, created_at__date__lte=end_date).exclude(status='cancelled')
purchases = Purchase.objects.filter(created_at__date__gte=start_date, created_at__date__lte=end_date).exclude(status='cancelled').prefetch_related('items__product')
# Output VAT (Sales)
total_sales_subtotal = sales.aggregate(sum=Sum('subtotal'))['sum'] or 0
total_output_vat = sales.aggregate(sum=Sum('vat_amount'))['sum'] or 0
total_sales_gross = sales.aggregate(sum=Sum('total_amount'))['sum'] or 0
# Input VAT (Purchases) - Estimated based on Product VAT rate
# Since Purchase model doesn't store VAT explicitly, we calculate it from items
total_purchases_subtotal = 0
total_input_vat = 0
for purchase in purchases:
purchase_vat = 0
purchase_subtotal = 0
for item in purchase.items.all():
# Assume item line_total is cost * quantity
# We calculate VAT on top.
rate = float(item.product.vat)
line_total = float(item.line_total)
tax = line_total * (rate / 100.0)
purchase_vat += tax
purchase_subtotal += line_total
total_input_vat += purchase_vat
total_purchases_subtotal += purchase_subtotal
total_purchases_gross = total_purchases_subtotal + total_input_vat
context = {
'start_date': start_date,
'end_date': end_date,
'total_sales_subtotal': total_sales_subtotal,
'total_output_vat': total_output_vat,
'total_sales_gross': total_sales_gross,
'total_purchases_subtotal': total_purchases_subtotal,
'total_input_vat': total_input_vat,
'total_purchases_gross': total_purchases_gross,
'net_vat': float(total_output_vat) - total_input_vat,
'currency': 'OMR',
}
return render(request, 'accounting/vat_report.html', context)
@login_required @login_required
def accounting_dashboard(request): def accounting_dashboard(request):
total_assets = sum(acc.balance for acc in Account.objects.filter(account_type='asset')) total_assets = sum(acc.balance for acc in Account.objects.filter(account_type='asset'))

View File

@ -3,11 +3,44 @@
{% block content %} {% block content %}
<div class="container-fluid"> <div class="container-fluid">
<div class="d-flex justify-content-between align-items-center mb-4"> <div class="d-flex justify-content-between align-items-center mb-4 no-print">
<h1 class="h3 mb-0 text-gray-800">Customer Statement</h1> <h1 class="h3 mb-0 text-gray-800">Customer Statement</h1>
{% if selected_customer %}
<button onclick="window.print()" class="btn btn-secondary">
<i class="bi bi-printer"></i> Print Statement
</button>
{% endif %}
</div> </div>
<div class="card shadow mb-4"> <style>
@media print {
body * {
visibility: hidden;
}
.printable-area, .printable-area * {
visibility: visible;
}
.printable-area {
position: absolute;
left: 0;
top: 0;
width: 100%;
}
.no-print {
display: none !important;
}
/* Reset card styles for print */
.card {
border: none !important;
box-shadow: none !important;
}
.card-body {
padding: 0 !important;
}
}
</style>
<div class="card shadow mb-4 no-print">
<div class="card-header py-3"> <div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Filter</h6> <h6 class="m-0 font-weight-bold text-primary">Filter</h6>
</div> </div>
@ -40,28 +73,45 @@
</div> </div>
{% if selected_customer %} {% if selected_customer %}
<div class="card shadow"> <div class="card shadow printable-area">
<div class="card-body"> <div class="card-body">
<h5 class="card-title">Statement for: {{ selected_customer.name }}</h5> <div class="mb-4">
<h4 class="text-center mb-3">Customer Statement</h4>
<div class="row">
<div class="col-6">
<h5 class="fw-bold">{{ selected_customer.name }}</h5>
{% if selected_customer.phone %}<p class="mb-0">Phone: {{ selected_customer.phone }}</p>{% endif %}
</div>
<div class="col-6 text-end">
<p class="mb-0">Date Range:</p>
<p class="fw-bold">
{% if start_date %}{{ start_date }}{% else %}Start{% endif %}
to
{% if end_date %}{{ end_date }}{% else %}Present{% endif %}
</p>
</div>
</div>
</div>
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-bordered"> <table class="table table-bordered">
<thead> <thead>
<tr> <tr class="table-light">
<th>Date</th> <th>Date</th>
<th>Invoice #</th> <th>Invoice #</th>
<th>Total</th> <th class="text-end">Total</th>
<th>Paid</th> <th class="text-end">Paid</th>
<th>Balance</th> <th class="text-end">Balance</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for sale in sales %} {% for sale in sales %}
<tr> <tr>
<td>{{ sale.created_at|date:"Y-m-d" }}</td> <td>{{ sale.created_at|date:"Y-m-d" }}</td>
<td>{{ sale.invoice_number }}</td> <td>{{ sale.invoice_number|default:sale.id }}</td>
<td>{{ sale.total_amount }}</td> <td class="text-end">{{ sale.total_amount }}</td>
<td>{{ sale.paid_amount }}</td> <td class="text-end">{{ sale.paid_amount }}</td>
<td>{{ sale.balance_due }}</td> <td class="text-end">{{ sale.balance_due }}</td>
</tr> </tr>
{% empty %} {% empty %}
<tr> <tr>
@ -69,6 +119,14 @@
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
<tfoot>
<tr class="fw-bold table-light">
<td colspan="2" class="text-end">Totals:</td>
<td class="text-end">{{ total_amount|floatformat:2 }}</td>
<td class="text-end">{{ total_paid|floatformat:2 }}</td>
<td class="text-end">{{ total_balance|floatformat:2 }}</td>
</tr>
</tfoot>
</table> </table>
</div> </div>
</div> </div>

View File

@ -58,6 +58,17 @@
</div> </div>
</a> </a>
</div> </div>
<div class="col-md-3">
<a href="{% url 'vat_report' %}" class="card border-0 shadow-sm text-decoration-none h-100 lift-hover">
<div class="card-body text-center p-4">
<div class="icon-box bg-info-soft text-info rounded-circle mx-auto mb-3" style="width: 60px; height: 60px; display: flex; align-items: center; justify-content: center;">
<i class="bi bi-percent fs-3"></i>
</div>
<h6 class="fw-bold text-dark mb-1">{% trans "VAT Report" %}</h6>
<small class="text-muted">{% trans "Tax declaration summary" %}</small>
</div>
</a>
</div>
</div> </div>
<div class="row g-4"> <div class="row g-4">
@ -121,6 +132,7 @@
.bg-success-soft { background-color: rgba(25, 135, 84, 0.1); } .bg-success-soft { background-color: rgba(25, 135, 84, 0.1); }
.bg-primary-soft { background-color: rgba(13, 110, 253, 0.1); } .bg-primary-soft { background-color: rgba(13, 110, 253, 0.1); }
.bg-warning-soft { background-color: rgba(255, 193, 7, 0.1); } .bg-warning-soft { background-color: rgba(255, 193, 7, 0.1); }
.bg-info-soft { background-color: rgba(13, 202, 240, 0.1); }
.lift-hover { transition: transform 0.2s, box-shadow 0.2s; } .lift-hover { transition: transform 0.2s, box-shadow 0.2s; }
.lift-hover:hover { transform: translateY(-5px); box-shadow: 0 .5rem 1rem rgba(0,0,0,.15)!important; } .lift-hover:hover { transform: translateY(-5px); box-shadow: 0 .5rem 1rem rgba(0,0,0,.15)!important; }
</style> </style>

View File

@ -3,11 +3,44 @@
{% block content %} {% block content %}
<div class="container-fluid"> <div class="container-fluid">
<div class="d-flex justify-content-between align-items-center mb-4"> <div class="d-flex justify-content-between align-items-center mb-4 no-print">
<h1 class="h3 mb-0 text-gray-800">Supplier Statement</h1> <h1 class="h3 mb-0 text-gray-800">Supplier Statement</h1>
{% if selected_supplier %}
<button onclick="window.print()" class="btn btn-secondary">
<i class="bi bi-printer"></i> Print Statement
</button>
{% endif %}
</div> </div>
<div class="card shadow mb-4"> <style>
@media print {
body * {
visibility: hidden;
}
.printable-area, .printable-area * {
visibility: visible;
}
.printable-area {
position: absolute;
left: 0;
top: 0;
width: 100%;
}
.no-print {
display: none !important;
}
/* Reset card styles for print */
.card {
border: none !important;
box-shadow: none !important;
}
.card-body {
padding: 0 !important;
}
}
</style>
<div class="card shadow mb-4 no-print">
<div class="card-header py-3"> <div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Filter</h6> <h6 class="m-0 font-weight-bold text-primary">Filter</h6>
</div> </div>
@ -40,28 +73,45 @@
</div> </div>
{% if selected_supplier %} {% if selected_supplier %}
<div class="card shadow"> <div class="card shadow printable-area">
<div class="card-body"> <div class="card-body">
<h5 class="card-title">Statement for: {{ selected_supplier.name }}</h5> <div class="mb-4">
<h4 class="text-center mb-3">Supplier Statement</h4>
<div class="row">
<div class="col-6">
<h5 class="fw-bold">{{ selected_supplier.name }}</h5>
{% if selected_supplier.phone %}<p class="mb-0">Phone: {{ selected_supplier.phone }}</p>{% endif %}
</div>
<div class="col-6 text-end">
<p class="mb-0">Date Range:</p>
<p class="fw-bold">
{% if start_date %}{{ start_date }}{% else %}Start{% endif %}
to
{% if end_date %}{{ end_date }}{% else %}Present{% endif %}
</p>
</div>
</div>
</div>
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-bordered"> <table class="table table-bordered">
<thead> <thead>
<tr> <tr class="table-light">
<th>Date</th> <th>Date</th>
<th>Ref #</th> <th>Ref #</th>
<th>Total</th> <th class="text-end">Total</th>
<th>Paid</th> <th class="text-end">Paid</th>
<th>Balance</th> <th class="text-end">Balance</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for purchase in purchases %} {% for purchase in purchases %}
<tr> <tr>
<td>{{ purchase.created_at|date:"Y-m-d" }}</td> <td>{{ purchase.created_at|date:"Y-m-d" }}</td>
<td>{{ purchase.invoice_number }}</td> <td>{{ purchase.invoice_number|default:purchase.id }}</td>
<td>{{ purchase.total_amount }}</td> <td class="text-end">{{ purchase.total_amount }}</td>
<td>{{ purchase.paid_amount }}</td> <td class="text-end">{{ purchase.paid_amount }}</td>
<td>{{ purchase.balance_due }}</td> <td class="text-end">{{ purchase.balance_due }}</td>
</tr> </tr>
{% empty %} {% empty %}
<tr> <tr>
@ -69,6 +119,14 @@
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
<tfoot>
<tr class="fw-bold table-light">
<td colspan="2" class="text-end">Totals:</td>
<td class="text-end">{{ total_amount|floatformat:2 }}</td>
<td class="text-end">{{ total_paid|floatformat:2 }}</td>
<td class="text-end">{{ total_balance|floatformat:2 }}</td>
</tr>
</tfoot>
</table> </table>
</div> </div>
</div> </div>

View File

@ -900,24 +900,42 @@ def customer_statement(request):
selected_customer = None selected_customer = None
sales = [] sales = []
# Totals
total_amount = 0
total_paid = 0
total_balance = 0
customer_id = request.GET.get('customer') customer_id = request.GET.get('customer')
start_date = request.GET.get('start_date') start_date = request.GET.get('start_date')
end_date = request.GET.get('end_date') end_date = request.GET.get('end_date')
if customer_id: if customer_id:
selected_customer = get_object_or_404(Customer, id=customer_id) selected_customer = get_object_or_404(Customer, id=customer_id)
sales = Sale.objects.filter(customer=selected_customer).order_by('-created_at') sales = Sale.objects.filter(customer=selected_customer).order_by('created_at')
if start_date: if start_date:
sales = sales.filter(created_at__date__gte=start_date) sales = sales.filter(created_at__date__gte=start_date)
if end_date: if end_date:
sales = sales.filter(created_at__date__lte=end_date) sales = sales.filter(created_at__date__lte=end_date)
# Calculate totals
aggregates = sales.aggregate(
sum_total=Sum('total_amount'),
sum_paid=Sum('paid_amount'),
sum_balance=Sum('balance_due')
)
total_amount = aggregates['sum_total'] or 0
total_paid = aggregates['sum_paid'] or 0
total_balance = aggregates['sum_balance'] or 0
context = { context = {
'customers': customers, 'customers': customers,
'selected_customer': selected_customer, 'selected_customer': selected_customer,
'sales': sales, 'sales': sales,
'start_date': start_date, 'start_date': start_date,
'end_date': end_date 'end_date': end_date,
'total_amount': total_amount,
'total_paid': total_paid,
'total_balance': total_balance
} }
return render(request, 'core/customer_statement.html', context) return render(request, 'core/customer_statement.html', context)
@ -927,24 +945,42 @@ def supplier_statement(request):
selected_supplier = None selected_supplier = None
purchases = [] purchases = []
# Totals
total_amount = 0
total_paid = 0
total_balance = 0
supplier_id = request.GET.get('supplier') supplier_id = request.GET.get('supplier')
start_date = request.GET.get('start_date') start_date = request.GET.get('start_date')
end_date = request.GET.get('end_date') end_date = request.GET.get('end_date')
if supplier_id: if supplier_id:
selected_supplier = get_object_or_404(Supplier, id=supplier_id) selected_supplier = get_object_or_404(Supplier, id=supplier_id)
purchases = Purchase.objects.filter(supplier=selected_supplier).order_by('-created_at') purchases = Purchase.objects.filter(supplier=selected_supplier).order_by('created_at')
if start_date: if start_date:
purchases = purchases.filter(created_at__date__gte=start_date) purchases = purchases.filter(created_at__date__gte=start_date)
if end_date: if end_date:
purchases = purchases.filter(created_at__date__lte=end_date) purchases = purchases.filter(created_at__date__lte=end_date)
# Calculate totals
aggregates = purchases.aggregate(
sum_total=Sum('total_amount'),
sum_paid=Sum('paid_amount'),
sum_balance=Sum('balance_due')
)
total_amount = aggregates['sum_total'] or 0
total_paid = aggregates['sum_paid'] or 0
total_balance = aggregates['sum_balance'] or 0
context = { context = {
'suppliers': suppliers, 'suppliers': suppliers,
'selected_supplier': selected_supplier, 'selected_supplier': selected_supplier,
'purchases': purchases, 'purchases': purchases,
'start_date': start_date, 'start_date': start_date,
'end_date': end_date 'end_date': end_date,
'total_amount': total_amount,
'total_paid': total_paid,
'total_balance': total_balance
} }
return render(request, 'core/supplier_statement.html', context) return render(request, 'core/supplier_statement.html', context)