613 lines
23 KiB
Python

from django.shortcuts import render, get_object_or_404, redirect
from django.http import HttpResponse, JsonResponse
from django.contrib import messages
from django.template.loader import render_to_string
from django.views.generic import ListView, CreateView, UpdateView, DeleteView
from django.urls import reverse_lazy
from xhtml2pdf import pisa
from django.conf import settings
from .models import InventarioAdministrativo, InventarioTactico, Directores, Subjefes, Unidades, AsignacionAdministrativa, AsignadaUnidadAdministrativa, AsignacionTactica, AsignadaUnidadTactica
from .forms import InventarioAdministrativoForm, InventarioTacticoForm, DirectoresForm, SubjefesForm, UnidadForm, AsignacionAdministrativaForm, AsignacionTacticaForm
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required
from django.contrib.auth.views import LoginView
import reportlab.graphics.barcode.code128
import reportlab.graphics.barcode.code39
class CustomLoginView(LoginView):
template_name = 'registration/login.html'
redirect_authenticated_user = True
def form_valid(self, form):
response = super().form_valid(form)
# Reiniciar el timer al hacer login
if hasattr(self.request, 'session'):
self.request.session.modified = True
return response
def form_invalid(self, form):
messages.error(self.request, 'Usuario o contraseña incorrectos')
return super().form_invalid(form)
# Vista principal
@login_required
def principal(request):
vehiculos_administrativos_count = InventarioAdministrativo.objects.count()
vehiculos_tacticos_count = InventarioTactico.objects.count()
unidades_count = Unidades.objects.count()
request.session.modified = True
request.session.set_expiry(settings.SESSION_COOKIE_AGE)
context = {
'vehiculos_administrativos_count': vehiculos_administrativos_count,
'vehiculos_tacticos_count': vehiculos_tacticos_count,
'unidades_count': unidades_count,
'session_timeout': settings.SESSION_COOKIE_AGE,
'warning_time': settings.WARNING_TIME,
}
return render(request, 'principal/principal.html', context)
@login_required
def obtener_datos(request):
vehiculos_administrativos_count = InventarioAdministrativo.objects.count()
vehiculos_tacticos_count = InventarioTactico.objects.count()
unidades_count = Unidades.objects.count()
data = {
'vehiculos_administrativos': vehiculos_administrativos_count,
'vehiculos_tacticos': vehiculos_tacticos_count,
'unidades': unidades_count,
}
return JsonResponse(data)
# Vistas genéricas para Directores
@method_decorator(login_required, name='dispatch')
class DirectoresListView(ListView):
model = Directores
template_name = 'directores/directores.html'
context_object_name = 'directores'
@method_decorator(login_required, name='dispatch')
class DirectoresCreateView(CreateView):
model = Directores
form_class = DirectoresForm
template_name = 'directores/crear.html'
success_url = reverse_lazy('listar_directores')
def form_valid(self, form):
response = super().form_valid(form)
messages.success(self.request, 'Director registrado con éxito.')
if self.request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return HttpResponse('OK')
return response
@method_decorator(login_required, name='dispatch')
class DirectoresUpdateView(UpdateView):
model = Directores
form_class = DirectoresForm
template_name = 'directores/editar.html'
success_url = reverse_lazy('listar_directores')
def form_valid(self, form):
response = super().form_valid(form)
messages.success(self.request, 'Director actualizado con éxito.')
if self.request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return HttpResponse('OK')
return response
@method_decorator(login_required, name='dispatch')
class DirectoresDeleteView(DeleteView):
model = Directores
template_name = 'directores/eliminar_director.html'
success_url = reverse_lazy('listar_directores')
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
self.object.delete()
messages.success(request, 'Director eliminado con éxito.')
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return HttpResponse('OK')
return redirect(self.success_url)
# Vistas genéricas para Subjefes
@method_decorator(login_required, name='dispatch')
class SubjefesListView(ListView):
model = Subjefes
template_name = 'jefes/jefes.html'
context_object_name = 'subjefes'
@method_decorator(login_required, name='dispatch')
class SubjefesCreateView(CreateView):
model = Subjefes
form_class = SubjefesForm
template_name = 'jefes/crear.html'
success_url = reverse_lazy('listar_subjefes')
def form_valid(self, form):
response = super().form_valid(form)
messages.success(self.request, 'Grupo registrado con éxito.')
if self.request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return HttpResponse('OK')
return response
@method_decorator(login_required, name='dispatch')
class SubjefesUpdateView(UpdateView):
model = Subjefes
form_class = SubjefesForm
template_name = 'jefes/editar.html'
success_url = reverse_lazy('listar_subjefes')
def form_valid(self, form):
response = super().form_valid(form)
messages.success(self.request, 'Grupo actualizado con éxito.')
if self.request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return HttpResponse('OK')
return response
@method_decorator(login_required, name='dispatch')
class SubjefesDeleteView(DeleteView):
model = Subjefes
template_name = 'jefes/eliminar_subjefe.html'
success_url = reverse_lazy('listar_subjefes')
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
self.object.delete()
messages.success(request, 'Grupo eliminado con éxito.')
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return HttpResponse('OK')
return redirect(self.success_url)
# Vistas genéricas para Inventario Administrativo
@method_decorator(login_required, name='dispatch')
class InventarioAdministrativoListView(ListView):
model = InventarioAdministrativo
template_name = 'inventario_administrativo/listar.html'
context_object_name = 'inventarios'
@method_decorator(login_required, name='dispatch')
class InventarioAdministrativoCreateView(CreateView):
model = InventarioAdministrativo
form_class = InventarioAdministrativoForm
template_name = 'inventario_administrativo/crear.html'
success_url = reverse_lazy('listar_inventario_administrativo')
def form_valid(self, form):
response = super().form_valid(form)
messages.success(self.request, 'Vehículo administrativo registrado con éxito.')
if self.request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return HttpResponse('OK')
return response
@method_decorator(login_required, name='dispatch')
class InventarioAdministrativoUpdateView(UpdateView):
model = InventarioAdministrativo
form_class = InventarioAdministrativoForm
template_name = 'inventario_administrativo/editar.html'
success_url = reverse_lazy('listar_inventario_administrativo')
def form_valid(self, form):
response = super().form_valid(form)
messages.success(self.request, 'Vehículo administrativo actualizado con éxito.')
if self.request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return HttpResponse('OK')
return response
@method_decorator(login_required, name='dispatch')
class InventarioAdministrativoDeleteView(DeleteView):
model = InventarioAdministrativo
template_name = 'inventario_administrativo/eliminacion.html'
success_url = reverse_lazy('listar_inventario_administrativo')
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
self.object.delete()
messages.success(request, 'Vehículo administrativo eliminado con éxito.')
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return HttpResponse('OK')
return redirect(self.success_url)
# Vistas genéricas para Inventario Táctico
@method_decorator(login_required, name='dispatch')
class InventarioTacticoListView(ListView):
model = InventarioTactico
template_name = 'inventario_tactico/listar.html'
context_object_name = 'inventarios'
@method_decorator(login_required, name='dispatch')
class InventarioTacticoCreateView(CreateView):
model = InventarioTactico
form_class = InventarioTacticoForm
template_name = 'inventario_tactico/crear.html'
success_url = reverse_lazy('listar_inventario_tactico')
def form_valid(self, form):
response = super().form_valid(form)
messages.success(self.request, 'Vehículo táctico registrado con éxito.')
if self.request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return HttpResponse('OK')
return response
@method_decorator(login_required, name='dispatch')
class InventarioTacticoUpdateView(UpdateView):
model = InventarioTactico
form_class = InventarioTacticoForm
template_name = 'inventario_tactico/editar.html'
success_url = reverse_lazy('listar_inventario_tactico')
def form_valid(self, form):
response = super().form_valid(form)
messages.success(self.request, 'Vehículo táctico actualizado con éxito.')
if self.request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return HttpResponse('OK')
return response
@method_decorator(login_required, name='dispatch')
class InventarioTacticoDeleteView(DeleteView):
model = InventarioTactico
template_name = 'inventario_tactico/eliminar.html'
success_url = reverse_lazy('listar_inventario_tactico')
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
self.object.delete()
messages.success(request, 'Vehículo táctico eliminado con éxito.')
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return HttpResponse('OK')
return redirect(self.success_url)
# Vistas genéricas para Unidades
@method_decorator(login_required, name='dispatch')
class UnidadListView(ListView):
model = Unidades
template_name = 'unidad/listar.html'
context_object_name = 'unidades'
@method_decorator(login_required, name='dispatch')
class UnidadCreateView(CreateView):
model = Unidades
form_class = UnidadForm
template_name = 'unidad/crear.html'
success_url = reverse_lazy('unidad_list')
def form_valid(self, form):
response = super().form_valid(form)
messages.success(self.request, 'Unidad registrada con éxito.')
if self.request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return HttpResponse('OK')
return response
@method_decorator(login_required, name='dispatch')
class UnidadUpdateView(UpdateView):
model = Unidades
form_class = UnidadForm
template_name = 'unidad/editar.html'
success_url = reverse_lazy('unidad_list')
def form_valid(self, form):
response = super().form_valid(form)
messages.success(self.request, 'Unidad actualizada con éxito.')
if self.request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return HttpResponse('OK')
return response
@method_decorator(login_required, name='dispatch')
class UnidadDeleteView(DeleteView):
model = Unidades
template_name = 'unidad/eliminar.html'
success_url = reverse_lazy('unidad_list')
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
self.object.delete()
messages.success(request, 'Unidad eliminada con éxito.')
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return HttpResponse('OK')
return redirect(self.success_url)
@login_required
def eliminar_asignacion_admin(request, pk):
asignacion = get_object_or_404(AsignadaUnidadAdministrativa, pk=pk)
vehiculo = asignacion.vehiculo
vehiculo.asignado = False # Liberar el vehículo
vehiculo.save()
asignacion.delete()
messages.success(request, "Vehículo administrativo liberado exitosamente.")
return redirect('detalle', id=asignacion.unidad.id)
@login_required
def eliminar_asignacion_tactica(request, pk):
asignacion = get_object_or_404(AsignadaUnidadTactica, pk=pk)
vehiculo = asignacion.vehiculo
vehiculo.asignado = False # Liberar el vehículo
vehiculo.save()
asignacion.delete()
messages.success(request, "Vehículo táctico liberado exitosamente.")
return redirect('detalle', id=asignacion.unidad.id)
# Asignación Administrativa
@login_required
def asignacion_administrativa(request):
administrativos = AsignacionAdministrativa.objects.all()
if request.method == 'POST':
form = AsignacionAdministrativaForm(request.POST)
if form.is_valid():
unidad = form.cleaned_data['unidad']
directores = form.cleaned_data['directores']
jefes = form.cleaned_data['jefes']
comprobante = form.cleaned_data['comprobante']
vehiculos = form.cleaned_data['vehiculos']
precios = request.POST.getlist('precios')
if len(vehiculos) != len(precios):
messages.error(request, "Debes ingresar un precio para cada vehículo seleccionado.")
else:
asignaciones = []
for vehiculo, precio in zip(vehiculos, precios):
try:
precio_decimal = float(precio)
asignacion = AsignacionAdministrativa(
unidad=unidad,
directores=directores,
jefes=jefes,
vehiculo=vehiculo,
comprobante=comprobante,
precio=precio_decimal
)
asignacion.save()
asignaciones.append(asignacion)
except ValueError:
messages.error(request, f"Precio inválido para {vehiculo}: '{precio}'")
break
else:
messages.success(request, f"{len(asignaciones)} asignación(es) registrada(s) con éxito.")
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return HttpResponse('OK')
return redirect('asignacion_administrativa')
else:
form = AsignacionAdministrativaForm()
context = {'form': form, 'administrativos': administrativos}
return render(request, 'asignacion_administrativa/index.html', context)
@login_required
def eliminar_todo(request):
if request.method == 'POST':
asignaciones = AsignacionAdministrativa.objects.all()
for asignacion in asignaciones:
# Crear registro histórico
AsignadaUnidadAdministrativa.objects.create(
unidad=asignacion.unidad,
directores=asignacion.directores,
jefes=asignacion.jefes,
vehiculo=asignacion.vehiculo,
fecha_creacion=asignacion.fecha_creacion,
comprobante=asignacion.comprobante,
precio=asignacion.precio
)
# Marcar como no reasignable
vehiculo = asignacion.vehiculo
vehiculo.asignado = True # Mantener como asignado permanentemente
vehiculo.save()
AsignacionAdministrativa.objects.all().delete()
messages.success(request, "Todas las asignaciones han sido archivadas.")
return redirect('asignacion_administrativa')
@login_required
def generar_pdf(request):
asignaciones = AsignacionAdministrativa.objects.all()
if asignaciones.exists():
unidad = asignaciones.first().unidad
director = asignaciones.first().directores
jefe = asignaciones.first().jefes
else:
unidad = director = jefe = None
html_string = render_to_string('pdf/pdf_asinacion.html', {
'asignaciones': [],
'jefe': None,
'director': None,
'unidad_nombre': "Sin Unidad",
})
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="asignacion_administrativa.pdf"'
pisa.CreatePDF(html_string, dest=response)
messages.info(request, "No hay asignaciones para generar el PDF.")
return response
html_string = render_to_string('pdf/pdf_asinacion.html', {
'asignaciones': asignaciones,
'jefe': jefe,
'director': director,
'unidad_nombre': unidad.nombre if unidad else "Sin Unidad",
})
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="asignacion_administrativa.pdf"'
pisa_status = pisa.CreatePDF(html_string, dest=response)
if pisa_status.err:
return HttpResponse('Error al generar el PDF.')
messages.success(request, "PDF generado con éxito.")
return response
# Asignación Táctica
@login_required
def asignacion_tactica(request):
asignaciones = AsignacionTactica.objects.all()
if request.method == 'POST':
form = AsignacionTacticaForm(request.POST)
if form.is_valid():
unidad = form.cleaned_data['unidad']
directores = form.cleaned_data['directores']
jefes = form.cleaned_data['jefes']
comprobante = form.cleaned_data['comprobante']
vehiculos = form.cleaned_data['vehiculos']
precios = request.POST.getlist('precios')
if len(vehiculos) != len(precios):
messages.error(request, "Debes ingresar un precio para cada vehículo seleccionado.")
else:
asignaciones_nuevas = []
for vehiculo, precio in zip(vehiculos, precios):
try:
precio_decimal = float(precio)
asignacion = AsignacionTactica(
unidad=unidad,
directores=directores,
jefes=jefes,
vehiculo=vehiculo,
comprobante=comprobante,
precio=precio_decimal
)
asignacion.save()
asignaciones_nuevas.append(asignacion)
except ValueError:
messages.error(request, f"Precio inválido para {vehiculo}: '{precio}'")
break
else:
messages.success(request, f"{len(asignaciones_nuevas)} asignación(es) registrada(s) con éxito.")
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return HttpResponse('OK')
return redirect('asignacion_tactica')
else:
form = AsignacionTacticaForm()
context = {'form': form, 'asignaciones': asignaciones}
return render(request, 'asignacion_tactica/index.html', context)
@login_required
def eliminar_todo_tactico(request):
if request.method == 'POST':
asignaciones = AsignacionTactica.objects.all()
for asignacion in asignaciones:
AsignadaUnidadTactica.objects.create(
unidad=asignacion.unidad,
directores=asignacion.directores,
jefes=asignacion.jefes,
vehiculo=asignacion.vehiculo,
fecha_creacion=asignacion.fecha_creacion,
comprobante=asignacion.comprobante,
precio=asignacion.precio
)
vehiculo = asignacion.vehiculo
vehiculo.asignado = True # Mantener como asignado permanentemente
vehiculo.save()
AsignacionTactica.objects.all().delete()
messages.success(request, "Todas las asignaciones tácticas han sido archivadas.")
return redirect('asignacion_tactica')
@login_required
def generar_pdf_tactico(request):
asignaciones = AsignacionTactica.objects.all()
if asignaciones.exists():
unidad = asignaciones.first().unidad
director = asignaciones.first().directores
jefe = asignaciones.first().jefes
else:
unidad = director = jefe = None
html_string = render_to_string('pdf/pdf_asignacion_tactica.html', {
'asignaciones': [],
'jefe': None,
'director': None,
'unidad_nombre': "Sin Unidad",
})
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="asignacion_tactica.pdf"'
pisa.CreatePDF(html_string, dest=response)
messages.info(request, "No hay asignaciones para generar el PDF.")
return response
html_string = render_to_string('pdf/pdf_asignacion_tactica.html', {
'asignaciones': asignaciones,
'jefe': jefe,
'director': director,
'unidad_nombre': unidad.nombre if unidad else "Sin Unidad",
})
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="asignacion_tactica.pdf"'
pisa_status = pisa.CreatePDF(html_string, dest=response)
if pisa_status.err:
return HttpResponse('Error al generar el PDF.')
messages.success(request, "PDF generado con éxito.")
return response
# Detalle de Unidad
@login_required
def detalle_unidad(request, id):
unidad = get_object_or_404(Unidades, id=id)
administrativo = AsignadaUnidadAdministrativa.objects.filter(unidad=id)
tactico = AsignadaUnidadTactica.objects.filter(unidad=id)
context = {
'unidad': unidad,
'administrativo': administrativo,
'tactico': tactico
}
return render(request, 'unidad/detalle.html', context)
# Generar PDF por Unidad (Administrativo o Táctico)
@login_required
def detalle_pdf(request, tipo, unidad_id):
unidad = get_object_or_404(Unidades, pk=unidad_id)
jefe = Subjefes.objects.first()
director = Directores.objects.first()
context = {
'unidad_nombre': unidad.nombre,
'jefe': jefe,
'director': director,
'tipo': tipo,
}
if tipo == 'administrativo':
asignaciones = AsignadaUnidadAdministrativa.objects.filter(unidad=unidad)
context['asignaciones'] = asignaciones
elif tipo == 'tactico':
asignaciones = AsignadaUnidadTactica.objects.filter(unidad=unidad)
context['asignaciones'] = asignaciones
else:
messages.error(request, "Tipo no válido")
return HttpResponse("Tipo no válido", status=400)
html = render_to_string('pdf/administrativo.html', context)
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = f'attachment; filename="reporte_{unidad.nombre}_{tipo}.pdf"'
pisa_status = pisa.CreatePDF(html, dest=response, encoding='UTF-8')
if pisa_status.err:
messages.error(request, "Error al generar el PDF")
return HttpResponse("Error al generar el PDF", status=500)
messages.success(request, f"PDF de {tipo} generado con éxito para {unidad.nombre}")
return response
def expired_page(request):
return render(request, 'expired.html', status=403)