diff --git a/accounting/__pycache__/urls.cpython-311.pyc b/accounting/__pycache__/urls.cpython-311.pyc index 38d3eff..2953fcd 100644 Binary files a/accounting/__pycache__/urls.cpython-311.pyc and b/accounting/__pycache__/urls.cpython-311.pyc differ diff --git a/accounting/__pycache__/views.cpython-311.pyc b/accounting/__pycache__/views.cpython-311.pyc index 60fe963..30c1509 100644 Binary files a/accounting/__pycache__/views.cpython-311.pyc and b/accounting/__pycache__/views.cpython-311.pyc differ diff --git a/accounting/templates/accounting/vat_report.html b/accounting/templates/accounting/vat_report.html new file mode 100644 index 0000000..e5a5b9d --- /dev/null +++ b/accounting/templates/accounting/vat_report.html @@ -0,0 +1,121 @@ +{% extends 'base.html' %} +{% load i18n %} + +{% block content %} +
+ +
+
+

{% trans "VAT Report" %} / تقرير ضريبة القيمة المضافة

+

{% trans "Tax Declaration Summary" %} / ملخص الإقرار الضريبي

+
+
+ +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ +
+
+
+
+ + +
+ +
+
+
+
+ 1. {% trans "Sales (Output VAT)" %} / المبيعات (ضريبة المخرجات) +
+
+
+ + + + + + + + + + + + + + + +
{% trans "Total Sales (Excl. VAT)" %}
إجمالي المبيعات (غير شامل الضريبة)
{{ total_sales_subtotal|floatformat:3 }} {{ global_settings.currency_symbol }}
{% trans "Total VAT Collected" %}
إجمالي الضريبة المحصلة
{{ total_output_vat|floatformat:3 }} {{ global_settings.currency_symbol }}
{% trans "Total Gross Sales" %}
إجمالي المبيعات (شامل الضريبة)
{{ total_sales_gross|floatformat:3 }} {{ global_settings.currency_symbol }}
+
+
+
+ + +
+
+
+
+ 2. {% trans "Purchases (Input VAT)" %} / المشتريات (ضريبة المدخلات) +
+
+
+ + + + + + + + + + + + + + + +
{% trans "Total Purchases (Excl. VAT)" %}
إجمالي المشتريات (غير شامل الضريبة)
{{ total_purchases_subtotal|floatformat:3 }} {{ global_settings.currency_symbol }}
{% trans "Total VAT Paid (Recoverable)" %}
إجمالي الضريبة المدفوعة (القابلة للاسترداد)
{{ total_input_vat|floatformat:3 }} {{ global_settings.currency_symbol }}
{% trans "Total Gross Purchases" %}
إجمالي المشتريات (شامل الضريبة)
{{ total_purchases_gross|floatformat:3 }} {{ global_settings.currency_symbol }}
+
+
+
+ + +
+
+
+

{% trans "Net VAT Payable / (Refundable)" %}

+
صافي الضريبة المستحقة الدفع / (المستردة)
+

+ {{ net_vat|floatformat:3 }} {{ global_settings.currency_symbol }} +

+

+ {% if net_vat > 0 %} + {% trans "Amount to be paid to Tax Authority" %} / المبلغ المستحق للدفع للهيئة الضريبية + {% else %} + {% trans "Amount to be refunded from Tax Authority" %} / المبلغ المستحق للاسترداد من الهيئة الضريبية + {% endif %} +

+
+
+
+
+
+{% endblock %} diff --git a/accounting/urls.py b/accounting/urls.py index b1d6a56..8b4699a 100644 --- a/accounting/urls.py +++ b/accounting/urls.py @@ -9,6 +9,7 @@ urlpatterns = [ path('journal-entries/', views.journal_entries, name='journal_entries'), path('journal-entries/manual/', views.manual_journal_entry, name='manual_journal_entry'), path('ledger//', 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('balance-sheet/', views.balance_sheet, name='balance_sheet'), path('profit-loss/', views.profit_loss, name='profit_loss'), diff --git a/accounting/views.py b/accounting/views.py index 6a563f6..28476a5 100644 --- a/accounting/views.py +++ b/accounting/views.py @@ -4,13 +4,71 @@ from django.contrib.auth.decorators import login_required from django.contrib import messages from .models import Account, JournalEntry, JournalItem 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.utils import timezone -from datetime import datetime +from datetime import datetime, date from django.db import transaction 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 def accounting_dashboard(request): total_assets = sum(acc.balance for acc in Account.objects.filter(account_type='asset')) diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 2433d21..1e00527 100644 Binary files a/core/__pycache__/views.cpython-311.pyc and b/core/__pycache__/views.cpython-311.pyc differ diff --git a/core/templates/core/customer_statement.html b/core/templates/core/customer_statement.html index 0f755ca..7c23d2f 100644 --- a/core/templates/core/customer_statement.html +++ b/core/templates/core/customer_statement.html @@ -3,11 +3,44 @@ {% block content %}
-
+

Customer Statement

+ {% if selected_customer %} + + {% endif %}
-
+ + +
Filter
@@ -40,28 +73,45 @@
{% if selected_customer %} -
+
-
Statement for: {{ selected_customer.name }}
+
+

Customer Statement

+
+
+
{{ selected_customer.name }}
+ {% if selected_customer.phone %}

Phone: {{ selected_customer.phone }}

{% endif %} +
+
+

Date Range:

+

+ {% if start_date %}{{ start_date }}{% else %}Start{% endif %} + to + {% if end_date %}{{ end_date }}{% else %}Present{% endif %} +

+
+
+
+
- + - - - + + + {% for sale in sales %} - - - - + + + + {% empty %} @@ -69,6 +119,14 @@ {% endfor %} + + + + + + + +
Date Invoice #TotalPaidBalanceTotalPaidBalance
{{ sale.created_at|date:"Y-m-d" }}{{ sale.invoice_number }}{{ sale.total_amount }}{{ sale.paid_amount }}{{ sale.balance_due }}{{ sale.invoice_number|default:sale.id }}{{ sale.total_amount }}{{ sale.paid_amount }}{{ sale.balance_due }}
Totals:{{ total_amount|floatformat:2 }}{{ total_paid|floatformat:2 }}{{ total_balance|floatformat:2 }}
diff --git a/core/templates/core/reports.html b/core/templates/core/reports.html index af7edff..d2a4683 100644 --- a/core/templates/core/reports.html +++ b/core/templates/core/reports.html @@ -58,6 +58,17 @@
+
@@ -121,6 +132,7 @@ .bg-success-soft { background-color: rgba(25, 135, 84, 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-info-soft { background-color: rgba(13, 202, 240, 0.1); } .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; } diff --git a/core/templates/core/supplier_statement.html b/core/templates/core/supplier_statement.html index 191622e..4a95fa5 100644 --- a/core/templates/core/supplier_statement.html +++ b/core/templates/core/supplier_statement.html @@ -3,11 +3,44 @@ {% block content %}
-
+

Supplier Statement

+ {% if selected_supplier %} + + {% endif %}
-
+ + +
Filter
@@ -40,28 +73,45 @@
{% if selected_supplier %} -
+
-
Statement for: {{ selected_supplier.name }}
+
+

Supplier Statement

+
+
+
{{ selected_supplier.name }}
+ {% if selected_supplier.phone %}

Phone: {{ selected_supplier.phone }}

{% endif %} +
+
+

Date Range:

+

+ {% if start_date %}{{ start_date }}{% else %}Start{% endif %} + to + {% if end_date %}{{ end_date }}{% else %}Present{% endif %} +

+
+
+
+
- + - - - + + + {% for purchase in purchases %} - - - - + + + + {% empty %} @@ -69,6 +119,14 @@ {% endfor %} + + + + + + + +
Date Ref #TotalPaidBalanceTotalPaidBalance
{{ purchase.created_at|date:"Y-m-d" }}{{ purchase.invoice_number }}{{ purchase.total_amount }}{{ purchase.paid_amount }}{{ purchase.balance_due }}{{ purchase.invoice_number|default:purchase.id }}{{ purchase.total_amount }}{{ purchase.paid_amount }}{{ purchase.balance_due }}
Totals:{{ total_amount|floatformat:2 }}{{ total_paid|floatformat:2 }}{{ total_balance|floatformat:2 }}
diff --git a/core/views.py b/core/views.py index 2af5d92..a594402 100644 --- a/core/views.py +++ b/core/views.py @@ -900,24 +900,42 @@ def customer_statement(request): selected_customer = None sales = [] + # Totals + total_amount = 0 + total_paid = 0 + total_balance = 0 + customer_id = request.GET.get('customer') start_date = request.GET.get('start_date') end_date = request.GET.get('end_date') if 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: sales = sales.filter(created_at__date__gte=start_date) if 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 = { 'customers': customers, 'selected_customer': selected_customer, 'sales': sales, '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) @@ -927,24 +945,42 @@ def supplier_statement(request): selected_supplier = None purchases = [] + # Totals + total_amount = 0 + total_paid = 0 + total_balance = 0 + supplier_id = request.GET.get('supplier') start_date = request.GET.get('start_date') end_date = request.GET.get('end_date') if 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: purchases = purchases.filter(created_at__date__gte=start_date) if 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 = { 'suppliers': suppliers, 'selected_supplier': selected_supplier, 'purchases': purchases, '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)