views.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. from django.contrib import messages
  2. from django.shortcuts import redirect, render
  3. from django.urls import reverse_lazy
  4. from django.views.generic import CreateView, DeleteView, ListView, UpdateView
  5. from .forms import DividendoForm, OperacionForm, ValorForm
  6. from .models import Dividendo, Operacion, Valor
  7. from .services import actualizar_precios, desglose_fifo, posiciones, resumen_fiscal, totales
  8. class CancelarUrlMixin:
  9. """Aporta la URL de cancelación al formulario (vuelve atrás si hay referer)."""
  10. def get_context_data(self, **kwargs):
  11. ctx = super().get_context_data(**kwargs)
  12. ctx['cancelar_url'] = self.request.META.get('HTTP_REFERER') or str(self.success_url)
  13. return ctx
  14. def dashboard(request):
  15. pos = posiciones()
  16. context = {
  17. 'posiciones': pos,
  18. 'abiertas': [p for p in pos if p['acciones'] > 0],
  19. 'cerradas': [p for p in pos if p['acciones'] == 0],
  20. 'totales': totales(pos),
  21. }
  22. return render(request, 'cartera/dashboard.html', context)
  23. def actualizar(request):
  24. if request.method == 'POST':
  25. correctos, errores = actualizar_precios()
  26. if correctos:
  27. messages.success(request, f'Precios actualizados: {correctos}')
  28. for ticker, motivo in errores:
  29. messages.warning(request, f'{ticker}: {motivo}')
  30. return redirect('cartera:dashboard')
  31. def fifo(request):
  32. filas = desglose_fifo()
  33. context = {
  34. 'filas': filas,
  35. 'fiscal': resumen_fiscal(filas),
  36. 'total': sum((f['resultado'] or 0 for f in filas), start=0),
  37. }
  38. return render(request, 'cartera/fifo.html', context)
  39. class OperacionListView(ListView):
  40. model = Operacion
  41. template_name = 'cartera/operacion_list.html'
  42. context_object_name = 'operaciones'
  43. queryset = Operacion.objects.select_related('valor')
  44. class OperacionCreateView(CancelarUrlMixin, CreateView):
  45. model = Operacion
  46. form_class = OperacionForm
  47. template_name = 'cartera/form.html'
  48. success_url = reverse_lazy('cartera:operaciones')
  49. titulo = 'Nueva operación'
  50. def form_valid(self, form):
  51. messages.success(self.request, 'Operación registrada')
  52. return super().form_valid(form)
  53. class OperacionUpdateView(CancelarUrlMixin, UpdateView):
  54. model = Operacion
  55. form_class = OperacionForm
  56. template_name = 'cartera/form.html'
  57. success_url = reverse_lazy('cartera:operaciones')
  58. titulo = 'Editar operación'
  59. def form_valid(self, form):
  60. messages.success(self.request, 'Operación actualizada')
  61. return super().form_valid(form)
  62. class OperacionDeleteView(DeleteView):
  63. model = Operacion
  64. template_name = 'cartera/confirm_delete.html'
  65. success_url = reverse_lazy('cartera:operaciones')
  66. titulo = 'Eliminar operación'
  67. def form_valid(self, form):
  68. messages.success(self.request, 'Operación eliminada')
  69. return super().form_valid(form)
  70. class DividendoListView(ListView):
  71. model = Dividendo
  72. template_name = 'cartera/dividendo_list.html'
  73. context_object_name = 'dividendos'
  74. queryset = Dividendo.objects.select_related('valor')
  75. class DividendoCreateView(CancelarUrlMixin, CreateView):
  76. model = Dividendo
  77. form_class = DividendoForm
  78. template_name = 'cartera/form.html'
  79. success_url = reverse_lazy('cartera:dividendos')
  80. titulo = 'Nuevo dividendo'
  81. def form_valid(self, form):
  82. messages.success(self.request, 'Dividendo registrado')
  83. return super().form_valid(form)
  84. class DividendoUpdateView(CancelarUrlMixin, UpdateView):
  85. model = Dividendo
  86. form_class = DividendoForm
  87. template_name = 'cartera/form.html'
  88. success_url = reverse_lazy('cartera:dividendos')
  89. titulo = 'Editar dividendo'
  90. def form_valid(self, form):
  91. messages.success(self.request, 'Dividendo actualizado')
  92. return super().form_valid(form)
  93. class DividendoDeleteView(DeleteView):
  94. model = Dividendo
  95. template_name = 'cartera/confirm_delete.html'
  96. success_url = reverse_lazy('cartera:dividendos')
  97. titulo = 'Eliminar dividendo'
  98. def form_valid(self, form):
  99. messages.success(self.request, 'Dividendo eliminado')
  100. return super().form_valid(form)
  101. class ValorListView(ListView):
  102. model = Valor
  103. template_name = 'cartera/valor_list.html'
  104. context_object_name = 'valores'
  105. class ValorCreateView(CancelarUrlMixin, CreateView):
  106. model = Valor
  107. form_class = ValorForm
  108. template_name = 'cartera/form.html'
  109. success_url = reverse_lazy('cartera:valores')
  110. titulo = 'Nuevo valor'
  111. def form_valid(self, form):
  112. messages.success(self.request, 'Valor creado')
  113. return super().form_valid(form)
  114. class ValorUpdateView(CancelarUrlMixin, UpdateView):
  115. model = Valor
  116. form_class = ValorForm
  117. template_name = 'cartera/form.html'
  118. success_url = reverse_lazy('cartera:valores')
  119. titulo = 'Editar valor'
  120. def form_valid(self, form):
  121. messages.success(self.request, 'Valor actualizado')
  122. return super().form_valid(form)
  123. class ValorDeleteView(DeleteView):
  124. model = Valor
  125. template_name = 'cartera/confirm_delete.html'
  126. success_url = reverse_lazy('cartera:valores')
  127. titulo = 'Eliminar valor'
  128. def form_valid(self, form):
  129. messages.success(self.request, 'Valor eliminado (con todas sus operaciones)')
  130. return super().form_valid(form)