From a15f836e076359a0284185b2311c30e334de971f Mon Sep 17 00:00:00 2001 From: adrigongv23 Date: Tue, 30 Jun 2026 19:55:37 +0200 Subject: [PATCH] =?UTF-8?q?Secci=C3=B3n=20de=20contabilida=20a=C3=B1adida?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migrations/0003_factura_estado.py | 18 ++ Plataform_Web/documentos/models.py | 7 + Plataform_Web/gades_manager/urls.py | 5 + Plataform_Web/gestion/forms.py | 24 ++ Plataform_Web/gestion/views.py | 136 +++++++++- Plataform_Web/templates/base.html | 2 +- Plataform_Web/templates/contabilidad.html | 247 ++++++++++++++++++ 7 files changed, 430 insertions(+), 9 deletions(-) create mode 100644 Plataform_Web/documentos/migrations/0003_factura_estado.py create mode 100644 Plataform_Web/gestion/forms.py create mode 100644 Plataform_Web/templates/contabilidad.html diff --git a/Plataform_Web/documentos/migrations/0003_factura_estado.py b/Plataform_Web/documentos/migrations/0003_factura_estado.py new file mode 100644 index 0000000..0d89bea --- /dev/null +++ b/Plataform_Web/documentos/migrations/0003_factura_estado.py @@ -0,0 +1,18 @@ +# Generated by Django 6.0.2 on 2026-06-30 16:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('documentos', '0002_factura_alter_documento_options_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='factura', + name='estado', + field=models.CharField(choices=[('pendiente', 'Pendiente'), ('aceptada', 'Aceptada'), ('rechazada', 'Rechazada')], default='pendiente', max_length=20), + ), + ] diff --git a/Plataform_Web/documentos/models.py b/Plataform_Web/documentos/models.py index 0d8c6c9..2703e84 100644 --- a/Plataform_Web/documentos/models.py +++ b/Plataform_Web/documentos/models.py @@ -62,11 +62,18 @@ class Documento(models.Model): os.remove(self.archivo.path) super().delete(*args, **kwargs) +ESTADO_FACTURA = [ + ('pendiente', 'Pendiente'), + ('aceptada', 'Aceptada'), + ('rechazada', 'Rechazada'), +] + # Herencia de la clase Factura: Factura hereda de Documento class Factura(Documento): # Al heredar de Documento, ya tiene nombre, archivo, categoria, etc. empresa = models.CharField(max_length=100, verbose_name="Nombre de la empresa") importe = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="Importe (€)") + estado = models.CharField(max_length=20, choices=ESTADO_FACTURA, default='pendiente') class Meta: verbose_name = "Factura" diff --git a/Plataform_Web/gades_manager/urls.py b/Plataform_Web/gades_manager/urls.py index a181953..0844684 100644 --- a/Plataform_Web/gades_manager/urls.py +++ b/Plataform_Web/gades_manager/urls.py @@ -36,4 +36,9 @@ urlpatterns = [ path('gestion/temporadas/crear/', temporadas_views.crear_temporada, name='crear_temporada'), path('gestion/temporadas//editar/', temporadas_views.editar_temporada, name='editar_temporada'), path('gestion/temporadas//eliminar/', temporadas_views.eliminar_temporada, name='eliminar_temporada'), + path('gestion/contabilidad/', views.contabilidad, name='contabilidad'), + path('gestion/contabilidad/gasto/anadir/', views.anadir_gasto, name='anadir_gasto'), + path('gestion/contabilidad/ingreso/anadir/', views.anadir_ingreso, name='anadir_ingreso'), + path('gestion/contabilidad/factura//aceptar/', views.aceptar_factura, name='aceptar_factura'), + path('gestion/contabilidad/factura//rechazar/', views.rechazar_factura, name='rechazar_factura'), ] \ No newline at end of file diff --git a/Plataform_Web/gestion/forms.py b/Plataform_Web/gestion/forms.py new file mode 100644 index 0000000..599c39c --- /dev/null +++ b/Plataform_Web/gestion/forms.py @@ -0,0 +1,24 @@ +from django import forms +from .models import Gasto, Ingreso + + +class GastoForm(forms.ModelForm): + class Meta: + model = Gasto + fields = ['concepto', 'importe', 'categoria', 'observaciones'] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + for field in self.fields.values(): + field.widget.attrs['class'] = 'form-control' + + +class IngresoForm(forms.ModelForm): + class Meta: + model = Ingreso + fields = ['concepto', 'importe', 'categoria', 'observaciones'] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + for field in self.fields.values(): + field.widget.attrs['class'] = 'form-control' diff --git a/Plataform_Web/gestion/views.py b/Plataform_Web/gestion/views.py index 692bc32..9b99dff 100644 --- a/Plataform_Web/gestion/views.py +++ b/Plataform_Web/gestion/views.py @@ -1,14 +1,134 @@ -from django.shortcuts import render +from django.shortcuts import render, redirect, get_object_or_404 +from django.contrib import messages from django.contrib.auth.decorators import login_required +from django.views.decorators.http import require_POST +from django.db.models import Sum +from datetime import date + from temporadas.models import Temporada +from documentos.models import Factura +from users.decorators import require_rol +from .models import Gasto, Ingreso +from .forms import GastoForm, IngresoForm + +# Categorías válidas en Gasto (Documento usa 'normativa' en su lugar de 'general') +_CATEGORIAS_GASTO_VALIDAS = {c[0] for c in Gasto.CATEGORIAS_GASTOS} + @login_required def inicio(request): - # Buscamos la temporada que configuraste como actual temporada_activa = Temporada.objects.filter(actual=True).first() - - context = { - 'temporada_actual': temporada_activa - } - # Renderiza index.html, el cual hereda automáticamente de base.html - return render(request, 'index.html', context) \ No newline at end of file + return render(request, 'index.html', {'temporada_actual': temporada_activa}) + + +@require_rol('directiva') +def contabilidad(request): + temporada_actual = Temporada.objects.filter(actual=True).first() + + gastos = [] + ingresos = [] + facturas_pendientes = [] + total_gastos = 0 + total_ingresos = 0 + presupuesto_inicial = 0 + presupuesto_actual = 0 + + if temporada_actual: + gastos = Gasto.objects.filter(temporada=temporada_actual) + ingresos = Ingreso.objects.filter(temporada=temporada_actual) + facturas_pendientes = Factura.objects.filter( + temporada=temporada_actual, estado='pendiente' + ) + + total_gastos = gastos.aggregate(total=Sum('importe'))['total'] or 0 + total_ingresos = ingresos.aggregate(total=Sum('importe'))['total'] or 0 + presupuesto_inicial = temporada_actual.presupuesto + presupuesto_actual = presupuesto_inicial - total_gastos + total_ingresos + + return render(request, 'contabilidad.html', { + 'temporada_actual': temporada_actual, + 'gastos': gastos, + 'ingresos': ingresos, + 'facturas_pendientes': facturas_pendientes, + 'total_gastos': total_gastos, + 'total_ingresos': total_ingresos, + 'presupuesto_inicial': presupuesto_inicial, + 'presupuesto_actual': presupuesto_actual, + 'gasto_form': GastoForm(), + 'ingreso_form': IngresoForm(), + }) + + +@require_rol('directiva') +@require_POST +def anadir_gasto(request): + temporada_actual = Temporada.objects.filter(actual=True).first() + if not temporada_actual: + messages.error(request, 'No hay temporada activa.') + return redirect('contabilidad') + + form = GastoForm(request.POST) + if form.is_valid(): + gasto = form.save(commit=False) + gasto.fecha = date.today() + gasto.temporada = temporada_actual + gasto.save() + messages.success(request, 'Gasto añadido correctamente.') + else: + messages.error(request, 'Error al añadir el gasto. Revisa los datos.') + return redirect('contabilidad') + + +@require_rol('directiva') +@require_POST +def anadir_ingreso(request): + temporada_actual = Temporada.objects.filter(actual=True).first() + if not temporada_actual: + messages.error(request, 'No hay temporada activa.') + return redirect('contabilidad') + + form = IngresoForm(request.POST) + if form.is_valid(): + ingreso = form.save(commit=False) + ingreso.fecha = date.today() + ingreso.temporada = temporada_actual + ingreso.save() + messages.success(request, 'Ingreso añadido correctamente.') + else: + messages.error(request, 'Error al añadir el ingreso. Revisa los datos.') + return redirect('contabilidad') + + +@require_rol('directiva') +@require_POST +def aceptar_factura(request, pk): + factura = get_object_or_404(Factura, pk=pk) + temporada_actual = Temporada.objects.filter(actual=True).first() + if not temporada_actual: + messages.error(request, 'No hay temporada activa.') + return redirect('contabilidad') + + categoria = factura.categoria if factura.categoria in _CATEGORIAS_GASTO_VALIDAS else 'general' + Gasto.objects.create( + concepto=factura.nombre, + importe=factura.importe, + fecha=date.today(), + categoria=categoria, + temporada=temporada_actual, + observaciones=f'Factura de {factura.empresa}', + doc_justificativo=factura.archivo, + ) + factura.estado = 'aceptada' + factura.save() + messages.success(request, f'Factura de {factura.empresa} aceptada y registrada como gasto.') + return redirect('contabilidad') + + +@require_rol('directiva') +@require_POST +def rechazar_factura(request, pk): + factura = get_object_or_404(Factura, pk=pk) + factura.estado = 'rechazada' + factura.save() + messages.success(request, f'Factura de {factura.empresa} rechazada.') + return redirect('contabilidad') diff --git a/Plataform_Web/templates/base.html b/Plataform_Web/templates/base.html index beb1863..161e082 100644 --- a/Plataform_Web/templates/base.html +++ b/Plataform_Web/templates/base.html @@ -58,7 +58,7 @@ {% if user.is_authenticated and user.rol == 'directiva' %}