diff --git a/expenses_manager/expenses/templates/categories/confirm_delete.html b/expenses_manager/expenses/templates/categories/confirm_delete.html
new file mode 100644
index 0000000..87b4140
--- /dev/null
+++ b/expenses_manager/expenses/templates/categories/confirm_delete.html
@@ -0,0 +1,38 @@
+{% extends "expenses/base.html" %}
+
+{% block title %}
+ Categorías
+{% endblock %}
+
+{% block content %}
+
Eliminar categoría
+
+ ¿Seguro que quieres eliminar la categoría {{ category.name }}?
+
+ {% if expense_count %}
+
+ Esta categoría tiene {{ expense_count }} gasto{{ expense_count|pluralize }} asociado{{ expense_count|pluralize }}
+ y no se puede eliminar. Reasigna esos gastos a otra categoría primero.
+
+ {% else %}
+ {% if children %}
+
+ Atención: también se eliminarán sus subcategorías:
+ {% for child in children %}{{child.name}}{% if not forloop.last %}, {% endif %}{% endfor %}.
+
+ {% endif %}
+
+ {% if goal_count %}
+
+ Atención: se eliminarán también {{ goal_count }} objetivo{{ goal_count|pluralize }} asociado{{ goal_count|pluralize }} a esta categoría.
+
+ {% endif %}
+ {% endif %}
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/expenses_manager/expenses/templates/categories/form.html b/expenses_manager/expenses/templates/categories/form.html
new file mode 100644
index 0000000..e45d9c1
--- /dev/null
+++ b/expenses_manager/expenses/templates/categories/form.html
@@ -0,0 +1,16 @@
+{% extends "expenses/base.html" %}
+
+{% block title %}
+ Categorías
+{% endblock %}
+
+{% block content %}
+ Editar categoría
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/expenses_manager/expenses/templates/categories/list.html b/expenses_manager/expenses/templates/categories/list.html
index f89d645..24826ab 100644
--- a/expenses_manager/expenses/templates/categories/list.html
+++ b/expenses_manager/expenses/templates/categories/list.html
@@ -30,6 +30,10 @@
| {{ category.name }} |
{% if category.parent %}{{ category.parent.name }}{% endif %} |
+
+ Editar
+ Eliminar
+ |
{% endfor %}
diff --git a/expenses_manager/expenses/urls.py b/expenses_manager/expenses/urls.py
index 47e1f56..ad395e9 100644
--- a/expenses_manager/expenses/urls.py
+++ b/expenses_manager/expenses/urls.py
@@ -24,6 +24,8 @@ urlpatterns = [
path('fuel/create/', views.fuel_create, name='fuel_create'),
path('fuel//edit/', views.fuel_edit, name='fuel_edit'),
path('categories/', views.category_list, name='category_list'),
+ path('categories//edit/', views.category_edit, name='category_edit'),
+ path('categories//delete/', views.category_delete, name='category_delete'),
path('settings/', views.settings_index, name='settings_index'),
path('goals/', views.goal_list, name='goal_list'),
path('goals/new/', views.goal_create, name='goal_create'),
diff --git a/expenses_manager/expenses/views.py b/expenses_manager/expenses/views.py
index ecd3f04..69817c0 100644
--- a/expenses_manager/expenses/views.py
+++ b/expenses_manager/expenses/views.py
@@ -13,8 +13,8 @@ from .forms import (
GoalForm,
)
-from django.db.models import Sum
from django.core.paginator import Paginator
+from django.db.models import Sum, ProtectedError
from django.db.models.functions import ExtractMonth, ExtractYear, ExtractDay
from django.contrib.auth.decorators import login_required
@@ -859,6 +859,60 @@ def category_list(request):
)
+@login_required
+def category_edit(request, pk):
+ category = get_object_or_404(Category, pk=pk, owner=request.user)
+
+ if request.method == "POST":
+ form = CategoryForm(request.POST, instance=category, user=request.user)
+ if form.is_valid():
+ form.save()
+ messages.success(request, "Categoría actualizada")
+ return redirect("category_list")
+ else:
+ form = CategoryForm(instance=category, user=request.user)
+
+ return render(
+ request,
+ "categories/form.html",
+ {
+ "active_menu": "settings",
+ "form":form,
+ "category": category,
+ }
+ )
+
+
+@login_required
+def category_delete(request, pk):
+ category = get_object_or_404(Category, pk=pk, owner=request.user)
+
+ if request.method == "POST":
+ try:
+ category.delete()
+ except ProtectedError:
+ messages.error(
+ request,
+ f"No se puede eliminar «{category.name}»; tiene gastos asociados. "
+ "Reasigna esos gastos a otra categoría antes de borrarla."
+ )
+ else:
+ messages.success(request, "Categoría eliminada.")
+ return redirect("category_list")
+
+ return render(
+ request,
+ "categories/confirm_delete.html",
+ {
+ "active_menu": "settings",
+ "category": category,
+ "children": category.children.all(),
+ "expense_count": category.expenses.count(),
+ "goal_count": category.goal_set.count(),
+ },
+ )
+
+
@login_required
def goal_list(request):
goals = Goal.objects.filter(owner=request.user)