- Export all expenses as CSV from Settings (semicolon, UTF-8 BOM) - Budget calculation now includes planned expenses: income - fixed - savings - planned - Budget live update also accounts for planned expenses
121 lines
3.2 KiB
Python
121 lines
3.2 KiB
Python
import csv
|
|
from datetime import timedelta
|
|
|
|
from django.http import HttpResponse
|
|
from django.shortcuts import render, redirect
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.utils import timezone
|
|
|
|
from .models import Category, Expense, FixedExpenseConcept
|
|
from .forms import ExpenseForm
|
|
|
|
|
|
@login_required
|
|
def dashboard(request):
|
|
form = ExpenseForm()
|
|
context = {"form": form}
|
|
return render(request, "pages/expenses/dashboard.html", context)
|
|
|
|
|
|
@login_required
|
|
def add_expense(request):
|
|
if request.method == "POST":
|
|
form = ExpenseForm(request.POST)
|
|
if form.is_valid():
|
|
form.save()
|
|
return redirect("expenses:expense_success")
|
|
|
|
context = {"form": form}
|
|
return render(request, "pages/expenses/dashboard.html", context)
|
|
|
|
return redirect("expenses:dashboard")
|
|
|
|
|
|
@login_required
|
|
def expense_success(request):
|
|
return render(request, "pages/expenses/expense_success.html")
|
|
|
|
|
|
@login_required
|
|
def week_view(request):
|
|
from collections import OrderedDict
|
|
from decimal import Decimal
|
|
|
|
today = timezone.localdate()
|
|
offset = int(request.GET.get("offset", 0))
|
|
monday = today - timedelta(days=today.weekday()) + timedelta(weeks=offset)
|
|
sunday = monday + timedelta(days=6)
|
|
expenses = Expense.objects.filter(
|
|
created_at__date__gte=monday,
|
|
created_at__date__lte=sunday,
|
|
).select_related("category", "subcategory")
|
|
|
|
categories_data = OrderedDict()
|
|
total = Decimal("0")
|
|
for expense in expenses:
|
|
cat = expense.category
|
|
if cat.id not in categories_data:
|
|
categories_data[cat.id] = {
|
|
"category": cat,
|
|
"expenses": [],
|
|
"subcategory_totals": OrderedDict(),
|
|
"total": Decimal("0"),
|
|
}
|
|
entry = categories_data[cat.id]
|
|
entry["expenses"].append(expense)
|
|
entry["total"] += expense.amount
|
|
total += expense.amount
|
|
|
|
sub = expense.subcategory
|
|
if sub.id not in entry["subcategory_totals"]:
|
|
entry["subcategory_totals"][sub.id] = {
|
|
"subcategory": sub,
|
|
"total": Decimal("0"),
|
|
}
|
|
entry["subcategory_totals"][sub.id]["total"] += expense.amount
|
|
|
|
is_current_week = offset == 0
|
|
context = {
|
|
"categories_data": categories_data.values(),
|
|
"total": total,
|
|
"monday": monday,
|
|
"sunday": sunday,
|
|
"offset": offset,
|
|
"prev_offset": offset - 1,
|
|
"next_offset": offset + 1,
|
|
"is_current_week": is_current_week,
|
|
}
|
|
return render(request, "pages/expenses/week.html", context)
|
|
|
|
|
|
@login_required
|
|
def settings_view(request):
|
|
categories = Category.objects.all().prefetch_related("subcategories")
|
|
fixed_expenses = FixedExpenseConcept.objects.all()
|
|
context = {"categories": categories, "fixed_expenses": fixed_expenses}
|
|
return render(request, "pages/expenses/settings.html", context)
|
|
|
|
|
|
@login_required
|
|
def export_csv(request):
|
|
response = HttpResponse(content_type="text/csv")
|
|
response["Content-Disposition"] = 'attachment; filename="kakebo_expenses.csv"'
|
|
response.write("\ufeff") # BOM for Excel UTF-8
|
|
|
|
writer = csv.writer(response, delimiter=";")
|
|
writer.writerow(["Date", "Concept", "Amount", "Category", "Subcategory"])
|
|
|
|
expenses = Expense.objects.all().select_related("category", "subcategory")
|
|
for expense in expenses:
|
|
writer.writerow(
|
|
[
|
|
expense.created_at.strftime("%Y-%m-%d"),
|
|
expense.concept,
|
|
str(expense.amount),
|
|
expense.category.name,
|
|
expense.subcategory.name,
|
|
]
|
|
)
|
|
|
|
return response
|