from django.utils.translation import gettext as _ from django.shortcuts import render, get_object_or_404, redirect 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 from django.utils import timezone from datetime import datetime from django.db import transaction import json @login_required def accounting_dashboard(request): total_assets = sum(acc.balance for acc in Account.objects.filter(account_type='asset')) total_liabilities = sum(acc.balance for acc in Account.objects.filter(account_type='liability')) total_equity = sum(acc.balance for acc in Account.objects.filter(account_type='equity')) # Revenue and Expenses for current month month_start = timezone.now().replace(day=1, hour=0, minute=0, second=0, microsecond=0) revenue_items = JournalItem.objects.filter( account__account_type='income', entry__date__gte=month_start ) monthly_revenue = (revenue_items.filter(type='credit').aggregate(total=Sum('amount'))['total'] or 0) - \ (revenue_items.filter(type='debit').aggregate(total=Sum('amount'))['total'] or 0) expense_items = JournalItem.objects.filter( account__account_type='expense', entry__date__gte=month_start ) monthly_expense = (expense_items.filter(type='debit').aggregate(total=Sum('amount'))['total'] or 0) - \ (expense_items.filter(type='credit').aggregate(total=Sum('amount'))['total'] or 0) context = { 'total_assets': total_assets, 'total_liabilities': total_liabilities, 'total_equity': total_equity, 'monthly_revenue': monthly_revenue, 'monthly_expense': monthly_expense, 'net_profit': monthly_revenue - monthly_expense, 'recent_entries': JournalEntry.objects.order_by('-date', '-id')[:10] } return render(request, 'accounting/dashboard.html', context) @login_required def chart_of_accounts(request): accounts = Account.objects.all().order_by('code') return render(request, 'accounting/chart_of_accounts.html', {'accounts': accounts}) @login_required def account_create_update(request, pk=None): if pk: account = get_object_or_404(Account, pk=pk) else: account = None if request.method == 'POST': form = AccountForm(request.POST, instance=account) if form.is_valid(): form.save() messages.success(request, _("Account saved successfully.")) return redirect('chart_of_accounts') else: form = AccountForm(instance=account) return render(request, 'accounting/account_form.html', {'form': form, 'account': account}) @login_required def journal_entries(request): entries = JournalEntry.objects.all().order_by('-date', '-id') return render(request, 'accounting/journal_entries.html', {'entries': entries}) @login_required def manual_journal_entry(request): accounts = Account.objects.filter(is_active=True).order_by('code') if request.method == 'POST': form = JournalEntryForm(request.POST) # Manual journal entry requires at least two items and they must balance account_ids = request.POST.getlist('account[]') types = request.POST.getlist('type[]') amounts = request.POST.getlist('amount[]') if form.is_valid(): try: with transaction.atomic(): entry = form.save() total_debit = 0 total_credit = 0 for i in range(len(account_ids)): acc_id = account_ids[i] item_type = types[i] amount = float(amounts[i]) if amount <= 0: continue JournalItem.objects.create( entry=entry, account_id=acc_id, type=item_type, amount=amount ) if item_type == 'debit': total_debit += amount else: total_credit += amount if round(total_debit, 3) != round(total_credit, 3): raise Exception(f"Journal entry does not balance. Total Debit: {total_debit}, Total Credit: {total_credit}") if total_debit == 0: raise Exception("Journal entry must have at least one debit and one credit.") messages.success(request, _("Manual journal entry created successfully.")) return redirect('journal_entries') except Exception as e: messages.error(request, str(e)) else: form = JournalEntryForm() return render(request, 'accounting/journal_entry_form.html', { 'form': form, 'accounts': accounts }) @login_required def account_ledger(request, account_id): account = get_object_or_404(Account, id=account_id) items = JournalItem.objects.filter(account=account).order_by('entry__date', 'entry__id') # Calculate running balance running_balance = 0 ledger_items = [] for item in items: if account.account_type in ['asset', 'expense']: change = item.amount if item.type == 'debit' else -item.amount else: change = item.amount if item.type == 'credit' else -item.amount running_balance += change ledger_items.append({ 'item': item, 'balance': running_balance }) return render(request, 'accounting/account_ledger.html', { 'account': account, 'ledger_items': ledger_items, 'total_balance': running_balance }) @login_required def trial_balance(request): accounts = Account.objects.all().order_by('code') trial_data = [] total_debit = 0 total_credit = 0 for acc in accounts: items = acc.journal_items.all() debits = items.filter(type='debit').aggregate(total=Sum('amount'))['total'] or 0 credits = items.filter(type='credit').aggregate(total=Sum('amount'))['total'] or 0 if debits > 0 or credits > 0: trial_data.append({ 'account': acc, 'debit': debits, 'credit': credits }) total_debit += debits total_credit += credits return render(request, 'accounting/trial_balance.html', { 'trial_data': trial_data, 'total_debit': total_debit, 'total_credit': total_credit }) @login_required def balance_sheet(request): assets = Account.objects.filter(account_type='asset') liabilities = Account.objects.filter(account_type='liability') equity = Account.objects.filter(account_type='equity') # Include Net Income in Equity revenue = JournalItem.objects.filter(account__account_type='income').aggregate( cr=Sum('amount', filter=Q(type='credit')), dr=Sum('amount', filter=Q(type='debit')) ) net_revenue = (revenue['cr'] or 0) - (revenue['dr'] or 0) expenses = JournalItem.objects.filter(account__account_type='expense').aggregate( dr=Sum('amount', filter=Q(type='debit')), cr=Sum('amount', filter=Q(type='credit')) ) net_expenses = (expenses['dr'] or 0) - (expenses['cr'] or 0) net_income = net_revenue - net_expenses asset_total = sum(acc.balance for acc in assets) liability_total = sum(acc.balance for acc in liabilities) equity_total = sum(acc.balance for acc in equity) + net_income return render(request, 'accounting/balance_sheet.html', { 'assets': assets, 'liabilities': liabilities, 'equity': equity, 'net_income': net_income, 'asset_total': asset_total, 'liability_total': liability_total, 'equity_total': equity_total, 'date': timezone.now() }) @login_required def profit_loss(request): revenue_accounts = Account.objects.filter(account_type='income') expense_accounts = Account.objects.filter(account_type='expense') revenue_total = sum(acc.balance for acc in revenue_accounts) expense_total = sum(acc.balance for acc in expense_accounts) return render(request, 'accounting/profit_loss.html', { 'revenue_accounts': revenue_accounts, 'expense_accounts': expense_accounts, 'revenue_total': revenue_total, 'expense_total': expense_total, 'net_profit': revenue_total - expense_total, 'date': timezone.now() })