924 lines
25 KiB
Python
924 lines
25 KiB
Python
from operator import truediv
|
|
from datetime import date, datetime
|
|
from django.contrib import messages
|
|
from django.template import context
|
|
from .models import Account, Category, Expense, FuelEntry, Tag, Income
|
|
from .forms import ExpenseForm, IncomeForm, TagForm, AccountForm, FuelEntryForm, CategoryForm
|
|
# from dateutli.relativedelta import relativedelta
|
|
|
|
from django.db.models import Sum
|
|
from django.contrib.auth import login
|
|
from django.core.paginator import Paginator
|
|
from django.utils.ipv6 import is_valid_ipv6_address
|
|
from django.db.models.functions import ExtractMonth, ExtractYear
|
|
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.shortcuts import get_object_or_404, render, redirect
|
|
|
|
MONTHS = {
|
|
1:'ENERO',
|
|
2:'FEBRERO',
|
|
3:'MARZO',
|
|
4:'ABRIL',
|
|
5:'MAYO',
|
|
6:'JUNIO',
|
|
7:'JULIO',
|
|
8:'AGOSTO',
|
|
9:'SEPTIEMBRE',
|
|
10:'OCTUBRE',
|
|
11:'NOVIEMBRE',
|
|
12:'DICIEMBRE'
|
|
}
|
|
|
|
def _get_int(value):
|
|
try:
|
|
return int(value)
|
|
except (TypeError, ValueError):
|
|
return None
|
|
|
|
def sub_months(year, month, n):
|
|
month -= n
|
|
while month <= 0:
|
|
month += 12
|
|
year -= 1
|
|
return year, month
|
|
|
|
@login_required
|
|
def home(request):
|
|
expenses = Expense.objects.filter(owner=request.user)
|
|
|
|
# Last expenses
|
|
last_expenses = (
|
|
expenses
|
|
.select_related('category')
|
|
.order_by('-date')[:5]
|
|
)
|
|
|
|
# Simple KPIs (current month)
|
|
today = date.today()
|
|
month_expenses = expenses.filter(
|
|
date__year=today.year,
|
|
date__month=today.month
|
|
)
|
|
|
|
kpi_total = month_expenses.aggregate(
|
|
total=Sum('amount')
|
|
)['total'] or 0
|
|
|
|
kpi_count = month_expenses.count()
|
|
|
|
kpi_categories = (
|
|
month_expenses
|
|
.values('category')
|
|
.distinct()
|
|
.count()
|
|
)
|
|
|
|
six_months = []
|
|
for i in range(5, -1, -1):
|
|
y, m = sub_months(today.year, today.month, i)
|
|
six_months.append((y, m))
|
|
|
|
mini_data = []
|
|
for y, m in six_months:
|
|
total = expenses.filter(
|
|
date__year=y,
|
|
date__month=m
|
|
).aggregate(total=Sum('amount'))['total'] or 0
|
|
|
|
mini_data.append({
|
|
'label': f'{m}/{y}',
|
|
'total': float(total),
|
|
})
|
|
|
|
return render(request, 'expenses/home.html', {
|
|
'active_menu': 'home',
|
|
'last_expenses': last_expenses,
|
|
'kpi_total': kpi_total,
|
|
'kpi_count': kpi_count,
|
|
'kpi_categories': kpi_categories,
|
|
'mini_chart_labels': [x['label'] for x in mini_data],
|
|
'mini_chart_data': [x['total'] for x in mini_data],
|
|
})
|
|
|
|
@login_required
|
|
def expense_list(request):
|
|
expenses = Expense.objects.filter(owner=request.user)
|
|
|
|
categories = Category.objects.filter(owner=request.user)
|
|
|
|
year_list = (
|
|
Expense.objects.filter(owner=request.user)
|
|
.dates('date', 'year')
|
|
)
|
|
|
|
months = list(range(1, 13))
|
|
|
|
# Filters
|
|
year = _get_int(request.GET.get('year'))
|
|
month = _get_int(request.GET.get('month'))
|
|
category = _get_int(request.GET.get('category'))
|
|
account_id = _get_int(request.GET.get('account'))
|
|
|
|
tag_ids = request.GET.getlist('tag')
|
|
tag_ids = [int(t) for t in tag_ids]
|
|
|
|
if year:
|
|
expenses = expenses.filter(date__year=year)
|
|
|
|
if month:
|
|
expenses = expenses.filter(date__month=month)
|
|
|
|
if category:
|
|
expenses = expenses.filter(category_id=category)
|
|
|
|
if tag_ids:
|
|
expenses = expenses.filter(tags__id__in=tag_ids).distinct()
|
|
|
|
if account_id:
|
|
expenses = expenses.filter(account_id=account_id)
|
|
|
|
selected_tags = tag_ids or []
|
|
|
|
expenses = expenses.order_by('-date')
|
|
|
|
total_amount = expenses.aggregate(
|
|
total=Sum('amount')
|
|
)['total'] or 0
|
|
|
|
expense_count = expenses.count()
|
|
|
|
category_count = (
|
|
expenses.values('category')
|
|
.distinct()
|
|
.count()
|
|
)
|
|
|
|
# Pagination
|
|
paginator = Paginator(expenses, 10)
|
|
page_number = request.GET.get('page')
|
|
page_obj = paginator.get_page(page_number)
|
|
|
|
query_params = request.GET.copy()
|
|
query_params.pop('page', None)
|
|
|
|
# tags with state
|
|
tags_with_state = []
|
|
|
|
for tag in Tag.objects.filter(owner=request.user):
|
|
if tag.id in selected_tags:
|
|
new_tags = [t for t in selected_tags if t != tag.id]
|
|
active = True
|
|
else:
|
|
new_tags = selected_tags + [tag.id]
|
|
active = False
|
|
|
|
query = '&'.join([f'tag={t}' for t in new_tags])
|
|
|
|
tags_with_state.append({
|
|
'id': tag.id,
|
|
'name': tag.name,
|
|
'active': active,
|
|
'query': query,
|
|
})
|
|
|
|
advanced_filters_open = bool(
|
|
category or selected_tags
|
|
)
|
|
|
|
return render (
|
|
request,
|
|
'expenses/expense_list.html',
|
|
{
|
|
'active_menu': 'expenses',
|
|
'expenses': page_obj,
|
|
'page_obj': page_obj,
|
|
'selected_year': year,
|
|
'selected_month': month,
|
|
'selected_category': category,
|
|
'categories': categories,
|
|
'year_list': [y.year for y in year_list],
|
|
'months': months,
|
|
'kpi_total': total_amount,
|
|
'kpi_count': expense_count,
|
|
'kpi_categories': category_count,
|
|
'selected_tags': selected_tags,
|
|
'tags': Tag.objects.filter(owner=request.user),
|
|
'tags_with_state': tags_with_state,
|
|
'accounts': Account.objects.filter(owner=request.user),
|
|
'selected_account': account_id,
|
|
'advanced_filters_open' : advanced_filters_open,
|
|
'query_params': query_params.urlencode(),
|
|
},
|
|
)
|
|
|
|
|
|
@login_required
|
|
def expense_create(request):
|
|
if request.method == "POST":
|
|
form = ExpenseForm(request.POST, user=request.user)
|
|
if form.is_valid():
|
|
expense = form.save(commit=False)
|
|
expense.owner = request.user
|
|
expense.save()
|
|
form.save_m2m()
|
|
|
|
messages.success(request, 'Gasto creado correctamente.')
|
|
return redirect('expense_list')
|
|
else:
|
|
form = ExpenseForm(user=request.user)
|
|
|
|
return render(
|
|
request,
|
|
'expenses/expense_form.html',
|
|
{'active_menu': 'expenses','form': form},
|
|
)
|
|
|
|
|
|
@login_required
|
|
def expense_edit(request, pk):
|
|
# sourcery skip: assign-if-exp, merge-else-if-into-elif
|
|
expense = get_object_or_404(
|
|
Expense,
|
|
pk=pk,
|
|
owner=request.user,
|
|
)
|
|
|
|
if request.method == "POST":
|
|
form = ExpenseForm(
|
|
request.POST or None,
|
|
instance=expense,
|
|
user=request.user)
|
|
|
|
if form.is_valid():
|
|
expense = form.save()
|
|
messages.success(request, 'Gasto actualizado')
|
|
return redirect('expense_list')
|
|
else:
|
|
form = ExpenseForm(instance=expense, user=request.user)
|
|
|
|
return render(
|
|
request,
|
|
'expenses/expense_form.html', {
|
|
'active_menu': 'expenses',
|
|
'form': form,
|
|
}
|
|
)
|
|
|
|
|
|
@login_required
|
|
def expense_delete(request, pk):
|
|
expense = get_object_or_404(
|
|
Expense,
|
|
pk=pk,
|
|
owner=request.user,
|
|
)
|
|
|
|
if request.method == 'POST':
|
|
expense.delete()
|
|
messages.success(request, 'Gasto eliminado correctamente.')
|
|
return redirect('expense_list')
|
|
|
|
return render(
|
|
request,
|
|
'expenses/expense_confirm_delete.html',
|
|
{'active_menu': 'expenses','expense': expense},
|
|
)
|
|
|
|
|
|
@login_required
|
|
def dashboard(request):
|
|
# ------------------
|
|
# Filters
|
|
# ------------------
|
|
year = _get_int(request.GET.get('year'))
|
|
month = _get_int(request.GET.get('month'))
|
|
period = request.GET.get('period')
|
|
|
|
current_year = date.today().year
|
|
|
|
account_id = _get_int(request.GET.get('account'))
|
|
accounts = Account.objects.filter(
|
|
owner=request.user,
|
|
active=True,
|
|
)
|
|
|
|
selected_year = year or current_year
|
|
selected_month = month
|
|
|
|
compare_enabled = request.GET.get("compare") == "1"
|
|
|
|
# ------------------
|
|
# Queryset base
|
|
# -----------------
|
|
expenses = Expense.objects.filter(owner=request.user)
|
|
|
|
selected_account_obj = None
|
|
kpi_balance = None
|
|
if account_id:
|
|
expenses = expenses.filter(account_id=account_id)
|
|
selected_account_obj = accounts.filter(id=account_id).first()
|
|
kpi_balance = selected_account_obj.current_balance() if selected_account_obj else 0
|
|
else:
|
|
kpi_balance = sum(
|
|
account.current_balance() for account in accounts
|
|
)
|
|
|
|
today = date.today()
|
|
|
|
if period == 'this_month':
|
|
selected_year = today.year
|
|
selected_month = today.month
|
|
|
|
elif period == 'last_month':
|
|
if today.month == 1:
|
|
selected_year = today.year - 1
|
|
selected_month = 12
|
|
else:
|
|
selected_year = today.year
|
|
selected_month = today.month
|
|
|
|
elif period == 'this_year':
|
|
selected_year = today.year
|
|
selected_month = None
|
|
|
|
expenses_filtered = expenses.filter(date__year=selected_year)
|
|
|
|
if selected_month:
|
|
expenses_filtered = expenses_filtered.filter(date__month=selected_month)
|
|
|
|
total_amount = expenses_filtered.aggregate(
|
|
total=Sum('amount')
|
|
)['total'] or 0
|
|
|
|
expense_count = expenses_filtered.count()
|
|
|
|
category_count = (
|
|
expenses_filtered
|
|
.values('category')
|
|
.distinct()
|
|
.count()
|
|
)
|
|
|
|
# ------------------
|
|
# Totals by category
|
|
# -----------------
|
|
by_category = (
|
|
expenses_filtered
|
|
.values('category__name')
|
|
.annotate(total=Sum('amount'))
|
|
.order_by('category__name')
|
|
)
|
|
|
|
# ------------------
|
|
# Totals by month
|
|
# -----------------
|
|
|
|
by_month_qs = (
|
|
expenses_filtered
|
|
.annotate(month=ExtractMonth('date'))
|
|
.values('month')
|
|
.annotate(total=Sum('amount'))
|
|
)
|
|
|
|
month_totals = {
|
|
row['month']: float(row['total'])
|
|
for row in by_month_qs
|
|
}
|
|
|
|
months = list(range(1, 13))
|
|
|
|
by_month = [
|
|
{
|
|
'month': m,
|
|
'total': month_totals.get(m, 0),
|
|
}
|
|
for m in months
|
|
]
|
|
|
|
# ------------------
|
|
# Availables years
|
|
# -----------------
|
|
|
|
year_list = (
|
|
expenses
|
|
.annotate(year=ExtractYear('date'))
|
|
.values_list('year', flat=True)
|
|
.distinct()
|
|
.order_by('year')
|
|
)
|
|
|
|
# ------------------
|
|
# Chart
|
|
# -----------------
|
|
chart_labels = months
|
|
chart_totals = [row['total'] for row in by_month]
|
|
|
|
# ------------------
|
|
# Compare period
|
|
# -----------------
|
|
|
|
previous_total = None
|
|
kpi_difference = None
|
|
percentage = None
|
|
category_comparison = None
|
|
kpi_trend = None
|
|
kpi_difference_abs = None
|
|
|
|
if compare_enabled:
|
|
previous_expenses = Expense.objects.filter(owner=request.user)
|
|
if account_id:
|
|
previous_expenses.filter(account_id=account_id)
|
|
|
|
if selected_month:
|
|
# Monthly compare
|
|
if selected_month == 1:
|
|
prev_year = selected_year - 1
|
|
prev_month = 12
|
|
else:
|
|
prev_year = selected_year
|
|
prev_month = selected_month - 1
|
|
|
|
previous_expenses = previous_expenses.filter(
|
|
date__year=prev_year,
|
|
date__month=prev_month
|
|
)
|
|
else:
|
|
# Anual compare
|
|
prev_year = selected_year - 1
|
|
previous_expenses = previous_expenses.filter(
|
|
date__year=prev_year
|
|
)
|
|
|
|
previous_total = (
|
|
previous_expenses.aggregate(total=Sum('amount'))['total'] or 0
|
|
)
|
|
|
|
kpi_difference = total_amount - previous_total
|
|
|
|
if previous_total:
|
|
percentage = (kpi_difference / previous_total) * 100
|
|
|
|
kpi_trend = None
|
|
if kpi_difference is not None:
|
|
if kpi_difference > 0:
|
|
kpi_trend = 'up'
|
|
elif kpi_difference < 0:
|
|
kpi_trend = 'down'
|
|
else:
|
|
kpi_trend = 'equal'
|
|
kpi_difference_abs = abs(kpi_difference) if kpi_difference is not None else None
|
|
|
|
# ------------------
|
|
# Previous expenses by category
|
|
# ------------------
|
|
|
|
previous_by_category = (
|
|
previous_expenses
|
|
.values('category__name')
|
|
.annotate(total=Sum('amount'))
|
|
)
|
|
|
|
current_map = {
|
|
row['category__name']: row['total']
|
|
for row in by_category
|
|
}
|
|
|
|
previous_map = {
|
|
row['category__name']: row['total']
|
|
for row in previous_by_category
|
|
}
|
|
|
|
all_categories = set(current_map.keys()) | set(previous_map.keys())
|
|
|
|
category_comparison = []
|
|
for category in all_categories:
|
|
current_total = current_map.get(category, 0)
|
|
previous_total_cat = previous_map.get(category, 0)
|
|
|
|
difference = current_total - previous_total_cat
|
|
|
|
category_comparison.append({
|
|
'category': category,
|
|
'current': current_total,
|
|
'previous': previous_total_cat,
|
|
'difference': difference,
|
|
'difference_abs': abs(difference),
|
|
})
|
|
|
|
# ------------------
|
|
# Previous expenses by category
|
|
# ------------------
|
|
accounts_charts = []
|
|
for account in accounts:
|
|
monthly_data = account.monthly_balance(selected_year)
|
|
m_balance = [row['balance'] for row in monthly_data]
|
|
|
|
accounts_charts.append({
|
|
'id': account.id,
|
|
'name': account.name,
|
|
'data': m_balance,
|
|
'current_balance': account.current_balance(),
|
|
})
|
|
|
|
# Send the data to the dashboard
|
|
return render(request, 'expenses/dashboard.html', {
|
|
'active_menu': 'dashboard',
|
|
'by_category': by_category,
|
|
'by_month': by_month,
|
|
'chart_labels': chart_labels,
|
|
'chart_data': chart_totals,
|
|
'year_list': year_list,
|
|
'months': months,
|
|
'selected_year': selected_year,
|
|
'selected_month': selected_month,
|
|
'kpi_total': total_amount,
|
|
'kpi_count': expense_count,
|
|
'kpi_categories': category_count,
|
|
'compare_enabled': compare_enabled,
|
|
'kpi_previous_total': previous_total,
|
|
'kpi_difference': kpi_difference,
|
|
'kpi_difference_abs': kpi_difference_abs,
|
|
'kpi_percentage': percentage,
|
|
'category_comparison': category_comparison,
|
|
'kpi_trend': kpi_trend,
|
|
'accounts':accounts,
|
|
'selected_account': account_id,
|
|
'selected_account_obj': selected_account_obj,
|
|
'kpi_balance': kpi_balance,
|
|
'accounts_charts': accounts_charts,
|
|
'period':period,
|
|
})
|
|
|
|
|
|
@login_required
|
|
def tag_list(request):
|
|
tags = Tag.objects.filter(owner=request.user)
|
|
|
|
return render(
|
|
request,
|
|
'expenses/tag_list.html',
|
|
{'active_menu': 'tags','tags':tags}
|
|
)
|
|
|
|
|
|
@login_required
|
|
def tag_create(request):
|
|
if request.method == 'POST':
|
|
form = TagForm(request.POST)
|
|
if form.is_valid():
|
|
tag = form.save(commit=False)
|
|
tag.owner = request.user
|
|
tag.save()
|
|
messages.success(request, 'Etiqueta creada correctamente.')
|
|
return redirect('tag_list')
|
|
else:
|
|
form = TagForm()
|
|
|
|
return render(
|
|
request,
|
|
'expenses/tag_form.html',
|
|
{'active_menu': 'tags','form': form}
|
|
)
|
|
|
|
|
|
@login_required
|
|
def tag_edit(request, pk):
|
|
tag = get_object_or_404(
|
|
Tag,
|
|
pk=pk,
|
|
owner=request.user
|
|
)
|
|
|
|
if request.method == 'POST':
|
|
form = TagForm(request.POST, instance=tag)
|
|
if form.is_valid():
|
|
form.save()
|
|
messages.success(request, 'Etiqueta actualizada.')
|
|
return redirect('tag_list')
|
|
else:
|
|
form = TagForm(instance=tag)
|
|
|
|
return render(
|
|
request,
|
|
'expenses/tag_form.html',
|
|
{'active_menu': 'tags','form': form}
|
|
)
|
|
|
|
|
|
@login_required
|
|
def tag_delete(request, pk):
|
|
tag = get_object_or_404(
|
|
Tag,
|
|
pk=pk,
|
|
owner=request.user
|
|
)
|
|
|
|
if request.method == 'POST':
|
|
tag.delete()
|
|
messages.success(request, 'Etiqueta eliminada.')
|
|
return redirect('tag_list')
|
|
|
|
return render(
|
|
request,
|
|
'expenses/tag_confirm_delete.html',
|
|
{'active_menu': 'tags','tag': tag}
|
|
)
|
|
|
|
|
|
@login_required
|
|
def account_list(request):
|
|
accounts = Account.objects.filter(owner=request.user)
|
|
return render(
|
|
request,
|
|
'expenses/account_list.html',
|
|
{'active_menu': 'accounts','accounts': accounts}
|
|
)
|
|
|
|
|
|
@login_required
|
|
def account_create(request):
|
|
if request.method == 'POST':
|
|
form = AccountForm(request.POST)
|
|
if form.is_valid():
|
|
account = form.save(commit=False)
|
|
account.owner = request.user
|
|
account.save()
|
|
messages.success(request, 'Cuenta creada correctamente.')
|
|
return redirect('account_list')
|
|
else:
|
|
form = AccountForm()
|
|
|
|
return render(
|
|
request,
|
|
'expenses/account_form.html',
|
|
{'active_menu': 'accounts','form': form}
|
|
)
|
|
|
|
@login_required
|
|
def account_edit(request, pk):
|
|
account = get_object_or_404(
|
|
Account,
|
|
pk=pk,
|
|
owner=request.user
|
|
)
|
|
|
|
if request.method == 'POST':
|
|
form = AccountForm(request.POST, instance=account)
|
|
if form.is_valid():
|
|
form.save()
|
|
messages.success(request, 'Cuenta actualizada.')
|
|
return redirect('account_list')
|
|
else:
|
|
form = AccountForm(instance=account)
|
|
|
|
return render(
|
|
request,
|
|
'expenses/account_form.html',
|
|
{'active_menu': 'accounts','form': form}
|
|
)
|
|
|
|
@login_required
|
|
def account_delete(request, pk):
|
|
account = get_object_or_404(Account, pk=pk, owner=request.user)
|
|
|
|
if request.method == 'POST':
|
|
account.active = False
|
|
account.save()
|
|
messages.success(request, 'Cuenta eliminada.')
|
|
return redirect('account_list')
|
|
|
|
return render(
|
|
request,
|
|
'expenses/account_confirm_delete.html',
|
|
{'active_menu': 'accounts','account':account}
|
|
)
|
|
|
|
@login_required
|
|
def income_create(request):
|
|
if request.method == 'POST':
|
|
form = IncomeForm(request.POST, user=request.user)
|
|
if form.is_valid():
|
|
income = form.save(commit=False)
|
|
income.owner = request.user
|
|
income.save()
|
|
messages.success(request, 'Ingreso creado correctamente.')
|
|
return redirect('income_list')
|
|
else:
|
|
form = IncomeForm(user=request.user)
|
|
|
|
return render(
|
|
request,
|
|
'expenses/income_form.html',
|
|
{'active_menu': 'incomes','form': form}
|
|
)
|
|
|
|
@login_required
|
|
def income_list(request):
|
|
incomes = Income.objects.filter(owner=request.user)
|
|
|
|
return render(
|
|
request,
|
|
'expenses/income_list.html',
|
|
{'active_menu': 'incomes','incomes': incomes}
|
|
)
|
|
|
|
@login_required
|
|
def income_edit(request, pk):
|
|
income = get_object_or_404(
|
|
Income,
|
|
pk=pk,
|
|
owner=request.user
|
|
)
|
|
|
|
if request.method == 'POST':
|
|
form = IncomeForm(request.POST, instance=income, user=request.user)
|
|
if form.is_valid():
|
|
form.save()
|
|
messages.success(request, 'Ingreso actualizado.')
|
|
return redirect('income_list')
|
|
else:
|
|
form = IncomeForm(instance=income, user=request.user)
|
|
|
|
return render(
|
|
request,
|
|
'expenses/income_form.html',
|
|
{'active_menu': 'incomes','form': form}
|
|
)
|
|
|
|
@login_required
|
|
def income_delete(request, pk):
|
|
income = get_object_or_404(Income, pk=pk, owner=request.user)
|
|
|
|
if request.method == 'POST':
|
|
income.delete()
|
|
messages.success(request, 'Ingreso eliminado.')
|
|
return redirect('income_list')
|
|
|
|
return render(
|
|
request,
|
|
'expenses/income_confirm_delete.html',
|
|
{'active_menu': 'incomes','income':income}
|
|
)
|
|
|
|
@login_required
|
|
def fuel_create(request):
|
|
if request.method == 'POST':
|
|
form = FuelEntryForm(request.POST, user=request.user)
|
|
|
|
if form.is_valid():
|
|
expense = Expense.objects.create(
|
|
owner=request.user,
|
|
date=form.cleaned_data['date'],
|
|
amount=form.cleaned_data['amount'],
|
|
account=form.cleaned_data['account'],
|
|
category=Category.objects.get(slug='gasolina'),
|
|
description='Repostaje',
|
|
)
|
|
|
|
FuelEntry.objects.create(
|
|
expense=expense,
|
|
odometer=form.cleaned_data['odometer'],
|
|
liters=form.cleaned_data['liters'],
|
|
)
|
|
|
|
return redirect('fuel_list')
|
|
|
|
else:
|
|
form = FuelEntryForm(user=request.user)
|
|
|
|
return render(request, 'fuel/create.html',{
|
|
'active_menu': 'fuel',
|
|
'form': form
|
|
})
|
|
|
|
@login_required
|
|
def fuel_list(request):
|
|
selected_year = request.GET.get('year')
|
|
|
|
current_year = datetime.now().year
|
|
|
|
selected_year = int(selected_year) if selected_year else current_year
|
|
fuels = FuelEntry.objects.filter(
|
|
expense__owner=request.user,
|
|
expense__date__year=selected_year
|
|
).select_related('expense').order_by('expense__date')
|
|
|
|
monthly_expenses = (
|
|
fuels
|
|
.annotate(month=ExtractMonth('expense__date'))
|
|
.values('month')
|
|
.annotate(total=Sum('expense__amount'))
|
|
)
|
|
|
|
month_map = {m['month']: float(m['total']) for m in monthly_expenses}
|
|
|
|
monthly_data = [
|
|
month_map.get(month, 0)
|
|
for month in range(1, 13)
|
|
]
|
|
|
|
year_list = (
|
|
FuelEntry.objects.filter(
|
|
expense__owner=request.user,)
|
|
.annotate(year=ExtractYear('expense__date'))
|
|
.values_list('year', flat=True)
|
|
.distinct()
|
|
.order_by('year')
|
|
)
|
|
|
|
km_data = []
|
|
dates = []
|
|
|
|
for fuel in fuels:
|
|
km = fuel.km_since_previous()
|
|
if km:
|
|
km_data.append(km)
|
|
dates.append(fuel.expense.date.strftime('%Y-%m-%d'))
|
|
|
|
return render( request, 'fuel/list.html',
|
|
{
|
|
'active_menu': 'fuel',
|
|
'fuels': fuels,
|
|
'monthly_data': monthly_data,
|
|
'km_data': km_data,
|
|
'km_dates': dates,
|
|
'selected_year': selected_year,
|
|
'year_list': year_list,
|
|
}
|
|
)
|
|
|
|
@login_required
|
|
def fuel_edit(request, pk):
|
|
expense = get_object_or_404(
|
|
Expense,
|
|
pk=pk,
|
|
owner=request.user,
|
|
)
|
|
|
|
fuel = get_object_or_404(FuelEntry, expense=expense)
|
|
|
|
if request.method == "POST":
|
|
form = FuelEntryForm(
|
|
request.POST,
|
|
user=request.user
|
|
)
|
|
|
|
if form.is_valid():
|
|
# Update expense
|
|
expense.date = form.cleaned_data['date']
|
|
expense.amount = form.cleaned_data['amount']
|
|
expense.account = form.cleaned_data['account']
|
|
expense.description = 'Repostaje'
|
|
expense.save()
|
|
|
|
# Update FuelEntry
|
|
fuel.odometer = form.cleaned_data['odometer']
|
|
fuel.liters = form.cleaned_data['liters']
|
|
fuel.save()
|
|
|
|
return redirect('expense_list')
|
|
else:
|
|
fuel = expense.fuel_data
|
|
# Initialize manually
|
|
form = FuelEntryForm(
|
|
initial={
|
|
'date': expense.date,
|
|
'amount': expense.amount,
|
|
'account': expense.account,
|
|
'odometer': fuel.odometer,
|
|
'liters': fuel.liters
|
|
},
|
|
user=request.user
|
|
)
|
|
|
|
return render(
|
|
request, 'fuel/create.html', {
|
|
'active_menu': 'expenses',
|
|
'form': form,
|
|
'editing': True,
|
|
}
|
|
)
|
|
|
|
@login_required
|
|
def category_list(request):
|
|
categories = Category.objects.filter(
|
|
owner=request.user
|
|
)
|
|
|
|
if request.method == "POST":
|
|
form = CategoryForm(request.POST)
|
|
if form.is_valid():
|
|
category = form.save(commit=False)
|
|
category.owner = request.user
|
|
category.save()
|
|
return redirect('category_list')
|
|
else:
|
|
form = CategoryForm()
|
|
|
|
return render(request, 'categories/list.html', {
|
|
'active_menu': 'categories',
|
|
'categories': categories,
|
|
'form': form,
|
|
})
|
|
|