WIP in the new feature 'Account'
This commit is contained in:
parent
abd07919d4
commit
e8c15c1eff
@ -1,5 +1,5 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
from .models import Expense, Category, Tag
|
from .models import Expense, Category, Tag, Account
|
||||||
|
|
||||||
class ExpenseForm(forms.ModelForm):
|
class ExpenseForm(forms.ModelForm):
|
||||||
|
|
||||||
@ -12,6 +12,7 @@ class ExpenseForm(forms.ModelForm):
|
|||||||
if user:
|
if user:
|
||||||
self.fields['category'].queryset = Category.objects.filter(owner=user)
|
self.fields['category'].queryset = Category.objects.filter(owner=user)
|
||||||
self.fields['tags'].queryset = Tag.objects.filter(owner=user)
|
self.fields['tags'].queryset = Tag.objects.filter(owner=user)
|
||||||
|
self.fields['account'].queryset = Account.objects(owner=user, active=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Expense
|
model = Expense
|
||||||
@ -31,4 +32,10 @@ class ExpenseForm(forms.ModelForm):
|
|||||||
class TagForm(forms.ModelForm):
|
class TagForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Tag
|
model = Tag
|
||||||
fields = ['name']
|
fields = ['name']
|
||||||
|
|
||||||
|
class AccountForm(forms.ModelForm):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Account
|
||||||
|
fields = ['name', 'initial_balance', 'active']
|
||||||
@ -0,0 +1,65 @@
|
|||||||
|
# Generated by Django 5.2.10 on 2026-01-30 20:12
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
import django.utils.timezone
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('expenses', '0001_initial'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='account',
|
||||||
|
options={'ordering': ['name']},
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='category',
|
||||||
|
unique_together=set(),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='account',
|
||||||
|
name='active',
|
||||||
|
field=models.BooleanField(default=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='account',
|
||||||
|
name='created_at',
|
||||||
|
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='account',
|
||||||
|
name='initial_balance',
|
||||||
|
field=models.DecimalField(decimal_places=2, default=0, max_digits=12),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='category',
|
||||||
|
name='owner',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='categories', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='account',
|
||||||
|
name='owner',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='accounts', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='expense',
|
||||||
|
name='account',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='expenses', to='expenses.account'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='expense',
|
||||||
|
name='category',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='expenses', to='expenses.category'),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='category',
|
||||||
|
unique_together={('name', 'parent', 'owner')},
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -1,21 +0,0 @@
|
|||||||
# Generated by Django 5.2.10 on 2026-01-09 08:42
|
|
||||||
|
|
||||||
import django.db.models.deletion
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('expenses', '0001_initial'),
|
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='category',
|
|
||||||
name='parent',
|
|
||||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to=settings.AUTH_USER_MODEL),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
# Generated by Django 5.2.10 on 2026-01-09 09:27
|
|
||||||
|
|
||||||
import django.db.models.deletion
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('expenses', '0002_alter_category_parent'),
|
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterUniqueTogether(
|
|
||||||
name='category',
|
|
||||||
unique_together=set(),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='category',
|
|
||||||
name='owner',
|
|
||||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='categories', to=settings.AUTH_USER_MODEL),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='category',
|
|
||||||
name='parent',
|
|
||||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='expenses.category'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='expense',
|
|
||||||
name='category',
|
|
||||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='expenses', to='expenses.category'),
|
|
||||||
),
|
|
||||||
migrations.AlterUniqueTogether(
|
|
||||||
name='category',
|
|
||||||
unique_together={('name', 'parent', 'owner')},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
# Generated by Django 5.2.10 on 2026-01-09 09:50
|
|
||||||
|
|
||||||
import django.db.models.deletion
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('expenses', '0003_alter_category_unique_together_category_owner_and_more'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='expense',
|
|
||||||
name='category',
|
|
||||||
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='expenses', to='expenses.category'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
# Generated by Django 5.2.10 on 2026-01-09 10:03
|
|
||||||
|
|
||||||
import django.db.models.deletion
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('expenses', '0004_alter_expense_category'),
|
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='category',
|
|
||||||
name='owner',
|
|
||||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='categories', to=settings.AUTH_USER_MODEL),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -26,14 +26,25 @@ class Category(models.Model):
|
|||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
class Account(models.Model):
|
class Account(models.Model):
|
||||||
name = models.CharField(max_length=100)
|
|
||||||
owner = models.ForeignKey(
|
owner = models.ForeignKey(
|
||||||
settings.AUTH_USER_MODEL,
|
settings.AUTH_USER_MODEL,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name="accounts",
|
related_name="accounts",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
name = models.CharField(max_length=100)
|
||||||
|
initial_balance = models.DecimalField(
|
||||||
|
max_digits=12,
|
||||||
|
decimal_places=2,
|
||||||
|
default=0
|
||||||
|
)
|
||||||
|
active = models.BooleanField(default=True)
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ['name']
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
@ -72,9 +83,7 @@ class Expense(models.Model):
|
|||||||
|
|
||||||
account = models.ForeignKey(
|
account = models.ForeignKey(
|
||||||
Account,
|
Account,
|
||||||
on_delete=models.SET_NULL,
|
on_delete=models.PROTECT,
|
||||||
null=True,
|
|
||||||
blank=True,
|
|
||||||
related_name="expenses",
|
related_name="expenses",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -45,6 +45,17 @@
|
|||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
<select name="account">
|
||||||
|
<option value="">Cuenta</option>
|
||||||
|
{% for acc in accounts %}
|
||||||
|
<option value="{{ acc.id }}"
|
||||||
|
{% if selected_account == acc.id %}selected{% endif %}
|
||||||
|
>
|
||||||
|
{{ acc.name }}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
|
||||||
<label for="tag">Filtrar por tags:</label>
|
<label for="tag">Filtrar por tags:</label>
|
||||||
<div class="active-tags">
|
<div class="active-tags">
|
||||||
{% for tag in tags_with_state %}
|
{% for tag in tags_with_state %}
|
||||||
@ -77,6 +88,7 @@
|
|||||||
<th>Fecha</th>
|
<th>Fecha</th>
|
||||||
<th>Categoría</th>
|
<th>Categoría</th>
|
||||||
<th>Importe</th>
|
<th>Importe</th>
|
||||||
|
<th>Cuenta</th>
|
||||||
<th>Etiquetas</th>
|
<th>Etiquetas</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
@ -87,6 +99,7 @@
|
|||||||
<td>{{ expense.date }}</td>
|
<td>{{ expense.date }}</td>
|
||||||
<td>{{ expense.category.name }}</td>
|
<td>{{ expense.category.name }}</td>
|
||||||
<td>{{ expense.amount }}</td>
|
<td>{{ expense.amount }}</td>
|
||||||
|
<td>{{ expense.account.name }}</td>
|
||||||
<td>
|
<td>
|
||||||
{% for tag in expense.tags.all %}
|
{% for tag in expense.tags.all %}
|
||||||
<span class="tag">
|
<span class="tag">
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
from datetime import date
|
from datetime import date
|
||||||
from operator import truediv
|
from operator import truediv
|
||||||
from .forms import ExpenseForm, TagForm
|
from .forms import ExpenseForm, TagForm
|
||||||
from .models import Category, Expense, Tag
|
from .models import Account, Category, Expense, Tag
|
||||||
# from dateutli.relativedelta import relativedelta
|
# from dateutli.relativedelta import relativedelta
|
||||||
|
|
||||||
from django.db.models import Sum
|
from django.db.models import Sum
|
||||||
@ -115,6 +115,7 @@ def expense_list(request):
|
|||||||
year = _get_int(request.GET.get('year'))
|
year = _get_int(request.GET.get('year'))
|
||||||
month = _get_int(request.GET.get('month'))
|
month = _get_int(request.GET.get('month'))
|
||||||
category = _get_int(request.GET.get('category'))
|
category = _get_int(request.GET.get('category'))
|
||||||
|
account_id = _get_int(request.GET.get('account'))
|
||||||
|
|
||||||
tag_ids = request.GET.getlist('tag')
|
tag_ids = request.GET.getlist('tag')
|
||||||
tag_ids = [int(t) for t in tag_ids]
|
tag_ids = [int(t) for t in tag_ids]
|
||||||
@ -131,6 +132,9 @@ def expense_list(request):
|
|||||||
if tag_ids:
|
if tag_ids:
|
||||||
expenses = expenses.filter(tags__id__in=tag_ids).distinct()
|
expenses = expenses.filter(tags__id__in=tag_ids).distinct()
|
||||||
|
|
||||||
|
if account_id:
|
||||||
|
expenses = expense.filter(account_id=account_id)
|
||||||
|
|
||||||
selected_tags = tag_ids or []
|
selected_tags = tag_ids or []
|
||||||
|
|
||||||
expenses = expenses.order_by('-date')
|
expenses = expenses.order_by('-date')
|
||||||
@ -190,6 +194,8 @@ def expense_list(request):
|
|||||||
'selected_tags': selected_tags,
|
'selected_tags': selected_tags,
|
||||||
'tags': Tag.objects.filter(owner=request.user),
|
'tags': Tag.objects.filter(owner=request.user),
|
||||||
'tags_with_state': tags_with_state,
|
'tags_with_state': tags_with_state,
|
||||||
|
'accounts': Account.object.filter(owner=request.user),
|
||||||
|
'selected_account': account_id,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -549,3 +555,36 @@ def tag_delete(request, pk):
|
|||||||
'expenses/tag_confirm_delete.html',
|
'expenses/tag_confirm_delete.html',
|
||||||
{'tag': tag}
|
{'tag': tag}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def account_list(request):
|
||||||
|
accounts = Account.objects.filter(owner=request.user)
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
'expenses/account_list.html',
|
||||||
|
{'account': 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()
|
||||||
|
return redirect('account_list')
|
||||||
|
else:
|
||||||
|
form = AccountForm()
|
||||||
|
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
'expenses/account_form.html',
|
||||||
|
{'form': form}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user