first commit: sistema de gestión de inventario y asignación de vehículos

This commit is contained in:
Cap. Miguel Arcangel Ollarves Mayorquin 2026-06-02 14:46:44 -04:00
commit 92428903a2
5430 changed files with 885253 additions and 0 deletions

73
README.md Normal file
View File

@ -0,0 +1,73 @@
# 🚛 SISTEMA DE GESTIÓN DE INVENTARIO Y ASIGNACIÓN DE VEHÍCULOS
Sistema web desarrollado en **Django** para la gestión integral de inventarios de vehículos administrativos y tácticos, asignación a unidades militares, control de historial y generación de reportes PDF.
---
## 📋 Tabla de Contenidos
- [📌 Características Principales](#-características-principales)
- [🗃️ Modelos de Datos](#-modelos-de-datos)
- [📦 Requisitos Previos](#-requisitos-previos)
- [⚙️ Instalación y Configuración](#-instalación-y-configuración)
- [▶️ Ejecución del Servidor](#-ejecución-del-servidor)
- [📖 Uso del Sistema](#-uso-del-sistema)
- [🔐 Autenticación](#-autenticación)
- [📊 Dashboard Principal](#-dashboard-principal)
- [📋 CRUDs disponibles](#-cruds-disponibles)
- [🔄 Asignación de Vehículos](#-asignación-de-vehículos)
- [📄 Generación de Reportes PDF](#-generación-de-reportes-pdf)
- [🔒 Seguridad y Sesiones](#-seguridad-y-sesiones)
- [🛠️ Mantenimiento y Backup](#-mantenimiento-y-backup)
- [💡 Posibles Mejoras](#-posibles-mejoras)
- [📁 Estructura del Proyecto](#-estructura-del-proyecto)
---
## 📌 Características Principales
- Gestión completa de inventarios (administrativo y táctico).
- CRUD de unidades, directores y grupos de trabajo (subjefes).
- Asignación temporal y definitiva de vehículos a unidades.
- Historial de asignaciones archivadas.
- Generación de reportes PDF con xhtml2pdf.
- Autenticación de usuarios y cierre de sesión automático por inactividad.
- Dashboard con contadores actualizados en tiempo real (AJAX).
- Interfaz responsive con Bootstrap.
---
## 🗃️ Modelos de Datos
| Modelo | Descripción |
|--------|-------------|
| `Directores` | Datos del director (grado, nombre, cargo) |
| `Subjefes` | Grupo de trabajo asociado a un director |
| `Unidades` | Unidades militares que reciben vehículos |
| `InventarioAdministrativo` | Vehículos administrativos |
| `InventarioTactico` | Vehículos tácticos |
| `AsignacionAdministrativa` | Asignación temporal de vehículos administrativos |
| `AsignadaUnidadAdministrativa` | Historial permanente de asignaciones administrativas |
| `AsignacionTactica` | Asignación temporal de vehículos tácticos |
| `AsignadaUnidadTactica` | Historial permanente de asignaciones tácticas |
---
## 📦 Requisitos Previos
Antes de comenzar, asegúrate de tener instalado:
- **Python** 3.8 o superior
- **pip** (gestor de paquetes de Python)
- **Git** (opcional, para clonar el repositorio)
- **SQLite** (incluido por defecto en Django) o **PostgreSQL/MySQL** (opcional)
---
## ⚙️ Instalación y Configuración
### 1. Clonar el repositorio
```bash
git clone https://git.ejercito.mil.ve/repo/tu-repositorio.git
cd nombre-del-proyecto

46
ServTransporte.iss Normal file
View File

@ -0,0 +1,46 @@
; Script de instalación para el Ejército Nacional de Venezuela
#define MyAppName "Servicio de Transporte Militar"
#define MyAppVersion "1.0.0"
#define MyAppPublisher "Ejército Nacional Bolivariano de Venezuela"
#define MyAppURL "https://www.ejercito.mil.ve"
#define MyAppExeName "Intendencia.exe"
[Setup]
AppId={{B3F5A6C2-1A8D-4E2A-9E7B-12C3D4E5F6A7}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}/soporte
AppUpdatesURL={#MyAppURL}/actualizaciones
DefaultDirName={autopf}\{#MyAppName}
OutputDir=B:\PROYECTOS\ProyectoTransporte\sistema\Installer_Output
OutputBaseFilename=Instalador_Servicio_Transporte_Militar
SetupIconFile=B:\PROYECTOS\ProyectoTransporte\sistema\icono.ico
Compression=lzma2/ultra
SolidCompression=yes
ArchitecturesAllowed=x64 x86
ArchitecturesInstallIn64BitMode=x64
WizardStyle=modern
PrivilegesRequired=admin
LanguageDetectionMethod=uilanguage
[Languages]
Name: "spanish"; MessagesFile: "compiler:Languages\Spanish.isl"
[Tasks]
Name: "desktopicon"; Description: "Crear icono en el escritorio"; GroupDescription: "Accesos directos:"
Name: "quicklaunchicon"; Description: "Crear icono en la barra de acceso rápido"; GroupDescription: "Accesos directos:"; Flags: unchecked
[Files]
Source: "B:\PROYECTOS\ProyectoTransporte\sistema\dist\Intendencia.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "B:\PROYECTOS\ProyectoTransporte\sistema\static\*"; DestDir: "{app}\static"; Flags: ignoreversion recursesubdirs
Source: "B:\PROYECTOS\ProyectoTransporte\sistema\intendencia\templates\*"; DestDir: "{app}\templates"; Flags: ignoreversion recursesubdirs
Source: "B:\PROYECTOS\ProyectoTransporte\sistema\db.sqlite3"; DestDir: "{app}"; Flags: onlyifdoesntexist
[Icons]
Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
Name: "{commonprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "Iniciar aplicación"; Flags: nowait postinstall skipifsilent

BIN
db.sqlite3 Normal file

Binary file not shown.

3
hook/hook-http.py Normal file
View File

@ -0,0 +1,3 @@
from PyInstaller.utils.hooks import collect_submodules
hiddenimports = collect_submodules('http')

4
hook/hook-jaraco.py Normal file
View File

@ -0,0 +1,4 @@
hiddenimports = [
'jaraco.context',
'jaraco.text'
]

BIN
icono.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

97
intendencia.spec Normal file
View File

@ -0,0 +1,97 @@
# -*- mode: python ; coding: utf-8 -*-
import os
import sys
from PyInstaller.utils.hooks import collect_data_files, collect_submodules
block_cipher = None
# Obtener directorio base de manera segura
if hasattr(sys, '_MEIPASS'):
base_dir = sys._MEIPASS
else:
base_dir = os.path.abspath('.')
# Configuración principal de datos
datas = [
# Base de datos
(os.path.join(base_dir, 'db.sqlite3'), '.'),
# Recursos de Django y dependencias
*collect_data_files('django.contrib.admin', include_py_files=True),
*collect_data_files('intendencia', include_py_files=True, includes=['**/templates/**/*']),
*collect_data_files('xhtml2pdf'),
*collect_data_files('reportlab'),
# Templates manuales (backup)
(os.path.join(base_dir, 'intendencia', 'templates'), 'intendencia/templates'),
]
# Configuración de análisis
a = Analysis(
['launcher.py'],
pathex=[
base_dir,
os.path.join(base_dir, 'sistema'),
os.path.join(base_dir, 'intendencia'),
],
binaries=[],
datas=datas,
hiddenimports=[
# Django
'django.core.handlers.wsgi',
'django.core.signals',
'django.contrib.admin.apps',
'django.contrib.auth.apps',
'django.contrib.contenttypes.apps',
'django.contrib.sessions.apps',
'django.contrib.messages.apps',
'django.contrib.staticfiles.apps',
# App específica
'intendencia.apps',
'intendencia.views',
'intendencia.urls',
'intendencia.context_processors',
# Dependencias
'waitress',
*collect_submodules('reportlab.graphics.barcode'),
*collect_submodules('xhtml2pdf'),
'reportlab.graphics.charts.legends',
'reportlab.graphics.widgets',
'reportlab.pdfbase._fontdata_enc_macexpert',
'reportlab.pdfbase._fontdata_enc_macroman',
],
hookspath=[],
runtime_hooks=[],
excludes=[
'tkinter', 'unittest', 'pydoc', 'pygments',
'psycopg2', 'numpy', 'pandas', 'scipy', 'matplotlib'
],
cipher=block_cipher,
noarchive=False,
)
# Configuración del PYZ
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
# Configuración del EXE
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
name='Intendencia',
debug=False,
bootloader_ignore_signals=True,
strip=False,
upx=True,
console=False,
icon=os.path.join(base_dir, 'icono.ico') if os.path.exists(os.path.join(base_dir, 'icono.ico')) else None,
disable_windowed_traceback=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)

0
intendencia/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

3
intendencia/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
intendencia/apps.py Normal file
View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class IntendenciaConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'intendencia'

View File

@ -0,0 +1,15 @@
from django.conf import settings
def session_settings(request):
return {
'session_timeout': settings.SESSION_COOKIE_AGE,
'warning_time': settings.WARNING_TIME
}
# context_processors.py (crea este nuevo archivo en tu app)
from datetime import datetime
def expiration_date(request):
return {
'expiration_date': datetime(2026, 12, 31) # Misma fecha que en el middleware
}

143
intendencia/forms.py Normal file
View File

@ -0,0 +1,143 @@
from django import forms
from .models import InventarioAdministrativo, InventarioTactico, Directores, Subjefes, Unidades, AsignacionAdministrativa, AsignacionTactica
class InventarioAdministrativoForm(forms.ModelForm):
class Meta:
model = InventarioAdministrativo
fields = ['placa_militar', 'clase', 'tipo', 'marca', 'modelo', 'color', 'placa_mtc', 'ano', 'serial_carroceria']
widgets = {
'placa_militar': forms.TextInput(attrs={'class': 'form-control', 'style': 'text-transform: uppercase;'}),
'clase': forms.TextInput(attrs={'class': 'form-control', 'style': 'text-transform: uppercase;'}),
'tipo': forms.TextInput(attrs={'class': 'form-control', 'style': 'text-transform: uppercase;'}),
'marca': forms.TextInput(attrs={'class': 'form-control', 'style': 'text-transform: uppercase;'}),
'modelo': forms.TextInput(attrs={'class': 'form-control', 'style': 'text-transform: uppercase;'}),
'color': forms.TextInput(attrs={'class': 'form-control', 'style': 'text-transform: uppercase;'}),
'placa_mtc': forms.TextInput(attrs={'class': 'form-control', 'style': 'text-transform: uppercase;'}),
'ano': forms.NumberInput(attrs={'class': 'form-control'}),
'serial_carroceria': forms.TextInput(attrs={'class': 'form-control', 'style': 'text-transform: uppercase;'}),
}
def clean(self):
cleaned_data = super().clean()
# Convertir todos los campos de texto a mayúsculas
for field in ['placa_militar', 'clase', 'tipo', 'marca', 'modelo', 'color', 'placa_mtc', 'serial_carroceria']:
if cleaned_data.get(field):
cleaned_data[field] = cleaned_data[field].upper()
return cleaned_data
def save(self, commit=True):
instance = super().save(commit=False)
instance.tipo_vehiculo = "ADMINISTRATIVO" # Predefinido en mayúsculas
if commit:
instance.save()
return instance
class InventarioTacticoForm(forms.ModelForm):
class Meta:
model = InventarioTactico
fields = ['placa_militar', 'tipo', 'modelo', 'marca', 'clase', 'color', 'placa_mtc', 'ano', 'serial_chasis', 'serial_motor']
widgets = {
'placa_militar': forms.TextInput(attrs={'class': 'form-control', 'style': 'text-transform: uppercase;'}),
'tipo': forms.TextInput(attrs={'class': 'form-control', 'style': 'text-transform: uppercase;'}),
'modelo': forms.TextInput(attrs={'class': 'form-control', 'style': 'text-transform: uppercase;'}),
'marca': forms.TextInput(attrs={'class': 'form-control', 'style': 'text-transform: uppercase;'}),
'clase': forms.TextInput(attrs={'class': 'form-control', 'style': 'text-transform: uppercase;'}),
'color': forms.TextInput(attrs={'class': 'form-control', 'style': 'text-transform: uppercase;'}),
'placa_mtc': forms.TextInput(attrs={'class': 'form-control', 'style': 'text-transform: uppercase;'}),
'ano': forms.NumberInput(attrs={'class': 'form-control'}),
'serial_chasis': forms.TextInput(attrs={'class': 'form-control', 'style': 'text-transform: uppercase;'}),
'serial_motor': forms.TextInput(attrs={'class': 'form-control', 'style': 'text-transform: uppercase;'}),
}
def clean(self):
cleaned_data = super().clean()
# Convertir todos los campos de texto a mayúsculas
for field in ['placa_militar', 'tipo', 'modelo', 'marca', 'clase', 'color', 'placa_mtc', 'serial_chasis', 'serial_motor']:
if cleaned_data.get(field):
cleaned_data[field] = cleaned_data[field].upper()
return cleaned_data
def save(self, commit=True):
instance = super().save(commit=False)
instance.tipo_vehiculo = "TÁCTICO" # Predefinido en mayúsculas
if commit:
instance.save()
return instance
class DirectoresForm(forms.ModelForm):
class Meta:
model = Directores
fields = '__all__'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in self.fields.values():
field.widget.attrs.update({'class': 'form-control', 'required': ''})
class SubjefesForm(forms.ModelForm):
class Meta:
model = Subjefes
fields = '__all__'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in self.fields.values():
field.widget.attrs.update({'class': 'form-select' if isinstance(field.widget, forms.Select) else 'form-control', 'required': ''})
class UnidadForm(forms.ModelForm):
class Meta:
model = Unidades
fields = '__all__'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['nombre'].widget.attrs.update({'class': 'form-control', 'required': ''})
class AsignacionAdministrativaForm(forms.ModelForm):
vehiculos = forms.ModelMultipleChoiceField(
queryset=InventarioAdministrativo.objects.filter(
asignado=False,
asignaciones_historial__isnull=True # Excluir los que ya están en histórico
),
widget=forms.SelectMultiple(attrs={'class': 'form-select', 'size': '5', 'id': 'vehiculos'}),
label="Vehículos Administrativos"
)
class Meta:
model = AsignacionAdministrativa
fields = ['unidad', 'directores', 'jefes', 'comprobante']
widgets = {
'unidad': forms.Select(attrs={'class': 'form-select'}),
'directores': forms.Select(attrs={'class': 'form-select'}),
'jefes': forms.Select(attrs={'class': 'form-select'}),
'comprobante': forms.TextInput(attrs={'class': 'form-control'}),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['vehiculos'].queryset = InventarioAdministrativo.objects.filter(asignado=False)
class AsignacionTacticaForm(forms.ModelForm):
vehiculos = forms.ModelMultipleChoiceField(
queryset=InventarioTactico.objects.filter(
asignaciones_temporales__isnull=True, # Excluir los que están en AsignacionTactica
asignado=False # Excluir los asignados actualmente
),
widget=forms.SelectMultiple(attrs={'class': 'form-select', 'size': '5', 'id': 'vehiculos'}),
label="Vehículos Tácticos"
)
class Meta:
model = AsignacionTactica
fields = ['unidad', 'directores', 'jefes', 'comprobante']
widgets = {
'unidad': forms.Select(attrs={'class': 'form-select'}),
'directores': forms.Select(attrs={'class': 'form-select'}),
'jefes': forms.Select(attrs={'class': 'form-select'}),
'comprobante': forms.TextInput(attrs={'class': 'form-control'}),
}

33
intendencia/middleware.py Normal file
View File

@ -0,0 +1,33 @@
# intendencia/middleware.py
from django.http import HttpResponseForbidden
from django.urls import reverse
from django.utils import timezone
from datetime import datetime
from django.shortcuts import redirect
class ExpirationCheckMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Fecha de expiración (cámbiala según necesites)
expiration_date = datetime(2026, 12, 31) # AAAA, MM, DD
# Permitir acceso a la página de expiración y a archivos estáticos
if request.path.startswith('/static/') or request.path.startswith('/media/'):
return self.get_response(request)
# Si la fecha actual es posterior a la de expiración
if timezone.now().date() > expiration_date.date():
# Permitir logout
if request.path == reverse('logout'):
return self.get_response(request)
# Redirigir a la página de expiración
if request.path != reverse('expired'):
return redirect('expired')
# Permitir acceso a superusuarios después de la expiración
if request.user.is_authenticated and request.user.is_superuser:
return self.get_response(request)
return self.get_response(request)

View File

@ -0,0 +1,143 @@
# Generated by Django 5.2.3 on 2025-07-01 14:32
import django.core.validators
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Directores',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('grado', models.CharField(max_length=100, verbose_name='Grado')),
('nombres', models.CharField(max_length=200, validators=[django.core.validators.MinLengthValidator(3)], verbose_name='Nombres y Apellidos')),
('cargos', models.CharField(max_length=100, verbose_name='Cargos')),
],
),
migrations.CreateModel(
name='Unidades',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('nombre', models.CharField(max_length=200, unique=True, verbose_name='Nombre de Unidad')),
],
),
migrations.CreateModel(
name='InventarioAdministrativo',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('placa_militar', models.CharField(max_length=50, unique=True, verbose_name='Placa Militar')),
('clase', models.CharField(max_length=100, verbose_name='Clase')),
('tipo', models.CharField(max_length=100, verbose_name='Tipo')),
('marca', models.CharField(max_length=100, verbose_name='Marca')),
('modelo', models.CharField(max_length=100, verbose_name='Modelo')),
('color', models.CharField(max_length=100, verbose_name='Color')),
('tipo_vehiculo', models.CharField(max_length=100, verbose_name='Tipo de Vehículo')),
('placa_mtc', models.CharField(blank=True, max_length=100, null=True, verbose_name='Placa MTC')),
('ano', models.IntegerField(blank=True, null=True, verbose_name='Año')),
('serial_carroceria', models.CharField(max_length=100, unique=True, verbose_name='Serial de Carrocería')),
('fecha_creacion', models.DateField(auto_now_add=True, verbose_name='Fecha de Creación')),
('asignado', models.BooleanField(default=False, verbose_name='Asignado')),
],
options={
'verbose_name': 'Inventario Administrativo',
'verbose_name_plural': 'Inventarios Administrativos',
'ordering': ['-fecha_creacion'],
'indexes': [models.Index(fields=['placa_militar'], name='intendencia_placa_m_ce9c1a_idx')],
},
),
migrations.CreateModel(
name='InventarioTactico',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('placa_militar', models.CharField(max_length=50, unique=True, verbose_name='Placa Militar')),
('tipo', models.CharField(max_length=100, verbose_name='Tipo')),
('modelo', models.CharField(max_length=100, verbose_name='Modelo')),
('marca', models.CharField(max_length=100, verbose_name='Marca')),
('clase', models.CharField(max_length=100, verbose_name='Clase')),
('color', models.CharField(max_length=100, verbose_name='Color')),
('tipo_vehiculo', models.CharField(max_length=100, verbose_name='Tipo de Vehículo')),
('placa_mtc', models.CharField(blank=True, max_length=100, null=True, verbose_name='Placa MTC')),
('ano', models.IntegerField(blank=True, null=True, verbose_name='Año')),
('serial_chasis', models.CharField(max_length=100, unique=True, verbose_name='Serial del Chasis')),
('serial_motor', models.CharField(max_length=100, unique=True, verbose_name='Serial del Motor')),
('fecha_creacion', models.DateField(auto_now_add=True, verbose_name='Fecha de Creación')),
('asignado', models.BooleanField(default=False, verbose_name='Asignado')),
],
options={
'verbose_name': 'Inventario Táctico',
'verbose_name_plural': 'Inventarios Tácticos',
'ordering': ['-fecha_creacion'],
'indexes': [models.Index(fields=['placa_militar'], name='intendencia_placa_m_9ee8f2_idx')],
},
),
migrations.CreateModel(
name='Subjefes',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('grado', models.CharField(max_length=100, verbose_name='Grado')),
('nombres', models.CharField(max_length=200, validators=[django.core.validators.MinLengthValidator(3)], verbose_name='Nombres y Apellidos')),
('cargos', models.CharField(max_length=100, verbose_name='Cargos')),
('director', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='subjefes', to='intendencia.directores', verbose_name='Director')),
],
),
migrations.CreateModel(
name='AsignadaUnidadTactica',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('fecha_creacion', models.DateField(verbose_name='Fecha de Creación')),
('comprobante', models.CharField(max_length=50, verbose_name='N° Comprobante')),
('precio', models.DecimalField(decimal_places=2, max_digits=15, verbose_name='Precio')),
('directores', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='asignaciones_unidad_tacticas', to='intendencia.directores', verbose_name='Directores')),
('vehiculo', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='asignaciones_historial', to='intendencia.inventariotactico', verbose_name='Vehículo')),
('jefes', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='asignaciones_unidad_tacticas', to='intendencia.subjefes', verbose_name='Grupo de Trabajo')),
('unidad', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='asignaciones_unidad_tacticas', to='intendencia.unidades', verbose_name='Unidad')),
],
),
migrations.CreateModel(
name='AsignadaUnidadAdministrativa',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('fecha_creacion', models.DateField(verbose_name='Fecha de Creación')),
('comprobante', models.CharField(max_length=50, verbose_name='N° Comprobante')),
('precio', models.DecimalField(decimal_places=2, max_digits=15, verbose_name='Precio')),
('directores', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='asignaciones_unidad_admin', to='intendencia.directores', verbose_name='Directores')),
('vehiculo', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='asignaciones_historial', to='intendencia.inventarioadministrativo', verbose_name='Vehículo')),
('jefes', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='asignaciones_unidad_admin', to='intendencia.subjefes', verbose_name='Grupo de Trabajo')),
('unidad', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='asignaciones_unidad_admin', to='intendencia.unidades', verbose_name='Unidad')),
],
),
migrations.CreateModel(
name='AsignacionTactica',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('fecha_creacion', models.DateField(auto_now_add=True, verbose_name='Fecha de Creación')),
('comprobante', models.CharField(max_length=50, verbose_name='N° Comprobante')),
('precio', models.DecimalField(decimal_places=2, max_digits=15, verbose_name='Precio')),
('directores', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='asignaciones_tacticas', to='intendencia.directores', verbose_name='Directores')),
('vehiculo', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='asignaciones_temporales', to='intendencia.inventariotactico', verbose_name='Vehículo Tactico')),
('jefes', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='asignaciones_tacticas', to='intendencia.subjefes', verbose_name='Grupo de Trabajo')),
('unidad', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='asignaciones_tacticas', to='intendencia.unidades', verbose_name='Unidad')),
],
),
migrations.CreateModel(
name='AsignacionAdministrativa',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('fecha_creacion', models.DateField(auto_now_add=True, verbose_name='Fecha de Creación')),
('comprobante', models.CharField(max_length=50, verbose_name='N° Comprobante')),
('precio', models.DecimalField(decimal_places=2, max_digits=15, verbose_name='Precio')),
('directores', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='asignaciones_admin', to='intendencia.directores', verbose_name='Directores')),
('vehiculo', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='asignaciones_temporales', to='intendencia.inventarioadministrativo', verbose_name='Vehículo Administrativo')),
('jefes', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='asignaciones_admin', to='intendencia.subjefes', verbose_name='Grupo de Trabajo')),
('unidad', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='asignaciones_admin', to='intendencia.unidades', verbose_name='Unidad')),
],
),
]

View File

137
intendencia/models.py Normal file
View File

@ -0,0 +1,137 @@
from django.db import models
from django.core.validators import MinLengthValidator
class Directores(models.Model):
grado = models.CharField(max_length=100, verbose_name="Grado")
nombres = models.CharField(max_length=200, validators=[MinLengthValidator(3)], verbose_name="Nombres y Apellidos")
cargos = models.CharField(max_length=100, verbose_name="Cargos")
def __str__(self):
return f"{self.grado} {self.nombres}"
class Subjefes(models.Model):
grado = models.CharField(max_length=100, verbose_name="Grado")
nombres = models.CharField(max_length=200, validators=[MinLengthValidator(3)], verbose_name="Nombres y Apellidos")
cargos = models.CharField(max_length=100, verbose_name="Cargos")
director = models.ForeignKey(Directores, on_delete=models.CASCADE, related_name="subjefes", verbose_name="Director")
def __str__(self):
return f"{self.grado} {self.nombres}"
class Unidades(models.Model):
nombre = models.CharField(max_length=200, unique=True, verbose_name="Nombre de Unidad")
def __str__(self):
return self.nombre
class InventarioAdministrativo(models.Model):
placa_militar = models.CharField(max_length=50, unique=True, verbose_name="Placa Militar")
clase = models.CharField(max_length=100, verbose_name="Clase")
tipo = models.CharField(max_length=100, verbose_name="Tipo")
marca = models.CharField(max_length=100, verbose_name="Marca")
modelo = models.CharField(max_length=100, verbose_name="Modelo")
color = models.CharField(max_length=100, verbose_name="Color")
tipo_vehiculo = models.CharField(max_length=100, verbose_name="Tipo de Vehículo")
placa_mtc = models.CharField(max_length=100, verbose_name="Placa MTC", blank=True, null=True) # Opcional
ano = models.IntegerField(verbose_name="Año", blank=True, null=True) # Cambiado a IntegerField
serial_carroceria = models.CharField(max_length=100, unique=True, verbose_name="Serial de Carrocería")
fecha_creacion = models.DateField(auto_now_add=True, verbose_name="Fecha de Creación")
asignado = models.BooleanField(default=False, verbose_name="Asignado")
def __str__(self):
return f"{self.placa_militar} - {self.marca} {self.modelo} ({self.clase})"
class Meta:
verbose_name = "Inventario Administrativo"
verbose_name_plural = "Inventarios Administrativos"
ordering = ['-fecha_creacion']
indexes = [models.Index(fields=['placa_militar'])]
class InventarioTactico(models.Model):
placa_militar = models.CharField(max_length=50, unique=True, verbose_name="Placa Militar")
tipo = models.CharField(max_length=100, verbose_name="Tipo")
modelo = models.CharField(max_length=100, verbose_name="Modelo")
marca = models.CharField(max_length=100, verbose_name="Marca")
clase = models.CharField(max_length=100, verbose_name="Clase")
color = models.CharField(max_length=100, verbose_name="Color")
tipo_vehiculo = models.CharField(max_length=100, verbose_name="Tipo de Vehículo")
placa_mtc = models.CharField(max_length=100, verbose_name="Placa MTC", blank=True, null=True) # Opcional
ano = models.IntegerField(verbose_name="Año", blank=True, null=True) # Cambiado a IntegerField
serial_chasis = models.CharField(max_length=100, unique=True, verbose_name="Serial del Chasis")
serial_motor = models.CharField(max_length=100, unique=True, verbose_name="Serial del Motor")
fecha_creacion = models.DateField(auto_now_add=True, verbose_name="Fecha de Creación")
asignado = models.BooleanField(default=False, verbose_name="Asignado")
def __str__(self):
return f"{self.tipo_vehiculo} - {self.marca} {self.modelo} (Chasis: {self.serial_chasis})"
class Meta:
verbose_name = "Inventario Táctico"
verbose_name_plural = "Inventarios Tácticos"
ordering = ['-fecha_creacion']
indexes = [models.Index(fields=['placa_militar'])]
class AsignacionAdministrativa(models.Model):
unidad = models.ForeignKey(Unidades, on_delete=models.CASCADE, related_name="asignaciones_admin", verbose_name="Unidad")
directores = models.ForeignKey(Directores, on_delete=models.CASCADE, related_name="asignaciones_admin", verbose_name="Directores")
jefes = models.ForeignKey(Subjefes, on_delete=models.CASCADE, related_name="asignaciones_admin", verbose_name="Grupo de Trabajo")
vehiculo = models.ForeignKey(InventarioAdministrativo, on_delete=models.CASCADE, related_name="asignaciones_temporales", verbose_name="Vehículo Administrativo")
fecha_creacion = models.DateField(auto_now_add=True, verbose_name="Fecha de Creación")
comprobante = models.CharField(max_length=50, verbose_name="N° Comprobante")
precio = models.DecimalField(max_digits=15, decimal_places=2, verbose_name="Precio")
def save(self, *args, **kwargs):
self.vehiculo.asignado = True
self.vehiculo.save()
super().save(*args, **kwargs)
def __str__(self):
return f"Asignación de {self.vehiculo} a {self.unidad}"
class AsignadaUnidadAdministrativa(models.Model):
unidad = models.ForeignKey(Unidades, on_delete=models.CASCADE, related_name="asignaciones_unidad_admin", verbose_name="Unidad")
directores = models.ForeignKey(Directores, on_delete=models.CASCADE, related_name="asignaciones_unidad_admin", verbose_name="Directores")
jefes = models.ForeignKey(Subjefes, on_delete=models.CASCADE, related_name="asignaciones_unidad_admin", verbose_name="Grupo de Trabajo")
vehiculo = models.ForeignKey(InventarioAdministrativo, on_delete=models.CASCADE, related_name="asignaciones_historial", verbose_name="Vehículo")
fecha_creacion = models.DateField(verbose_name="Fecha de Creación")
comprobante = models.CharField(max_length=50, verbose_name="N° Comprobante")
precio = models.DecimalField(max_digits=15, decimal_places=2, verbose_name="Precio")
def __str__(self):
return f"Asignación histórica de {self.vehiculo} a {self.unidad}"
class AsignacionTactica(models.Model):
unidad = models.ForeignKey(Unidades, on_delete=models.CASCADE, related_name="asignaciones_tacticas", verbose_name="Unidad")
directores = models.ForeignKey(Directores, on_delete=models.CASCADE, related_name="asignaciones_tacticas", verbose_name="Directores")
jefes = models.ForeignKey(Subjefes, on_delete=models.CASCADE, related_name="asignaciones_tacticas", verbose_name="Grupo de Trabajo")
vehiculo = models.ForeignKey(InventarioTactico, on_delete=models.CASCADE, related_name="asignaciones_temporales", verbose_name="Vehículo Tactico")
fecha_creacion = models.DateField(auto_now_add=True, verbose_name="Fecha de Creación")
comprobante = models.CharField(max_length=50, verbose_name="N° Comprobante")
precio = models.DecimalField(max_digits=15, decimal_places=2, verbose_name="Precio")
def save(self, *args, **kwargs):
self.vehiculo.asignado = True
self.vehiculo.save()
super().save(*args, **kwargs)
def __str__(self):
return f"Asignación de {self.vehiculo} a {self.unidad}"
class AsignadaUnidadTactica(models.Model):
unidad = models.ForeignKey(Unidades, on_delete=models.CASCADE, related_name="asignaciones_unidad_tacticas", verbose_name="Unidad")
directores = models.ForeignKey(Directores, on_delete=models.CASCADE, related_name="asignaciones_unidad_tacticas", verbose_name="Directores")
jefes = models.ForeignKey(Subjefes, on_delete=models.CASCADE, related_name="asignaciones_unidad_tacticas", verbose_name="Grupo de Trabajo")
vehiculo = models.ForeignKey(InventarioTactico, on_delete=models.CASCADE, related_name="asignaciones_historial", verbose_name="Vehículo")
fecha_creacion = models.DateField(verbose_name="Fecha de Creación")
comprobante = models.CharField(max_length=50, verbose_name="N° Comprobante")
precio = models.DecimalField(max_digits=15, decimal_places=2, verbose_name="Precio")
def __str__(self):
return f"Asignación histórica de {self.vehiculo} a {self.unidad}"

View File

@ -0,0 +1,155 @@
{% extends 'base.html' %}
{% block titulo %} Asignación Administrativa {% endblock %}
{% block vertical %} {% include 'partes/vertical.html' %} {% endblock %}
{% block horizontal %} {% include 'partes/horizontal.html' %} {% endblock %}
{% block seccion %}
<div class="d-flex align-items-left align-items-md-center flex-column flex-md-row pt-2 pb-4">
<div>
<h3 class="fw-bold mb-3">Asignación de Vehículos Administrativos a Unidades</h3>
<h6 class="op-7 mb-2">Servicio de Transporte del Ejército Bolivariano</h6>
</div>
</div>
{% endblock %}
{% block contenido %}
<div class="card card-stats card-round">
<div class="card-body p-4">
<form method="post">
{% csrf_token %}
<div class="row mb-3">
<div class="col-md-4">
<label for="{{ form.unidad.id_for_label }}" class="form-label">{{ form.unidad.label }}</label>
{{ form.unidad }}
</div>
<div class="col-md-4">
<label for="{{ form.directores.id_for_label }}" class="form-label">{{ form.directores.label }}</label>
{{ form.directores }}
</div>
<div class="col-md-4">
<label for="{{ form.jefes.id_for_label }}" class="form-label">{{ form.jefes.label }}</label>
{{ form.jefes }}
</div>
</div>
<div class="row mb-3">
<div class="col-md-6">
<label for="{{ form.vehiculos.id_for_label }}" class="form-label">{{ form.vehiculos.label }}</label>
{{ form.vehiculos }}
<small class="form-text text-muted">Mantén presionada la tecla Ctrl (o Cmd en Mac) para seleccionar múltiples vehículos.</small>
</div>
<div class="col-md-3">
<label for="{{ form.comprobante.id_for_label }}" class="form-label">{{ form.comprobante.label }}</label>
{{ form.comprobante }}
</div>
</div>
<div class="row mb-3">
<div class="col-md-12">
<label class="form-label">Precios por vehículo</label>
<div id="precio-container" class="mb-3"></div>
</div>
</div>
<div class="row mb-3">
<div class="col-md-12 text-end">
<button type="submit" class="btn btn-primary">
<i class="fas fa-plus"></i> REGISTRAR VEHICULO
</button>
</div>
</div>
</form>
<div class="table-responsive mt-4">
<table class="table table-striped table-hover" style="width:100%">
<thead class="thead-dark">
<tr>
<th></th>
<th>Unidad</th>
<th>Placa Militar</th>
<th>Clase</th>
<th>Tipo</th>
<th>Marca</th>
<th>Modelo</th>
<th>Serial de Carrocería</th>
<th>Comprobante</th>
<th>Precio</th>
<th>Fecha de Asignación</th>
</tr>
</thead>
<tbody>
{% for administrativo in administrativos %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ administrativo.unidad.nombre }}</td>
<td>{{ administrativo.vehiculo.placa_militar }}</td>
<td>{{ administrativo.vehiculo.clase }}</td>
<td>{{ administrativo.vehiculo.tipo }}</td>
<td>{{ administrativo.vehiculo.marca }}</td>
<td>{{ administrativo.vehiculo.modelo }}</td>
<td>{{ administrativo.vehiculo.serial_carroceria }}</td>
<td>{{ administrativo.comprobante }}</td>
<td>{{ administrativo.precio }}</td>
<td>{{ administrativo.fecha_creacion|date:"d-m-Y" }}</td>
</tr>
{% empty %}
<tr>
<td colspan="11" class="text-center">No hay asignaciones activas.</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="d-flex justify-content-between mt-3 px-3">
<a href="{% url 'generar_pdf' %}" class="btn btn-primary">
<i class="fas fa-file-pdf"></i> Descargar PDF
</a>
<form action="{% url 'eliminar_todo' %}" method="post" style="display:inline;">
{% csrf_token %}
<button type="submit" class="btn btn-danger">
<i class="fas fa-trash"></i> Archivar Todo
</button>
</form>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const vehiculosSelect = document.getElementById('vehiculos');
const precioContainer = document.getElementById('precio-container');
function updatePrecios() {
precioContainer.innerHTML = '';
const selectedOptions = Array.from(vehiculosSelect.selectedOptions);
selectedOptions.forEach(option => {
const vehiculoId = option.value;
const vehiculoText = option.text;
const div = document.createElement('div');
div.className = 'input-group mb-2';
div.innerHTML = `
<span class="input-group-text">${vehiculoText}</span>
<input type="number" step="0.01" name="precios" class="form-control" placeholder="Precio" required>
`;
precioContainer.appendChild(div);
});
}
vehiculosSelect.addEventListener('change', updatePrecios);
updatePrecios();
$('#tabla-asignaciones').DataTable({
language: { url: '//cdn.datatables.net/plug-ins/1.13.6/i18n/es-ES.json' },
responsive: true,
autoWidth: false,
dom: '<"row"<"col-sm-12 col-md-6"l><"col-sm-12 col-md-6 d-flex justify-content-end"f>>rt<"row"<"col-sm-12 col-md-6"i><"col-sm-12 col-md-6"p>>',
order: [[0, 'asc']],
pageLength: 10,
});
});
</script>
</div>
</div>
{% endblock %}
{% block pie %} {% include 'partes/pie.html' %} {% endblock %}

View File

@ -0,0 +1,157 @@
{% extends 'base.html' %}
{% block titulo %} Asignación Táctica {% endblock %}
{% block vertical %} {% include 'partes/vertical.html' %} {% endblock %}
{% block horizontal %} {% include 'partes/horizontal.html' %} {% endblock %}
{% block seccion %}
<div class="d-flex align-items-left align-items-md-center flex-column flex-md-row pt-2 pb-4">
<div>
<h3 class="fw-bold mb-3">Asignación de Vehículos Tácticos a Unidades</h3>
<h6 class="op-7 mb-2">Servicio de Transporte del Ejército Bolivariano</h6>
</div>
</div>
{% endblock %}
{% block contenido %}
<div class="card card-stats card-round">
<div class="card-body p-4">
<form method="post">
{% csrf_token %}
<div class="row mb-3">
<div class="col-md-4">
<label for="{{ form.unidad.id_for_label }}" class="form-label">{{ form.unidad.label }}</label>
{{ form.unidad }}
</div>
<div class="col-md-4">
<label for="{{ form.directores.id_for_label }}" class="form-label">{{ form.directores.label }}</label>
{{ form.directores }}
</div>
<div class="col-md-4">
<label for="{{ form.jefes.id_for_label }}" class="form-label">{{ form.jefes.label }}</label>
{{ form.jefes }}
</div>
</div>
<div class="row mb-3">
<div class="col-md-6">
<label for="{{ form.vehiculos.id_for_label }}" class="form-label">{{ form.vehiculos.label }}</label>
{{ form.vehiculos }}
<small class="form-text text-muted">Mantén presionada la tecla Ctrl (o Cmd en Mac) para seleccionar múltiples vehículos.</small>
</div>
<div class="col-md-3">
<label for="{{ form.comprobante.id_for_label }}" class="form-label">{{ form.comprobante.label }}</label>
{{ form.comprobante }}
</div>
</div>
<div class="row mb-3">
<div class="col-md-12">
<label class="form-label">Precios por vehículo</label>
<div id="precio-container" class="mb-3"></div>
</div>
</div>
<div class="row mb-3">
<div class="col-md-12 text-end">
<button type="submit" class="btn btn-primary">
<i class="fas fa-plus"></i> Asignar Vehículos
</button>
</div>
</div>
</form>
<div class="table-responsive mt-4">
<table class="table table-striped table-hover" style="width:100%">
<thead class="thead-dark">
<tr>
<th></th>
<th>Unidad</th>
<th>Placa Militar</th>
<th>Clase</th>
<th>Tipo</th>
<th>Marca</th>
<th>Modelo</th>
<th>Serial Chasis</th>
<th>Serial Motor</th>
<th>Comprobante</th>
<th>Precio</th>
<th>Fecha de Asignación</th>
</tr>
</thead>
<tbody>
{% for asignacion in asignaciones %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ asignacion.unidad.nombre }}</td>
<td>{{ asignacion.vehiculo.placa_militar }}</td>
<td>{{ asignacion.vehiculo.clase }}</td>
<td>{{ asignacion.vehiculo.tipo }}</td>
<td>{{ asignacion.vehiculo.marca }}</td>
<td>{{ asignacion.vehiculo.modelo }}</td>
<td>{{ asignacion.vehiculo.serial_chasis }}</td>
<td>{{ asignacion.vehiculo.serial_motor }}</td>
<td>{{ asignacion.comprobante }}</td>
<td>{{ asignacion.precio }}</td>
<td>{{ asignacion.fecha_creacion|date:"d-m-Y" }}</td>
</tr>
{% empty %}
<tr>
<td colspan="12" class="text-center">No hay asignaciones activas.</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="d-flex justify-content-between mt-3 px-3">
<a href="{% url 'generar_pdf_tactico' %}" class="btn btn-primary">
<i class="fas fa-file-pdf"></i> Descargar PDF
</a>
<form action="{% url 'eliminar_todo_tactico' %}" method="post" style="display:inline;">
{% csrf_token %}
<button type="submit" class="btn btn-danger">
<i class="fas fa-trash"></i> Archivar Todo
</button>
</form>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const vehiculosSelect = document.getElementById('vehiculos');
const precioContainer = document.getElementById('precio-container');
function updatePrecios() {
precioContainer.innerHTML = '';
const selectedOptions = Array.from(vehiculosSelect.selectedOptions);
selectedOptions.forEach(option => {
const vehiculoId = option.value;
const vehiculoText = option.text;
const div = document.createElement('div');
div.className = 'input-group mb-2';
div.innerHTML = `
<span class="input-group-text">${vehiculoText}</span>
<input type="number" step="0.01" name="precios" class="form-control" placeholder="Precio" required>
`;
precioContainer.appendChild(div);
});
}
vehiculosSelect.addEventListener('change', updatePrecios);
updatePrecios();
$('#tabla-asignaciones').DataTable({
language: { url: '//cdn.datatables.net/plug-ins/1.13.6/i18n/es-ES.json' },
responsive: true,
autoWidth: false,
dom: '<"row"<"col-sm-12 col-md-6"l><"col-sm-12 col-md-6 d-flex justify-content-end"f>>rt<"row"<"col-sm-12 col-md-6"i><"col-sm-12 col-md-6"p>>',
order: [[0, 'asc']],
pageLength: 10,
});
});
</script>
</div>
</div>
{% endblock %}
{% block pie %} {% include 'partes/pie.html' %} {% endblock %}

View File

@ -0,0 +1,312 @@
{% load static %}
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<meta name="author" content="Maom">
<title>{% block titulo %}Servicio de Transporte{% endblock %}</title>
<link rel="icon" href="{% static 'img/transporte.ico' %}" type="image/x-icon">
<!-- Fonts and Icons -->
<link rel="stylesheet" href="{% static 'assets/fonts/iconos/css/all.min.css' %}" />
<!-- En tu archivo base.html -->
<!-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> -->
<!-- CSS Files -->
<link rel="stylesheet" href="{% static 'assets/css/select2.min.css' %}" />
<link rel="stylesheet" href="{% static 'assets/css/bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'assets/css/plugins.min.css' %}">
<link rel="stylesheet" href="{% static 'assets/css/kaiadmin.min.css' %}">
<link rel="stylesheet" href="{% static 'assets/css/animate.min.css' %}" />
<style>
body { transition: background-color 0.3s, color 0.3s; font-family: 'Poppins', sans-serif; }
.dark-mode { background-color: #1a2035; color: #ffffff; }
.dark-mode .main-panel { background-color: #1a2035; }
.dark-mode .card { background-color: #2f3545; color: #ffffff; }
.dark-mode .table-dark { background-color: #2f3545; }
.dark-mode .badge.bg-dark { background-color: #ffffff !important; color: #000000 !important; }
.page-inner { animation: fadeIn 0.5s ease-in; padding: 20px; }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
.card { border-radius: 15px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); transition: transform 0.3s ease; }
.card:hover { transform: translateY(-5px); }
.form-control:focus {
border-color: #0d6efd;
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
}
.card { border-radius: 15px; }
.card-header { border-radius: 15px 15px 0 0 !important; }
</style>
{% block css %}{% endblock %}
</head>
<body>
<div class="wrapper" id="contenido">
<div class="modal fade" id="logoutWarning" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Sesión por expirar</h5>
</div>
<div class="modal-body">
La sesión se cerrará en <span id="countdown">{{ warning_time }}</span> segundos.
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" onclick="resetTimer()">Continuar</button>
<button type="button" class="btn btn-primary" onclick="logoutUser()">Cerrar ahora</button>
</div>
</div>
</div>
</div>
{% block vertical %}{% include 'partes/vertical.html' %}{% endblock %}
<div class="main-panel">
{% block horizontal %}{% include 'partes/horizontal.html' %}{% endblock %}
<div class="container">
<div class="page-inner" id="page-content">
{% block seccion %}{% endblock %}
{% block contenido %}{% endblock %}
</div>
</div>
{% block pie %}{% include 'partes/pie.html' %}{% endblock %}
</div>
</div>
<!-- Core JS Files -->
<script src="{% static 'assets/js/core/jquery-3.7.1.min.js' %}"></script>
<script src="{% static 'assets/js/core/popper.min.js' %}"></script>
<script src="{% static 'assets/js/core/bootstrap.min.js' %}"></script>
<script src="{% static 'assets/js/plugin/datatables/datatables.min.js' %}"></script>
<!-- Plugin JS Files -->
<script src="{% static 'assets/js/plugin/jquery-scrollbar/jquery.scrollbar.min.js' %}"></script>
<script src="{% static 'assets/js/plugin/jquery.sparkline/jquery.sparkline.min.js' %}"></script>
<script src="{% static 'assets/js/plugin/bootstrap-notify/bootstrap-notify.min.js' %}"></script>
<script src="{% static 'assets/js/plugin/jsvectormap/jsvectormap.min.js' %}"></script>
<script src="{% static 'assets/js/plugin/jsvectormap/world.js' %}"></script>
<script src="{% static 'assets/js/sweetalert211.js' %}"></script>
<!-- Chart JS -->
<script src="{% static 'assets/js/plugin/chart.js/chart.min.js' %}"></script>
<!-- Kaiadmin JS -->
<script src="{% static 'assets/js/kaiadmin.min.js' %}"></script>
<script src="{% static 'assets/js/select2.min.js' %}"></script>
<!-- Pasar mensajes de Django a JavaScript como atributo data -->
<div id="django-messages" data-messages='[
{% if messages %}
{% for message in messages %}
{"tags": "{{ message.tags }}", "text": "{{ message | escapejs }}"},
{% endfor %}
{% endif %}
]'>
</div>
<!-- Script personalizado global -->
<script>
// Definir función global al inicio
window.loadPageContent = function(url, pushState = true) {
console.log('Iniciando loadPageContent con URL:', url);
fetch(url, { headers: { 'X-Requested-With': 'XMLHttpRequest' } })
.then(response => {
console.log('Respuesta recibida:', response.status);
if (!response.ok) throw new Error('Network response was not ok');
return response.text();
})
.then(html => {
console.log('HTML recibido:', html.substring(0, 100));
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
// Extraer solo el contenido interno de #page-content si existe, o el body completo
const newContentElement = doc.querySelector('#page-content') || doc.body;
const newContent = newContentElement.innerHTML;
const pageContent = document.getElementById('page-content');
if (pageContent) {
pageContent.innerHTML = newContent; // Reemplazar solo el contenido interno
console.log('Contenido cargado en #page-content');
window.reinitializeScripts(); // Re-inicializar scripts después de cargar
} else {
console.error('No se encontró #page-content en el DOM');
}
if (pushState) window.history.pushState({ url: url }, document.title, url);
})
.catch(error => console.error('Error en loadPageContent:', error));
};
function reinitializeScripts() {
if (typeof $.fn.select2 !== 'undefined') {
$('.select2').select2({ theme: 'bootstrap4', width: '100%' });
}
initializeCustomTables();
if (typeof bootstrap !== 'undefined') {
document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(el => new bootstrap.Tooltip(el));
}
}
// Ejecutar después de cargar el DOM
document.addEventListener('DOMContentLoaded', function () {
// Tema oscuro
const toggleDarkMode = () => {
document.body.classList.toggle('dark-mode');
localStorage.setItem('darkMode', document.body.classList.contains('dark-mode'));
};
if (localStorage.getItem('darkMode') === 'true') {
document.body.classList.add('dark-mode');
}
document.getElementById('darkModeToggle')?.addEventListener('click', toggleDarkMode);
// Obtener mensajes desde el atributo data-messages
const messagesContainer = document.getElementById('django-messages');
let djangoMessages = [];
if (messagesContainer && messagesContainer.getAttribute('data-messages')) {
const messagesStr = messagesContainer.getAttribute('data-messages').replace(/,\s*]/, ']');
djangoMessages = JSON.parse(messagesStr);
}
// Mostrar mensajes con SweetAlert2
if (djangoMessages.length > 0) {
const Toast = Swal.mixin({
toast: true,
position: "top-end",
showConfirmButton: false,
timer: 3000,
timerProgressBar: true,
didOpen: (toast) => {
toast.onmouseenter = Swal.stopTimer;
toast.onmouseleave = Swal.resumeTimer;
},
background: document.body.classList.contains('dark-mode') ? '#2f3545' : '#fff',
color: document.body.classList.contains('dark-mode') ? '#fff' : '#000'
});
djangoMessages.forEach(message => {
Toast.fire({
icon: message.tags,
title: message.text,
customClass: { popup: 'animate__animated animate__fadeInDown' }
});
});
}
});
document.addEventListener('DOMContentLoaded', function() {
const inputs = document.querySelectorAll('.form-control:not([type="number"])');
inputs.forEach(input => {
input.addEventListener('input', function() {
this.value = this.value.toUpperCase();
});
});
});
</script>
<script>
// Configuraciones de tiempo
const SESSION_TIMEOUT = parseInt("{{ session_timeout|default:1800 }}") * 1000; // 30 min
const WARNING_TIME = parseInt("{{ warning_time|default:300 }}") * 1000; // 5 min
// Variables de control
let timer;
let warningTimer;
let countdownInterval;
let isLoggingOut = false;
let modalVisible = false; // Nueva variable para controlar estado del modal
function resetTimer(forceReset = false) {
// Solo reiniciar si el modal no está visible o forzado
if (!modalVisible || forceReset) {
clearTimeout(timer);
clearTimeout(warningTimer);
clearInterval(countdownInterval);
$('#logoutWarning').modal('hide');
modalVisible = false;
warningTimer = setTimeout(() => {
$('#logoutWarning').modal('show');
modalVisible = true;
startCountdown();
}, SESSION_TIMEOUT - WARNING_TIME);
timer = setTimeout(logoutUser, SESSION_TIMEOUT);
}
}
function startCountdown() {
let seconds = Math.floor(WARNING_TIME / 1000);
const countdownElement = document.getElementById('countdown');
countdownElement.textContent = seconds;
countdownInterval = setInterval(() => {
seconds--;
countdownElement.textContent = seconds;
if (seconds <= 0) {
clearInterval(countdownInterval);
logoutUser();
}
}, 1000);
}
// Función de logout (sin cambios)
function logoutUser() {
if (isLoggingOut) return;
isLoggingOut = true;
const form = document.createElement('form');
form.method = 'POST';
form.action = "{% url 'logout' %}";
const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]').value;
const csrfInput = document.createElement('input');
csrfInput.type = 'hidden';
csrfInput.name = 'csrfmiddlewaretoken';
csrfInput.value = csrfToken;
form.appendChild(csrfInput);
document.body.appendChild(form);
form.submit();
}
document.addEventListener('DOMContentLoaded', function () {
// ... (tu código existente de temas y mensajes)
// Modificar el event listener para ignorar actividad cuando el modal está visible
const events = ['mousemove', 'keydown', 'click', 'scroll'];
events.forEach(e => window.addEventListener(e, () => {
if (!modalVisible) { // Solo resetear si el modal NO está visible
resetTimer();
}
}));
// Manejar el cierre del modal
$('#logoutWarning').on('hide.bs.modal', function () {
modalVisible = false;
});
// Iniciar temporizador
resetTimer();
// Botón continuar
document.querySelector('.btn-secondary')?.addEventListener('click', () => resetTimer(true));
});
</script>
{% block js %}{% endblock %}
</body>
</html>

View File

@ -0,0 +1,80 @@
{% extends 'base.html' %}
{% block titulo %}Registro Directores {% endblock %}
{% block vertical %} {% include 'partes/vertical.html' %} {% endblock %}
{% block horizontal %} {% include 'partes/horizontal.html' %} {% endblock %}
{% block seccion %}
<div class="d-flex align-items-left align-items-md-center flex-column flex-md-row pt-2 pb-4">
<div>
<h3 class="fw-bold mb-3">Formulario de Registro de Directores</h3>
<h6 class="op-7 mb-2">Servicio de Transporte del Ejército Bolivariano</h6>
</div>
</div>
{% endblock %}
{% block contenido %}
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-12 col-lg-10">
<div class="card shadow-lg border-primary">
<div class="card-header bg-primary text-white py-3">
<h3 class="mb-0 text-center">
<i class="bi bi-clipboard-data me-2"></i>Registrar Director
</h3>
</div>
<div class="card-body p-4">
<form method="post" class="ajax-form">
{% csrf_token %}
<div class="row mb-3">
<div class="col-md-6">
<label for="{{ form.grado.id_for_label }}" class="form-label">{{ form.grado.label }} <span class="text-danger">*</span></label>
{{ form.grado }}
</div>
<div class="col-md-6">
<label for="{{ form.nombres.id_for_label }}" class="form-label">{{ form.nombres.label }} <span class="text-danger">*</span></label>
{{ form.nombres }}
</div>
</div>
<div class="row mb-3">
<div class="col-md-12">
<label for="{{ form.cargos.id_for_label }}" class="form-label">{{ form.cargos.label }} <span class="text-danger">*</span></label>
{{ form.cargos }}
</div>
</div>
<div class="row mb-3">
<div class="col-md-12 text-end">
<button type="reset" class="btn btn-outline-danger me-2">
<i class="bi bi-eraser me-2"></i>Limpiar
</button>
<button type="submit" class="btn btn-success me-2">
<i class="bi bi-check-circle me-2"></i>Guardar
</button>
<a href="{% url 'listar_directores' %}" class="btn btn-secondary async-link" data-url="{% url 'listar_directores' %}">
<i class="bi bi-x-circle me-2"></i>Cancelar
</a>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block pie %} {% include 'partes/pie.html' %} {% endblock %}

View File

@ -0,0 +1,107 @@
{% extends 'base.html' %}
{% block titulo %}Directores{% endblock %}
{% block vertical %} {% include 'partes/vertical.html' %} {% endblock %}
{% block horizontal %} {% include 'partes/horizontal.html' %} {% endblock %}
{% block seccion %}
<div class="d-flex align-items-left align-items-md-center flex-column flex-md-row pt-2 pb-5">
<div>
<h3 class="fw-bold mb-2">Directores</h3>
<h6 class="op-7 mb-1">Servicio de Transporte del Ejército Bolivariano</h6>
</div>
<div class="ms-md-auto py-1">
<a href="{% url 'crear_director' %}" class="btn btn-primary fw-bold ">Crear Directores</a>
</div>
</div>
{% endblock %}
{% block contenido %}
<div class="card shadow-sm rounded-3 border-0">
<div class="card-header bg-primary py-2 rounded-top">
<h5 class="card-title mb-0 text-white fw-bold"><i class="fas fa-address-card me-1"></i>Lista de Directores</h5>
</div>
<div class="card-body p-2">
<div class="table-responsive">
<table id="directoresTable" class="table table-sm table-striped">
<thead style="background-color: #f8f9fa;">
<tr>
<th class="text-center py-1"></th>
<th class="py-1">Grado</th>
<th class="py-1">Nombres y Apellidos</th>
<th class="py-1">Cargo</th>
<th class="text-center py-1">Acciones</th>
</tr>
</thead>
<tbody>
{% for director in directores %}
<tr>
<td class="text-center py-1 fw-bold">{{ forloop.counter }}</td>
<td class="py-1">{{ director.grado|default:"-" }}</td>
<td class="py-1">{{ director.nombres|default:"-" }}</td>
<td class="py-1">{{ director.cargos|default:"-" }}</td>
<td class="text-center py-1">
<div class="d-flex justify-content-center gap-1">
<a href="{% url 'editar_director' director.pk %}" class="btn btn-primary btn-xs async-link" data-url="{% url 'editar_director' director.pk %}" title="Editar">
<i class="fas fa-pen fa-xs"></i>
</a>
<form action="{% url 'eliminar_director' director.pk %}" method="post" class="d-inline delete-form">
{% csrf_token %}
<button type="submit" class="btn btn-danger btn-xs" title="Eliminar">
<i class="fas fa-trash fa-xs"></i>
</button>
</form>
</div>
</td>
</tr>
{% empty %}
<tr>
<td colspan="10" class="text-center py-1">No hay directores registrados</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}
{% block pie %} {% include 'partes/pie.html' %} {% endblock %}
{% block js %}
<script>
$(document).ready(function() {
$('#directoresTable').DataTable({
"language": {
"decimal": "",
"emptyTable": "No hay datos disponibles",
"info": "Mostrando _START_ a _END_ de _TOTAL_ registros",
"infoEmpty": "Mostrando 0 a 0 de 0 registros",
"infoFiltered": "(filtrado de _MAX_ registros totales)",
"lengthMenu": "Mostrar _MENU_ registros",
"loadingRecords": "Cargando...",
"processing": "Procesando...",
"search": "Buscar:",
"zeroRecords": "No se encontraron registros",
"paginate": {
"first": "Primero",
"last": "Último",
"next": "Siguiente",
"previous": "Anterior"
}
},
"pageLength": 10,
"searching": true,
"paging": true,
"ordering": false,
"info": false,
"lengthChange": true
});
});
</script>
{% endblock %}

View File

@ -0,0 +1,72 @@
{% extends 'base.html' %}
{% block titulo %}Editar Registro de Directores {% endblock %}
{% block vertical %} {% include 'partes/vertical.html' %} {% endblock %}
{% block horizontal %} {% include 'partes/horizontal.html' %} {% endblock %}
{% block seccion %}
<div class="d-flex align-items-left align-items-md-center flex-column flex-md-row pt-2 pb-4">
<div>
<h3 class="fw-bold mb-3">Editar de Registro de Directores</h3>
<h6 class="op-7 mb-2">Servicio de Transporte del Ejército Bolivariano</h6>
</div>
</div>
{% endblock %}
{% block contenido %}
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-12 col-lg-10">
<div class="card shadow-lg border-primary">
<div class="card-header bg-primary text-white py-3">
<h3 class="mb-0 text-center">
<i class="bi bi-clipboard-data me-2"></i>Editar Director
</h3>
</div>
<div class="card-body p-4">
<form method="post" class="ajax-form">
{% csrf_token %}
<div class="row mb-3">
<div class="col-md-6">
<label for="{{ form.grado.id_for_label }}" class="form-label">{{ form.grado.label }} <span class="text-danger">*</span></label>
{{ form.grado }}
</div>
<div class="col-md-6">
<label for="{{ form.nombres.id_for_label }}" class="form-label">{{ form.nombres.label }} <span class="text-danger">*</span></label>
{{ form.nombres }}
</div>
</div>
<div class="row mb-3">
<div class="col-md-12">
<label for="{{ form.cargos.id_for_label }}" class="form-label">{{ form.cargos.label }} <span class="text-danger">*</span></label>
{{ form.cargos }}
</div>
</div>
<div class="row mb-3">
<div class="col-md-12 text-end">
<button type="submit" class="btn btn-success me-2">
<i class="bi bi-check-circle me-2"></i>Actualizar
</button>
<a href="{% url 'listar_directores' %}" class="btn btn-secondary async-link" data-url="{% url 'listar_directores' %}">
<i class="bi bi-x-circle me-2"></i>Cancelar
</a>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,27 @@
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-12 col-lg-10">
<div class="card shadow-lg border-danger">
<div class="card-header bg-danger text-white py-3">
<h3 class="mb-0 text-center">
<i class="bi bi-trash me-2"></i>Eliminar Director
</h3>
</div>
<div class="card-body p-4">
<p>¿Estás seguro de que deseas eliminar al director <strong>{{ object.nombres }}</strong>?</p>
<form method="post" class="ajax-form">
{% csrf_token %}
<div class="text-end">
<button type="submit" class="btn btn-danger me-2">
<i class="bi bi-trash me-2"></i>Eliminar
</button>
<a href="{% url 'listar_directores' %}" class="btn btn-secondary async-link" data-url="{% url 'listar_directores' %}">
<i class="bi bi-x-circle me-2"></i>Cancelar
</a>
</div>
</form>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1 @@
404

View File

@ -0,0 +1 @@
500

View File

@ -0,0 +1,94 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sistema Expirado - Transporte Militar</title>
<link rel="stylesheet" href="/static/assets/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<style>
.expired-card {
max-width: 600px;
margin: 2rem auto;
box-shadow: 0 0 20px rgba(0,0,0,0.3);
border: none;
border-radius: 15px;
overflow: hidden;
}
.warning-icon {
font-size: 5rem;
animation: pulse 1.5s infinite;
color: #dc3545;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}
.gradient-bg {
background: linear-gradient(135deg, #dc3545 0%, #a71d2a 100%);
}
</style>
</head>
<body class="gradient-bg vh-100">
<div class="d-flex align-items-center justify-content-center min-vh-100">
<div class="expired-card">
<div class="card-header bg-dark text-white py-4">
<div class="d-flex align-items-center justify-content-center gap-3">
<i class="bi bi-shield-lock-fill"></i>
<h1 class="mb-0 fw-bold">SISTEMA BLOQUEADO</h1>
</div>
</div>
<div class="card-body bg-light py-5 px-4">
<div class="text-center mb-4">
<i class="bi bi-exclamation-octagon-fill warning-icon"></i>
</div>
<div class="alert alert-danger border-2 fw-bold">
<div class="d-flex gap-3 align-items-center justify-content-center">
<i class="bi bi-calendar-x-fill"></i>
<span>LICENCIA EXPIRADA</span>
</div>
</div>
<div class="mb-4">
<p class="lead text-center text-dark mb-3">
<i class="bi bi-info-circle-fill me-2"></i>
El sistema ha superado la fecha límite de uso autorizado.
</p>
<div class="alert alert-warning d-inline-block">
<div class="d-flex align-items-center gap-2">
<i class="bi bi-clock-history"></i>
<span class="fw-bold">
Fecha de expiración:
<span class="text-danger">{{ expiration_date|date:"d/m/Y" }}</span>
</span>
</div>
</div>
</div>
<div class="text-center mt-4">
<p class="text-muted small mb-3">
<i class="bi bi-building-gear me-2"></i>
Para reactivar el sistema, contacte al Grupo de Trabajo de Ingenieria y Desarrollo.
</p>
<a href="{% url 'login' %}" class="btn btn-danger btn-lg px-5">
<i class="bi bi-box-arrow-left me-2"></i>
Cerrar Sesión
</a>
</div>
</div>
<div class="card-footer bg-dark text-white-50 text-center py-3 small">
<i class="bi bi-c-circle"></i> 2025 Dirección de Tecnologia de la Información y las Comunicaciones
</div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,104 @@
{% extends 'base.html' %}
{% block titulo %} Registrar Vehículo Administrativo {% endblock %}
{% block vertical %} {% include 'partes/vertical.html' %} {% endblock %}
{% block horizontal %} {% include 'partes/horizontal.html' %} {% endblock %}
{% block seccion %}
<div class="d-flex align-items-left align-items-md-center flex-column flex-md-row pt-2 pb-4">
<div>
<h3 class="fw-bold mb-3">Crear un Registro</h3>
<h6 class="op-7 mb-2">Servicio de Transporte del Ejército Bolivariano</h6>
</div>
</div>
{% endblock %}
{% block contenido %}
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-12 col-lg-10">
<div class="card shadow-lg border-primary">
<div class="card-header bg-primary text-white py-3">
<h3 class="mb-0 text-center">
<i class="bi bi-clipboard-data me-2"></i>Registrar Vehículo Administrativo
</h3>
</div>
<div class="card-body p-4">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endfor %}
{% endif %}
<form method="post" class="ajax-form">
{% csrf_token %}
<div class="row mb-3">
<div class="col-md-3">
<label for="{{ form.placa_militar.id_for_label }}" class="form-label">{{ form.placa_militar.label }} <span class="text-danger">*</span></label>
{{ form.placa_militar }}
</div>
<div class="col-md-3">
<label for="{{ form.clase.id_for_label }}" class="form-label">{{ form.clase.label }} <span class="text-danger">*</span></label>
{{ form.clase }}
</div>
<div class="col-md-3">
<label for="{{ form.tipo.id_for_label }}" class="form-label">{{ form.tipo.label }} <span class="text-danger">*</span></label>
{{ form.tipo }}
</div>
<div class="col-md-3">
<label for="{{ form.marca.id_for_label }}" class="form-label">{{ form.marca.label }} <span class="text-danger">*</span></label>
{{ form.marca }}
</div>
</div>
<div class="row mb-3">
<div class="col-md-3">
<label for="{{ form.modelo.id_for_label }}" class="form-label">{{ form.modelo.label }} <span class="text-danger">*</span></label>
{{ form.modelo }}
</div>
<div class="col-md-3">
<label for="{{ form.color.id_for_label }}" class="form-label">{{ form.color.label }} <span class="text-danger">*</span></label>
{{ form.color }}
</div>
<div class="col-md-3">
<label for="{{ form.placa_mtc.id_for_label }}" class="form-label">{{ form.placa_mtc.label }}</label>
{{ form.placa_mtc }}
</div>
<div class="col-md-3">
<label for="{{ form.ano.id_for_label }}" class="form-label">{{ form.ano.label }}</label>
{{ form.ano }}
</div>
</div>
<div class="row mb-3">
<div class="col-md-6">
<label for="{{ form.serial_carroceria.id_for_label }}" class="form-label">{{ form.serial_carroceria.label }} <span class="text-danger">*</span></label>
{{ form.serial_carroceria }}
</div>
<div class="col-md-6 text-end align-self-end">
<button type="reset" class="btn btn-outline-danger me-2">
<i class="bi bi-eraser me-2"></i>Limpiar
</button>
<button type="submit" class="btn btn-primary me-2">
<i class="bi bi-check-circle me-2"></i>Guardar
</button>
<a href="{% url 'listar_inventario_administrativo' %}" class="btn btn-black" data-url="{% url 'listar_inventario_administrativo' %}">
<i class="bi bi-x-circle me-2"></i>Cancelar
</a>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block pie %} {% include 'partes/pie.html' %} {% endblock %}

View File

@ -0,0 +1,62 @@
{% extends 'base.html' %}
{% block titulo %} Editar Vehículo Administrativo {% endblock %}
{% block vertical %} {% include 'partes/vertical.html' %} {% endblock %}
{% block horizontal %} {% include 'partes/horizontal.html' %} {% endblock %}
{% block seccion %}
<div class="d-flex align-items-left align-items-md-center flex-column flex-md-row pt-2 pb-4">
<div>
<h3 class="fw-bold mb-3">Editar Registro del Inventario</h3>
<h6 class="op-7 mb-2">Servicio de Transporte del Ejército Bolivariano</h6>
</div>
</div>
{% endblock %}
{% block contenido %}
<div class="row justify-content-center">
<div class="col-12 col-lg-8 col-xl-6">
<div class="card shadow-lg border-primary">
<div class="card-header bg-primary text-white py-3">
<h3 class="mb-0 text-center">
<i class="bi bi-clipboard-data me-2"></i>Editar Vehículo Administrativo
</h3>
</div>
<div class="card-body p-4">
<form method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<div class="mb-4">
<label for="{{ field.id_for_label }}" class="form-label fw-semibold">
{{ field.label }}
{% if field.field.required %}<span class="text-danger">*</span>{% endif %}
</label>
{{ field }}
{% if field.help_text %}
<small class="form-text text-muted">{{ field.help_text }}</small>
{% endif %}
{% for error in field.errors %}
<div class="invalid-feedback d-block">{{ error }}</div>
{% endfor %}
</div>
{% endfor %}
<div class="d-grid gap-3 d-md-flex justify-content-md-end mt-4 border-top pt-4">
<button type="submit" class="btn btn-success px-4">
<i class="bi bi-check-circle me-2"></i>Guardar
</button>
<a href="{% url 'listar_inventario_administrativo' %}" class="btn btn-secondary px-4">
<i class="bi bi-x-circle me-2"></i>Cancelar
</a>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
{% block pie %} {% include 'partes/pie.html' %} {% endblock %}

View File

@ -0,0 +1,144 @@
{% extends 'base.html' %}
{% block titulo %}Vehículos Administrativos{% endblock %}
{% block vertical %} {% include 'partes/vertical.html' %} {% endblock %}
{% block horizontal %} {% include 'partes/horizontal.html' %} {% endblock %}
{% block pie %} {% include 'partes/pie.html' %} {% endblock %}
{% block seccion %}
<div class="d-flex align-items-left align-items-md-center flex-column flex-md-row pt-1 pb-5">
<div>
<h3 class="fw-bold mb-1">Vehículos Administrativos</h3>
<h6 class="op-7 mb-0">Servicio de Transporte del Ejército Bolivariano</h6>
</div>
<div class="ms-md-auto py-1">
<a href="{% url 'asignacion_administrativa' %}" class="btn btn-info fw-bold rounded-4">Asignaciones</a>
<a href="{% url 'crear_inventario_administrativo' %}" class="btn btn-primary fw-bold rounded-4">Registrar</a>
</div>
</div>
{% endblock %}
{% block contenido %}
{% load humanize %}
<div class="card shadow-sm rounded-3">
<div class="card-header bg-primary py-1 rounded-top">
<h5 class="card-title mb-0 text-white fw-bold ">
<i class="fas fa-clipboard-list me-3"></i>
Inventario de Vehículos Administrativos
</h5>
</div>
<div class="card-body">
<div class="table-responsive">
<table id="vehiculosTable" class="table table-sm table-striped">
<thead style="background-color: #f8f9fa;">
<tr>
<th class="text-center py-0 px-1"></th>
<th class="py-0 px-1">Placa</th>
<th class="text-center py-0 px-1">Clase</th>
<th class="text-center py-0 px-1">Tipo</th>
<th class="text-center py-0 px-1">Marca</th>
<th class="text-center py-0 px-1">Modelo</th>
<th class="text-center py-0 px-1">Serial de Carroceria</th>
<th class="text-center py-0 px-1">Fecha Creación</th>
<!-- Columna Estado agregada -->
<th class="text-center py-0 px-1">Estado</th>
<th class="text-center py-0 px-1">Unidad Asignada</th>
<th class="text-center py-0 px-1">Acciones</th>
</tr>
</thead>
<tbody>
{% for administrativo in inventarios %}
<tr>
<td class="text-center py-0 px-1 fw-bold">{{ forloop.counter }}</td>
<td class="py-0 px-1">{{ administrativo.placa_militar|default:"-" }}</td>
<td class="text-center py-0 px-1">{{ administrativo.clase|default:"-" }}</td>
<td class="text-center py-0 px-1">{{ administrativo.tipo|default:"-" }}</td>
<td class="text-center py-0 px-1">{{ administrativo.marca|default:"-" }}</td>
<td class="text-center py-0 px-1">{{ administrativo.modelo|default:"-" }}</td>
<td class="text-center py-0 px-1">{{ administrativo.serial_carroceria|default:"-" }}</td>
<td class="text-center py-0 px-1">
<span class="badge bg-info text-dark">{{ administrativo.fecha_creacion|date:"d-m-Y" }}</span>
</td>
<!-- Nueva celda para Estado -->
<td class="text-center py-0 px-1">
{% if administrativo.asignaciones_temporales.exists %}
<span class="badge bg-warning">Asignado (Temporal)</span>
{% elif administrativo.asignaciones_historial.exists %}
<span class="badge bg-danger">Asignado</span>
{% else %}
<span class="badge bg-success">Disponible</span>
{% endif %}
</td>
<td class="text-center py-0 px-1">
{% if administrativo.asignaciones_temporales.exists %}
{{ administrativo.asignaciones_temporales.first.unidad.nombre }}
{% elif administrativo.asignaciones_historial.exists %}
{{ administrativo.asignaciones_historial.first.unidad.nombre }}
{% else %}
Sin asignar
{% endif %}
</td>
<td class="text-center py-0 px-1">
<div class="d-flex justify-content-center gap-1">
<a href="{% url 'editar_inventario' administrativo.pk %}" class="btn btn-primary btn-xs async-link" data-url="{% url 'editar_inventario' administrativo.pk %}" title="Editar">
<i class="fas fa-pen fa-xs"></i>
</a>
<form action="{% url 'eliminar_inventario' administrativo.pk %}" method="post" class="d-inline delete-form">
{% csrf_token %}
<button type="submit" class="btn btn-danger btn-xs" title="Eliminar" {% if administrativo.asignaciones_temporales.exists or administrativo.asignaciones_historial.exists %}disabled{% endif %}>
<i class="fas fa-trash fa-xs"></i>
</button>
</form>
</div>
</td>
</tr>
{% empty %}
<tr>
<!-- Ajustado colspan a 13 -->
<td colspan="13" class="text-center py-0 px-1">No hay vehículos registrados</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}
{% block js %}
<script>
$(document).ready(function() {
$('#vehiculosTable').DataTable({
"language": {
"decimal": "",
"emptyTable": "No hay datos disponibles",
"info": "Mostrando _START_ a _END_ de _TOTAL_ registros",
"infoEmpty": "Mostrando 0 a 0 de 0 registros",
"infoFiltered": "(filtrado de _MAX_ registros totales)",
"lengthMenu": "Mostrar _MENU_ registros",
"loadingRecords": "Cargando...",
"processing": "Procesando...",
"search": "Buscar:",
"zeroRecords": "No se encontraron registros",
"paginate": {
"first": "Primero",
"last": "Último",
"next": "Siguiente",
"previous": "Anterior"
}
},
"pageLength": 10,
"searching": true,
"paging": true,
"ordering": false,
"info": false,
"lengthChange": true
});
});
</script>
{% endblock %}

View File

@ -0,0 +1,107 @@
{% extends 'base.html' %}
{% block titulo %} Registrar Vehículo Táctico {% endblock %}
{% block vertical %} {% include 'partes/vertical.html' %} {% endblock %}
{% block horizontal %} {% include 'partes/horizontal.html' %} {% endblock %}
{% block seccion %}
<div class="d-flex align-items-left align-items-md-center flex-column flex-md-row pt-2 pb-4">
<div>
<h3 class="fw-bold mb-3">Registrar un Vehiculo Tactico</h3>
<h6 class="op-7 mb-2">Servicio de Transporte del Ejército Bolivariano</h6>
</div>
</div>
{% endblock %}
{% block contenido %}
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-12 col-lg-10">
<div class="card shadow-lg border-primary">
<div class="card-header bg-primary text-white py-3">
<h3 class="mb-0 text-center">
<i class="bi bi-clipboard-data me-2"></i>Registro de Vehículo Táctico
</h3>
</div>
<div class="card-body p-4">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endfor %}
{% endif %}
<form method="post" novalidate>
{% csrf_token %}
<div class="row mb-3">
<div class="col-md-3">
<label for="{{ form.placa_militar.id_for_label }}" class="form-label">Placa Militar <span class="text-danger">*</span></label>
{{ form.placa_militar }}
</div>
<div class="col-md-3">
<label for="{{ form.clase.id_for_label }}" class="form-label">Clase <span class="text-danger">*</span></label>
{{ form.clase }}
</div>
<div class="col-md-3">
<label for="{{ form.tipo.id_for_label }}" class="form-label">Tipo <span class="text-danger">*</span></label>
{{ form.tipo }}
</div>
<div class="col-md-3">
<label for="{{ form.marca.id_for_label }}" class="form-label">Marca <span class="text-danger">*</span></label>
{{ form.marca }}
</div>
</div>
<div class="row mb-3">
<div class="col-md-3">
<label for="{{ form.modelo.id_for_label }}" class="form-label">Modelo <span class="text-danger">*</span></label>
{{ form.modelo }}
</div>
<div class="col-md-3">
<label for="{{ form.color.id_for_label }}" class="form-label">Color <span class="text-danger">*</span></label>
{{ form.color }}
</div>
<div class="col-md-3">
<label for="{{ form.placa_mtc.id_for_label }}" class="form-label">Placa MTC</label>
{{ form.placa_mtc }}
</div>
<div class="col-md-3">
<label for="{{ form.ano.id_for_label }}" class="form-label">Año</label>
{{ form.ano }}
</div>
</div>
<div class="row mb-3">
<div class="col-md-3">
<label for="{{ form.serial_chasis.id_for_label }}" class="form-label">Serial Chasis <span class="text-danger">*</span></label>
{{ form.serial_chasis }}
</div>
<div class="col-md-3">
<label for="{{ form.serial_motor.id_for_label }}" class="form-label">Serial Motor <span class="text-danger">*</span></label>
{{ form.serial_motor }}
</div>
<div class="col-md-6 text-end align-self-end">
<button type="reset" class="btn btn-outline-danger me-2">
<i class="bi bi-eraser me-2"></i>Limpiar
</button>
<button type="submit" class="btn btn-primary me-2">
<i class="bi bi-check-circle me-2"></i>Guardar
</button>
<a href="{% url 'listar_inventario_tactico' %}" class="btn btn-black">
<i class="bi bi-x-circle me-2"></i>Cancelar
</a>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block pie %} {% include 'partes/pie.html' %} {% endblock %}

View File

@ -0,0 +1,80 @@
{% extends 'base.html' %}
{% block titulo %} Editar Vehículo Táctico {% endblock %}
{% block vertical %} {% include 'partes/vertical.html' %} {% endblock %}
{% block horizontal %} {% include 'partes/horizontal.html' %} {% endblock %}
{% block seccion %}
<div class="d-flex align-items-left align-items-md-center flex-column flex-md-row pt-2 pb-4">
<div>
<h3 class="fw-bold mb-3">Editar Registro del Inventario</h3>
<h6 class="op-7 mb-2">Servicio de Transporte del Ejército Bolivariano</h6>
</div>
</div>
{% endblock %}
{% block contenido %}
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-12 col-lg-8 col-xl-6">
<div class="card shadow-lg border-primary">
<div class="card-header bg-primary text-white py-3">
<h3 class="mb-0 text-center">
<i class="bi bi-clipboard-data me-2"></i>Editar Vehículo Táctico
</h3>
</div>
<div class="card-body p-4">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endfor %}
{% endif %}
<form method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<div class="mb-4">
<label for="{{ field.id_for_label }}" class="form-label fw-semibold">
{{ field.label }}
{% if field.field.required %}<span class="text-danger">*</span>{% endif %}
</label>
{{ field }}
{% if field.help_text %}
<small class="form-text text-muted">{{ field.help_text }}</small>
{% endif %}
{% for error in field.errors %}
<div class="invalid-feedback d-block">{{ error }}</div>
{% endfor %}
</div>
{% endfor %}
<div class="d-grid gap-3 d-md-flex justify-content-md-end mt-4 border-top pt-4">
<button type="submit" class="btn btn-success px-4">
<i class="bi bi-check-circle me-2"></i>Guardar
</button>
<a href="{% url 'listar_inventario_tactico' %}" class="btn btn-secondary px-4">
<i class="bi bi-x-circle me-2"></i>Cancelar
</a>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<style>
.form-control:focus, .form-select:focus {
border-color: #0d6efd;
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
}
.card { border-radius: 15px; }
.card-header { border-radius: 15px 15px 0 0 !important; }
</style>
{% endblock %}
{% block pie %} {% include 'partes/pie.html' %} {% endblock %}

View File

@ -0,0 +1,139 @@
{% extends 'base.html' %}
{% block titulo %}Vehículos Tácticos{% endblock %}
{% block vertical %} {% include 'partes/vertical.html' %} {% endblock %}
{% block horizontal %} {% include 'partes/horizontal.html' %} {% endblock %}
{% block pie %} {% include 'partes/pie.html' %} {% endblock %}
{% block seccion %}
<div class="d-flex align-items-left align-items-md-center flex-column flex-md-row pt-2 pb-5">
<div>
<h3 class="fw-bold mb-1">Mecanizado Táctico</h3>
<h6 class="op-7 mb-0">Servicio de Transporte del Ejército Bolivariano</h6>
</div>
<div class="ms-md-auto py-1">
<a href="{% url 'asignacion_tactica' %}" class="btn btn-info fw-bold rounded-3">Asignaciones</a>
<a href="{% url 'crear_inventario_tactico' %}" class="btn btn-primary fw-bold rounded-3">Registrar</a>
</div>
</div>
{% endblock %}
{% block contenido %}
<div class="card shadow-sm rounded-3 border-0">
<div class="card-header bg-primary py-1 rounded-top">
<h5 class="card-title mb-0 text-white fw-bold"><i class="fas fa-car-alt me-3"></i>Inventario Táctico de Vehículos</h5>
</div>
<div class="card-body p-1">
<div class="table-responsive">
<div class="table-responsive">
< <table id="vehiculosTacticosTable" class="table table-sm table-striped fs-6">
<thead style="background-color: #f8f9fa;">
<tr>
<th class="text-center"></th>
<th class="text-center">Tipo</th>
<th class="text-center">Modelo</th>
<th class="text-center">Marca</th>
<th class="text-center">Serial Chasis</th>
<th class="text-center">Serial Motor</th>
<th class="text-center">Fecha Creación</th>
<th class="text-center">Estado</th>
<th class="text-center">Unidad Asignada</th>
<th class="text-center">Acciones</th>
</tr>
</thead>
<tbody>
{% for tactico in inventarios %}
<tr>
<td class="text-center fw-bold">{{ forloop.counter }}</td>
<td class="text-center">{{ tactico.tipo }}</td>
<td class="text-center">{{ tactico.modelo }}</td>
<td class="text-center">{{ tactico.marca }}</td>
<td class="text-center font-monospace">{{ tactico.serial_chasis }}</td>
<td class="text-center font-monospace">{{ tactico.serial_motor }}</td>
<td class="text-center">
<span class="badge bg-info text-dark">{{ tactico.fecha_creacion|date:"d-m-Y" }}</span>
</td>
<td class="text-center">
{% if tactico.asignaciones_temporales.exists %}
<span class="badge bg-warning">Asignado (Temporal)</span>
{% elif tactico.asignaciones_historial.exists %}
<span class="badge bg-danger">Asignado</span>
{% else %}
<span class="badge bg-success">Disponible</span>
{% endif %}
</td>
<td class="text-center">
{% if tactico.asignaciones_temporales.exists %}
{{ tactico.asignaciones_temporales.first.unidad.nombre }}
{% elif tactico.asignaciones_historial.exists %}
{{ tactico.asignaciones_historial.first.unidad.nombre }}
{% else %}
Sin asignar
{% endif %}
</td>
<td class="text-center">
<div class="d-flex justify-content-center gap-2">
<a href="{% url 'editar_inventario_tactico' tactico.pk %}" class="btn btn-primary btn-sm px-3" data-bs-toggle="tooltip" title="Editar">
<i class="fas fa-pen"></i>
</a>
<form action="{% url 'eliminar_inventario_tactico' tactico.pk %}" method="post" class="d-inline">
{% csrf_token %}
<button type="submit" class="btn btn-danger btn-sm px-3" data-bs-toggle="tooltip" title="Eliminar" {% if tactico.asignaciones_temporales.exists or tactico.asignaciones_historial.exists %}disabled{% endif %}>
<i class="fas fa-trash"></i>
</button>
</form>
</div>
</td>
</tr>
{% empty %}
<tr>
<td colspan="16" class="text-center">No hay vehículos registrados.</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
{% endblock %}
{% block js %}
<script>
$(document).ready(function() {
$('#vehiculosTacticosTable').DataTable({
"language": {
"decimal": "",
"emptyTable": "No hay datos disponibles",
"info": "Mostrando _START_ a _END_ de _TOTAL_ registros",
"infoEmpty": "Mostrando 0 a 0 de 0 registros",
"infoFiltered": "(filtrado de _MAX_ registros totales)",
"lengthMenu": "Mostrar _MENU_ registros",
"loadingRecords": "Cargando...",
"processing": "Procesando...",
"search": "Buscar:",
"zeroRecords": "No se encontraron registros",
"paginate": {
"first": "Primero",
"last": "Último",
"next": "Siguiente",
"previous": "Anterior"
}
},
"pageLength": 10,
"searching": true,
"paging": true,
"ordering": false,
"info": false,
"lengthChange": true
});
});
</script>
{% endblock %}

View File

@ -0,0 +1,104 @@
{% extends 'base.html' %}
{% block titulo %}Registro de Grupo de Trabajos {% endblock %}
{% block vertical %} {% include 'partes/vertical.html' %} {% endblock %}
{% block horizontal %} {% include 'partes/horizontal.html' %} {% endblock %}
{% block seccion %}
<div class="d-flex align-items-left align-items-md-center flex-column flex-md-row pt-2 pb-4">
<div>
<h3 class="fw-bold mb-3">Formulario de Registro de Grupo de Trabajo</h3>
<h6 class="op-7 mb-2">Servicio de Transporte del Ejército Bolivariano</h6>
</div>
</div>
{% endblock %}
{% block contenido %}
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-12 col-lg-10">
<div class="card shadow-lg border-primary">
<div class="card-header bg-primary text-white py-3">
<h3 class="mb-0 text-center">
<i class="bi bi-clipboard-data me-2"></i>Registrar Grupo de Trabajo
</h3>
</div>
<div class="card-body p-4">
<form method="post" class="ajax-form">
{% csrf_token %}
<div class="row mb-3">
<div class="col-md-6">
<label for="{{ form.grado.id_for_label }}" class="form-label">{{ form.grado.label }} <span class="text-danger">*</span></label>
{{ form.grado }}
</div>
<div class="col-md-6">
<label for="{{ form.nombres.id_for_label }}" class="form-label">{{ form.nombres.label }} <span class="text-danger">*</span></label>
{{ form.nombres }}
</div>
</div>
<div class="row mb-3">
<div class="col-md-6">
<label for="{{ form.cargos.id_for_label }}" class="form-label">{{ form.cargos.label }} <span class="text-danger">*</span></label>
{{ form.cargos }}
</div>
<div class="col-md-6">
<label for="{{ form.director.id_for_label }}" class="form-label">{{ form.director.label }} <span class="text-danger">*</span></label>
{{ form.director }}
</div>
</div>
<div class="row mb-3">
<div class="col-md-12 text-end">
<button type="reset" class="btn btn-outline-danger me-2">
<i class="bi bi-eraser me-2"></i>Limpiar
</button>
<button type="submit" class="btn btn-success me-2">
<i class="bi bi-check-circle me-2"></i>Guardar
</button>
<a href="{% url 'listar_subjefes' %}" class="btn btn-secondary async-link" data-url="{% url 'listar_subjefes' %}">
<i class="bi bi-x-circle me-2"></i>Cancelar
</a>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<style>
.card {
border-radius: 15px;
}
.card-header {
border-radius: 15px 15px 0 0 !important;
}
.form-control:focus, .form-select:focus {
border-color: #0d6efd;
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
}
.btn-outline-danger:hover {
background-color: #dc3545;
color: white;
}
</style>
{% endblock %}
{% block pie %} {% include 'partes/pie.html' %} {% endblock %}

View File

@ -0,0 +1,91 @@
{% extends 'base.html' %}
{% block titulo %}Editar Grupo de Trabajos {% endblock %}
{% block vertical %} {% include 'partes/vertical.html' %} {% endblock %}
{% block horizontal %} {% include 'partes/horizontal.html' %} {% endblock %}
{% block seccion %}
<div class="d-flex align-items-left align-items-md-center flex-column flex-md-row pt-2 pb-4">
<div>
<h3 class="fw-bold mb-3">Editar de Registro de Grupo de Trabajo</h3>
<h6 class="op-7 mb-2">Servicio de Transporte del Ejército Bolivariano</h6>
</div>
</div>
{% endblock %}
{% block contenido %}
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-12 col-lg-10">
<div class="card shadow-lg border-primary">
<div class="card-header bg-primary text-white py-3">
<h3 class="mb-0 text-center">
<i class="bi bi-clipboard-data me-2"></i>Editar Grupo de Trabajo
</h3>
</div>
<div class="card-body p-4">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endfor %}
{% endif %}
<form method="post" class="ajax-form">
{% csrf_token %}
<div class="row mb-3">
<div class="col-md-6">
<label for="{{ form.grado.id_for_label }}" class="form-label">{{ form.grado.label }} <span class="text-danger">*</span></label>
{{ form.grado }}
</div>
<div class="col-md-6">
<label for="{{ form.nombres.id_for_label }}" class="form-label">{{ form.nombres.label }} <span class="text-danger">*</span></label>
{{ form.nombres }}
</div>
</div>
<div class="row mb-3">
<div class="col-md-6">
<label for="{{ form.cargos.id_for_label }}" class="form-label">{{ form.cargos.label }} <span class="text-danger">*</span></label>
{{ form.cargos }}
</div>
<div class="col-md-6">
<label for="{{ form.director.id_for_label }}" class="form-label">{{ form.director.label }} <span class="text-danger">*</span></label>
{{ form.director }}
</div>
</div>
<div class="row mb-3">
<div class="col-md-12 text-end">
<button type="submit" class="btn btn-success me-2">
<i class="bi bi-check-circle me-2"></i>Actualizar
</button>
<a href="{% url 'listar_subjefes' %}" class="btn btn-secondary async-link" data-url="{% url 'listar_subjefes' %}">
<i class="bi bi-x-circle me-2"></i>Cancelar
</a>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,36 @@
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-12 col-lg-10">
<div class="card shadow-lg border-danger">
<div class="card-header bg-danger text-white py-3">
<h3 class="mb-0 text-center">
<i class="bi bi-trash me-2"></i>Eliminar Grupo de Trabajo
</h3>
</div>
<div class="card-body p-4">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endfor %}
{% endif %}
<p>¿Estás seguro de que deseas eliminar al grupo <strong>{{ object.nombres }}</strong>?</p>
<form method="post" class="ajax-form">
{% csrf_token %}
<div class="text-end">
<button type="submit" class="btn btn-danger me-2">
<i class="bi bi-trash me-2"></i>Eliminar
</button>
<a href="{% url 'listar_subjefes' %}" class="btn btn-secondary async-link" data-url="{% url 'listar_subjefes' %}">
<i class="bi bi-x-circle me-2"></i>Cancelar
</a>
</div>
</form>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,106 @@
{% extends 'base.html' %}
{% block titulo %}Grupos de Trabajo{% endblock %}
{% block vertical %} {% include 'partes/vertical.html' %} {% endblock %}
{% block horizontal %} {% include 'partes/horizontal.html' %} {% endblock %}
{% block seccion %}
<div class="d-flex align-items-left align-items-md-center flex-column flex-md-row pt-2 pb-5">
<div>
<h3 class="fw-bold mb-2">Grupos de Trabajo</h3>
<h6 class="op-7 mb-1">Servicio de Transporte del Ejército Bolivariano</h6>
</div>
<div class="ms-md-auto py-1">
<a href="{% url 'crear_subjefe' %}" class="btn btn-primary fw-bold rounded-4">Crear Jefe de Grupo</a>
</div>
</div>
{% endblock %}
{% block contenido %}
{% load humanize %}
<div class="card shadow-sm rounded-3 border-0">
<div class="card-header bg-primary py-2 rounded-top">
<h5 class="card-title mb-0 text-white fw-bold"><i class="fas fa-box me-3"></i>Lista de Grupos de Trabajo</h5>
</div>
<div class="card-body p-2">
<div class="table-responsive">
<table id="gruposTable" class="table table-sm table-striped">
<thead style="background-color: #f8f9fa;">
<tr>
<th class="text-center py-1"></th>
<th class="py-1">Grado</th>
<th class="py-1">Nombres y Apellidos</th>
<th class="py-1">Cargo</th>
<th class="py-1">Director</th>
<th class="text-center py-1">Acciones</th>
</tr>
</thead>
<tbody>
{% for subjefe in subjefes %}
<tr>
<td class="text-center py-1 fw-bold">{{ forloop.counter }}</td>
<td class="py-1">{{ subjefe.grado|default:"-" }}</td>
<td class="py-1">{{ subjefe.nombres|default:"-" }}</td>
<td class="py-1">{{ subjefe.cargos|default:"-" }}</td>
<td class="py-1">{{ subjefe.director|default:"-" }}</td>
<td class="text-center py-1">
<div class="d-flex justify-content-center gap-1">
<a href="{% url 'editar_subjefe' subjefe.pk %}" class="btn btn-primary btn-xs async-link" data-url="{% url 'editar_subjefe' subjefe.pk %}" title="Editar">
<i class="fas fa-pen fa-xs"></i>
</a>
<form action="{% url 'eliminar_subjefe' subjefe.pk %}" method="post" class="d-inline delete-form">
{% csrf_token %}
<button type="submit" class="btn btn-danger btn-xs" title="Eliminar">
<i class="fas fa-trash fa-xs"></i>
</button>
</form>
</div>
</td>
</tr>
{% empty %}
<tr>
<td colspan="10" class="text-center py-1">No hay grupos de trabajo registrados</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}
{% block js %}
<script>
$(document).ready(function() {
$('#gruposTable').DataTable({
"language": {
"decimal": "",
"emptyTable": "No hay datos disponibles",
"info": "Mostrando _START_ a _END_ de _TOTAL_ registros",
"infoEmpty": "Mostrando 0 a 0 de 0 registros",
"infoFiltered": "(filtrado de _MAX_ registros totales)",
"lengthMenu": "Mostrar _MENU_ registros",
"loadingRecords": "Cargando...",
"processing": "Procesando...",
"search": "Buscar:",
"zeroRecords": "No se encontraron registros",
"paginate": {
"first": "Primero",
"last": "Último",
"next": "Siguiente",
"previous": "Anterior"
}
},
"pageLength": 10,
"searching": true,
"paging": true,
"ordering": false,
"info": false,
"lengthChange": true
});
});
</script>
{% endblock %}

View File

@ -0,0 +1,165 @@
{% load static %}
<div class="main-header">
<!-- Logo Header -->
<div class="main-header-logo">
<div class="logo-header" data-background-color="dark">
<a href="{% url 'principal' %}" class="logo">
<img
src="{% static 'img/logo.png' %}"
alt="navbar brand"
class="navbar-brand animate__animated animate__pulse animate__infinite"
height="30"
/>
</a>
<div class="nav-toggle">
<button class="btn btn-toggle toggle-sidebar" title="Toggle Sidebar">
<i class="gg-menu-right"></i>
</button>
<button class="btn btn-toggle sidenav-toggler" title="Toggle Sidenav">
<i class="gg-menu-left"></i>
</button>
</div>
<button class="topbar-toggler more" title="Más opciones">
<i class="gg-more-vertical-alt"></i>
</button>
</div>
</div>
<!-- End Logo Header -->
<!-- Navbar Header -->
<nav class="navbar navbar-header navbar-header-transparent navbar-expand-lg border-bottom">
<div class="container-fluid">
<!-- Barra de búsqueda -->
<!-- <nav class="navbar navbar-header-left navbar-expand-lg navbar-form nav-search p-0 d-none d-lg-flex">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-search"></i></span>
</div>
<input type="text" class="form-control" placeholder="Buscar..." id="searchInput">
</div>
</nav>
-->
<!-- Navegación derecha -->
<ul class="navbar-nav topbar-nav ms-md-auto align-items-center">
<!-- Búsqueda móvil -->
<li class="nav-item topbar-icon dropdown hidden-caret d-flex d-lg-none">
<a class="nav-link" href="#" id="mobileSearch" data-bs-toggle="dropdown" aria-expanded="false">
<i class="fa fa-search"></i>
</a>
<ul class="dropdown-menu dropdown-search animated fadeIn" aria-labelledby="mobileSearch">
<li>
<div class="input-group">
<input type="text" class="form-control" placeholder="Buscar...">
<button class="btn btn-primary" type="button"><i class="fa fa-search"></i></button>
</div>
</li>
</ul>
</li>
<!-- Notificaciones (comentadas pero optimizadas) -->
<!--
<li class="nav-item topbar-icon dropdown hidden-caret">
<a class="nav-link dropdown-toggle" href="#" id="notifDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="fa fa-bell"></i>
<span class="notification animate__animated animate__bounce animate__infinite">4</span>
</a>
<ul class="dropdown-menu notif-box animated fadeIn" aria-labelledby="notifDropdown">
<li>
<div class="dropdown-title">Tienes 4 notificaciones</div>
</li>
<li>
<div class="notif-scroll scrollbar-outer">
<div class="notif-center">
<a href="#" class="notif-item">
<div class="notif-icon notif-primary"><i class="fa fa-user-plus"></i></div>
<div class="notif-content">
<span class="block">Nuevo usuario registrado</span>
<span class="time">Hace 5 minutos</span>
</div>
</a>
</div>
</div>
</li>
<li>
<a class="see-all" href="javascript:void(0);">Ver todas las notificaciones <i class="fa fa-angle-right"></i></a>
</li>
</ul>
</li>
-->
<!-- Toggle Modo Oscuro -->
<li class="nav-item topbar-icon hidden-caret">
<button id="darkModeToggle" class="nav-link btn btn-icon" title="Cambiar tema">
<i class="fas fa-moon"></i>
</button>
</li>
<!-- Perfil de usuario -->
<li class="nav-item topbar-user dropdown hidden-caret">
<a class="dropdown-toggle profile-pic" data-bs-toggle="dropdown" href="#" aria-expanded="false">
<div class="avatar-sm">
<img
src="{% static 'img/perfil.png' %}"
alt="Perfil"
class="avatar-img rounded-circle animate__animated animate__zoomIn"
/>
</div>
<span class="profile-username">
<span class="fw-bold d-block">{{ request.user.first_name|default:"Usuario" }} {{ request.user.last_name }}</span>
<span class="op-7 d-block">{{ request.user.username }}</span>
</span>
</a>
<ul class="dropdown-menu dropdown-user animated fadeIn">
<div class="dropdown-user-scroll scrollbar-outer">
<li>
<div class="user-box">
<div class="avatar-lg">
<img src="{% static 'img/perfil.png' %}" alt="Perfil" class="avatar-img rounded" />
</div>
<div class="u-text">
<h4>{{ request.user.first_name|default:"Usuario" }} {{ request.user.last_name }}</h4>
<p class="text-muted">{{ request.user.email|default:"Sin correo" }}</p>
</div>
</div>
</li>
<li>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#">Mi Perfil</a>
<a class="dropdown-item" href="#">Configuración</a>
<div class="dropdown-divider"></div>
<form action="{% url 'logout' %}" method="POST" class="d-inline">
{% csrf_token %}
<button type="submit" class="nav-link btn btn-link">
<a class="dropdown-item">Cerrar Sesión</a>
</button>
</form>
</li>
</div>
</ul>
</li>
</ul>
</div>
</nav>
<!-- End Navbar -->
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
// Animación suave para el toggle del sidebar
const sidebarToggles = document.querySelectorAll('.btn-toggle');
sidebarToggles.forEach(toggle => {
toggle.addEventListener('click', function () {
this.classList.add('animate__animated', 'animate__pulse');
setTimeout(() => this.classList.remove('animate__animated', 'animate__pulse'), 500);
});
});
// Inicializar Select2 en futuros elementos (si los hay)
$('.select2').select2({
theme: 'bootstrap4',
width: '100%'
});
});
</script>

View File

@ -0,0 +1,23 @@
{% load static %}
<footer class="footer">
<div class="container-fluid d-flex justify-content-between">
<nav class="pull-left">
<ul class="nav">
<li class="nav-item">
<a class="nav-link" href="www.ejercito.mil.ve/">
Ejército Bolivariano
</a>
</li>
</ul>
</nav>
<div class="copyright">
2024, Dirección de Tecnologia de la iInformación y las Comunicaciones
<i class="fab fa-linux heart text-danger"></i>
</div>
<div>
Servicio de Transporte..
</div>
</div>
</footer>

View File

@ -0,0 +1,93 @@
{% load static %}
<!-- Sidebar -->
<div class="sidebar" data-background-color="dark" id="sidebar">
<div class="sidebar-logo">
<div class="logo-header" data-background-color="dark">
<a href="{% url 'principal' %}" class="logo" data-url="{% url 'principal' %}" style="display: flex; align-items: center;">
<img
src="{% static 'img/transporte.png' %}"
alt="navbar brand"
class="navbar-brand animate__animated animate__pulse animate__infinite"
height="50"
style="margin-right: 8px;"
/>
<div class="brand-text" style="color: white; font-weight: bold; font-size: 18px; font-family: 'Poppins', sans-serif;">
SERVTRANST
</div>
</a>
<div class="nav-toggle">
<button class="btn btn-toggle toggle-sidebar" title="Colapsar Sidebar">
<i class="gg-menu-right"></i>
</button>
<button class="btn btn-toggle sidenav-toggler" title="Expandir Sidebar">
<i class="gg-menu-left"></i>
</button>
</div>
<button class="topbar-toggler more d-lg-none" title="Más opciones">
<i class="gg-more-vertical-alt"></i>
</button>
</div>
</div>
<div class="sidebar-wrapper scrollbar scrollbar-inner">
<div class="sidebar-content">
<ul class="nav nav-secondary">
<li class="nav-section">
<span class="sidebar-mini-icon">
<i class="fa fa-ellipsis-h"></i>
</span>
<h4 class="text-section">Referencias</h4>
</li>
<li class="nav-item {% if request.path == '/principal/' %}active{% endif %}">
<a href="{% url 'principal' %}" data-bs-toggle="tooltip" title="Inicio">
<i class="fas fa-school"></i>
<p>Inicio</p>
</a>
</li>
<li class="nav-item {% if request.path == '/listar_directores/' %}active{% endif %}">
<a href="{% url 'listar_directores' %}" data-bs-toggle="tooltip" title="Directores">
<i class="fas fa-address-card"></i>
<p>Directores</p>
</a>
</li>
<li class="nav-item {% if request.path == '/listar_subjefes/' %}active{% endif %}">
<a href="{% url 'listar_subjefes' %}" data-bs-toggle="tooltip" title="Grupos de Trabajo">
<i class="fas fa-box"></i>
<p>Grupos</p>
</a>
</li>
<li class="nav-item {% if request.path == '/inventario_administrativo/' %}active{% endif %}">
<a href="{% url 'listar_inventario_administrativo' %}" data-bs-toggle="tooltip" title="Inventario Administrativo">
<i class="fas fa-warehouse"></i>
<p>Administrativo</p>
</a>
</li>
<li class="nav-item {% if request.path == '/inventario_tactico/' %}active{% endif %}">
<a href="{% url 'listar_inventario_tactico' %}" data-bs-toggle="tooltip" title="Inventario Táctico">
<i class="fas fa-warehouse"></i>
<p>Táctico</p>
</a>
</li>
<li class="nav-item {% if request.path == '/unidades/' %}active{% endif %}">
<a href="{% url 'unidad_list' %}" data-bs-toggle="tooltip" title="Unidades">
<i class="fas fa-store-alt"></i>
<p>Unidad</p>
</a>
</li>
<li class="nav-item {% if request.path == '/admin/' %}active{% endif %}">
<a href="{% url 'admin:index' %}"
target="_blank"
rel="noopener noreferrer"
data-bs-toggle="tooltip"
title="Administración">
<i class="fas fa-user-tie"></i>
<p>Admin</p>
</a>
</li>
</ul>
</div>
</div>
</div>
<!-- End Sidebar -->

View File

@ -0,0 +1,159 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>REPORTE {% if tipo == 'administrativo' %}ADMINISTRATIVO{% else %}TÁCTICO{% endif %}</title>
<style>
@page {
size: letter landscape;
margin: 1cm;
}
body {
font-family: Arial, sans-serif;
margin: 0;
}
.header {
text-align: center;
margin-bottom: 20px;
}
.header img {
width: 100px;
height: auto;
}
.title {
font-size: 13px;
font-weight: bold;
}
.table {
width: 100%;
margin-top: 20px;
border-collapse: collapse;
font-size: 10px;
}
.table th, .table td {
border: 1px solid #000;
padding: 4px;
text-align: center;
}
.signatures {
margin-top: 50px;
width: 100%;
}
.signature {
width: 45%;
display: inline-block;
text-align: center;
padding: 10px;
}
</style>
</head>
<body>
<div class="header">
<table>
<tbody>
<tr>
<td colspan="5" style="padding: 10px; text-align:center;">
<img src="{{ img_dos }}" alt="icon" width="80" height="80">
</td>
<td colspan="10">
<p class="title">
REPÚBLICA BOLIVARIANA DE VENEZUELA<br>
MINISTERIO DEL PODER POPULAR PARA LA DEFENSA<br>
EJÉRCITO BOLIVARIANO<br>
DIRECCIÓN LOGÍSTICA DEL EJÉRCITO BOLIVARIANO<br>
SERVICIO DE TRANSPORTE DEL EJÉRCITO BOLIVARIANO<br>
GRUPO DE TRABAJO DE ABASTECIMIENTO
</p>
</td>
<td colspan="5" style="padding: 20px;">
<img src="{{ img_uno }}" alt="icon" width="70">
</td>
</tr>
</tbody>
</table>
</div>
<h3>UNIDAD: {{ unidad_nombre }}</h3>
<h2 class="title" style="text-align: center; margin: 20px 0;">
{% if tipo == 'administrativo' %}MECANIZADA ADMINISTRATIVA{% else %}MECANIZADA TÁCTICA{% endif %}
</h2>
<table class="table">
<thead>
<tr>
<th></th>
{% if tipo == 'tactico' %}
<th>TIPO DE VEHICULO</th>
<th>MODELO</th>
<th>MARCA</th>
<th>SERIAL CHASIS</th>
<th>SERIAL MOTOR</th>
{% else %}
<th>PLACA MILITAR</th>
<th>CLASE</th>
<th>TIPO</th>
<th>MARCA</th>
<th>MODELO</th>
<th>SERIAL CARROCERÍA</th>
{% endif %}
<th>FECHA ASIGNACIÓN</th>
</tr>
</thead>
<tbody>
{% for asignacion in asignaciones %}
<tr>
<td>{{ forloop.counter }}</td>
{% if tipo == 'tactico' %}
<td>{{ asignacion.vehiculo.tipo }}</td>
<td>{{ asignacion.vehiculo.modelo }}</td>
<td>{{ asignacion.vehiculo.marca }}</td>
<td>{{ asignacion.vehiculo.serial_chasis }}</td>
<td>{{ asignacion.vehiculo.serial_motor }}</td>
{% else %}
<td>{{ asignacion.vehiculo.placa_militar }}</td>
<td>{{ asignacion.vehiculo.clase }}</td>
<td>{{ asignacion.vehiculo.tipo }}</td>
<td>{{ asignacion.vehiculo.marca }}</td>
<td>{{ asignacion.vehiculo.modelo }}</td>
<td>{{ asignacion.vehiculo.serial_carroceria }}</td>
{% endif %}
<td>{{ asignacion.fecha_creacion|date:"d/m/Y" }}</td>
</tr>
{% empty %}
<tr>
<td colspan="{% if tipo == 'tactico' %}13{% else %}13{% endif %}" style="text-align: center;">
No hay asignaciones históricas para esta unidad.
</td>
</tr>
{% endfor %}
</tbody>
</table>
<table class="signatures">
<tbody>
<tr>
<td class="signature">
{% if jefe %}
_________________________<br>
{{ jefe.nombres }}<br>
{{ jefe.grado }}<br>
{{ jefe.cargos }}
{% else %}
Jefe no disponible
{% endif %}
</td>
<td class="signature">
{% if director %}
_________________________<br>
{{ director.nombres }}<br>
{{ director.grado }}<br>
{{ director.cargos }}
{% else %}
Director no disponible
{% endif %}
</td>
</tr>
</tbody>
</table>
</body>
</html>

View File

@ -0,0 +1,215 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Comprobante de Transporte</title>
<style>
@page {
size: letter landscape;
margin: 2cm;
}
body {
font-family: "Courier New", Courier, monospace;
font-size: 10pt;
line-height: 1.2;
margin: 0;
padding: 0;
}
.container {
width: 100%;
min-height: 17cm;
position: relative;
}
.header-top {
margin-bottom: 15px;
}
.header-table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
}
.header-table td {
width: 40%;
padding: 0;
vertical-align: top;
}
.header-table td:first-child {
text-align: left;
white-space: nowrap;
}
.header-table td:last-child {
text-align: right;
}
.header-table p {
margin: 0;
font-size: 10pt;
}
.slogan {
text-align: center;
margin: 20px 0;
}
.main-table {
width: 90%;
border-collapse: collapse;
margin: 30px auto;
table-layout: fixed; /* Fuerza anchos fijos */
}
.main-table th, .main-table td {
border: 1px solid black;
padding: 2px;
text-align: center;
font-size: 7pt; /* Tamaño más pequeño para ajustar */
word-wrap: break-word; /* Permite ajustar texto largo */
overflow: hidden; /* Oculta exceso si es necesario */
white-space: normal; /* Permite múltiples líneas */
}
.main-table th {
font-weight: bold;
}
/* Ajuste de anchos proporcionales */
.main-table col:nth-child(1) { width: 1%; }
.main-table col:nth-child(2) { width: 14%; }
.main-table col:nth-child(3) { width: 8%; }
.main-table col:nth-child(4) { width: 7%; }
.main-table col:nth-child(5) { width: 12%; }
.main-table col:nth-child(6) { width: 4%; }
.main-table col:nth-child(7) { width: 20%; }
.main-table col:nth-child(8) { width: 15%; }
.main-table col:nth-child(9) { width: 15%; }
.firmas-table {
width: 100%;
border-collapse: collapse;
position: absolute;
bottom: 0;
margin-bottom: 0;
font-size: 8pt;
margin-top: 150px;
color: rgba(0, 0, 0, 0.5);
}
.firmas-table td {
width: 33.33%;
text-align: center;
padding: 3px;
border: none;
vertical-align: top;
}
.firmas-table p {
margin: 0;
}
.footer {
position: absolute;
bottom: 0;
width: 100%;
text-align: center;
font-size: 8pt;
color: rgba(0, 0, 0, 0.5);
}
</style>
</head>
<body>
<div class="container">
<div class="header-top">
<p><strong>54 BIENES EN DEPÓSITO</strong></p>
</div>
<table class="header-table">
<tr>
<td>
<p><strong>DEL:</strong> SERVICIO DE TRANSPORTE DEL EJÉRCITO BOLIVARIANO</p>
<p><strong>PARA:</strong> {{ unidad_nombre }}</p>
</td>
<td></td>
<td>
<p><strong>COMPROBANTE N°:</strong> _______</p>
<p><strong>CARACAS, 18 JULIO DEL 2024</strong></p>
</td>
</tr>
</table>
<div class="slogan">
<p><strong>CERECOSE / TRANSPORTE</strong></p>
</div>
<table class="main-table">
<colgroup>
<col />
<col />
<col />
<col />
<col />
<col />
<col />
<col />
<col />
</colgroup>
<thead>
<tr>
<th></th>
<th>VEHÍCULO</th>
<th>MODELO</th>
<th>MARCA</th>
<th>COLOR</th>
<th>AÑO</th>
<th>SERIAL DE CARROCERÍA</th>
<th>SERIAL DEL MOTOR</th>
<th>SERIAL</th>
</tr>
</thead>
<tbody>
{% for asignaciones in asignaciones %}
<tr>
<td>01</td>
<td>ADMINISTRATIVO</td>
<td>HILUX</td>
<td>TOYOTA</td>
<td>VERDE MILITAR</td>
<td>1981</td>
<td>WRODXSCDIP2529855</td>
<td>2TRB055068</td>
<td>EJB2859</td>
</tr>
{% endfor %}
</tbody>
</table>
<table class="firmas-table">
<tr>
<td>
{% if jefe %}
<p><strong>{{ jefe.nombres }}</strong></p>
<p><strong> {{ jefe.grado }} </strong></p>
<p><strong>{{ jefe.cargos }}</strong></p>
{% else %}
Jefe no disponible
{% endif %}
</td>
<td>
{% if director %}
<p><strong>{{ director.nombres }}</strong></p>
<p><strong>{{ director.grado }}</strong></p>
<p><strong>{{ director.cargos }}</strong></p>
{% else %}
Director no disponible
{% endif %}
</td>
<td>
<p><strong>RECIBIDO POR:</strong></p>
<p><strong>GRADO:</strong> __________________</p>
<p><strong>NOMBRE:</strong> _________________</p>
<p><strong>FIRMA:</strong> ____________________</p>
</td>
</tr>
</table>
</div>
<div class="footer">
<p><strong>CHÁVEZ VIVE LA PATRIA SIGUE INDEPENDENCIA Y PATRIA SOCIALISTA… VIVIREMOS Y VENCEREMOS</strong></p>
</div>
</body>
</html>

View File

@ -0,0 +1,206 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Comprobante de Transporte</title>
<style>
@page {
size: letter landscape;
margin: 2cm;
}
body {
font-family: "Courier New", Courier, monospace;
font-size: 10pt;
line-height: 1.2;
margin: 0;
padding: 0;
}
.container {
width: 100%;
min-height: 17cm;
position: relative;
}
.header-top {
margin-bottom: 15px;
}
.header-table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
}
.header-table td {
width: 40%;
padding: 0;
vertical-align: top;
}
.header-table td:first-child {
text-align: left;
white-space: nowrap;
}
.header-table td:last-child {
text-align: right;
}
.header-table p {
margin: 0;
font-size: 10pt;
}
.slogan {
text-align: center;
margin: 20px 0;
}
.main-table {
width: 98%;
border-collapse: collapse;
margin: 20px auto;
table-layout: fixed;
}
.main-table th, .main-table td {
border: 1px solid black;
padding: 2px;
text-align: center;
font-size: 7pt;
word-wrap: break-word;
white-space: normal;
}
.main-table th {
font-weight: bold;
background-color: #f0f0f0;
}
.main-table col:nth-child(1) { width: 4%; }
.main-table col:nth-child(2) { width: 10%; }
.main-table col:nth-child(3) { width: 10%; }
.main-table col:nth-child(4) { width: 10%; }
.main-table col:nth-child(5) { width: 8%; }
.main-table col:nth-child(6) { width: 5%; }
.main-table col:nth-child(7) { width: 18%; }
.main-table col:nth-child(8) { width: 18%; }
.main-table col:nth-child(9) { width: 12%; }
.firmas-table {
width: 100%;
border-collapse: collapse;
position: absolute;
bottom: 0;
margin-bottom: 0;
font-size: 10pt;
margin-top: 100px;
color: rgba(0, 0, 0, 0.5);
}
.firmas-table td {
width: 33.33%;
text-align: center;
padding: 3px;
border: none;
vertical-align: top;
}
.firmas-table p {
margin: 0;
}
.footer {
position: absolute;
bottom: 0;
width: 100%;
text-align: center;
font-size: 8pt;
color: rgba(0, 0, 0, 0.5);
}
</style>
</head>
<body>
<div class="container">
<div class="header-top">
<p><strong>54 BIENES EN DEPÓSITO</strong></p>
</div>
<table class="header-table">
<tr>
<td>
<p><strong>DEL:</strong> SERVICIO DE TRANSPORTE DEL EJÉRCITO BOLIVARIANO</p>
<p><strong>PARA:</strong> {{ unidad_nombre }}</p>
</td>
<td></td>
<td>
<p><strong>COMPROBANTE N°:</strong> {% if asignaciones %}{{ asignaciones.first.comprobante }}{% else %}_______{% endif %}</p>
<p><strong>CARACAS, {{ asignaciones.first.fecha_creacion|date:"d F Y"|upper }}</strong></p>
</td>
</tr>
</table>
<div class="slogan">
<p><strong>CERECOSE / TRANSPORTE</strong></p>
</div>
<table class="main-table">
<colgroup>
<col><col><col><col><col><col><col><col><col><col>
</colgroup>
<thead>
<tr>
<th></th>
<th>VEHÍCULO</th>
<th>MODELO</th>
<th>MARCA</th>
<th>COLOR</th>
<th>AÑO</th>
<th>SERIAL DE CARROCERÍA</th>
<th>PLACA MILITAR</th>
<th>PRECIO</th>
</tr>
</thead>
<tbody>
{% for asignacion in asignaciones %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ asignacion.vehiculo.tipo_vehiculo|default:"-" }}</td>
<td>{{ asignacion.vehiculo.modelo|default:"-" }}</td>
<td>{{ asignacion.vehiculo.marca|default:"-" }}</td>
<td>{{ asignacion.vehiculo.color|default:"-" }}</td>
<td>{{ asignacion.vehiculo.ano|default:"-" }}</td>
<td>{{ asignacion.vehiculo.serial_carroceria|default_if_none:"-" }}</td>
<td>{{ asignacion.vehiculo.placa_militar|default:"-" }}</td>
<td>{{ asignacion.precio|floatformat:2 }}</td>
</tr>
{% empty %}
<tr>
<td colspan="10">No hay asignaciones disponibles</td>
</tr>
{% endfor %}
</tbody>
</table>
<table class="firmas-table">
<tr>
<td>
{% if jefe %}
<p><strong>{{ jefe.nombres }}</strong></p>
<p><strong>{{ jefe.grado }}</strong></p>
<p><strong>{{ jefe.cargos }}</strong></p>
{% else %}
<p>Jefe no disponible</p>
{% endif %}
</td>
<td>
{% if director %}
<p><strong>{{ director.nombres }}</strong></p>
<p><strong>{{ director.grado }}</strong></p>
<p><strong>{{ director.cargos }}</strong></p>
{% else %}
<p>Director no disponible</p>
{% endif %}
</td>
<td>
<p><strong>RECIBIDO POR:</strong></p>
<p><strong>GRADO:</strong> __________________</p>
<p><strong>NOMBRE:</strong> _________________</p>
<p><strong>FIRMA:</strong> ____________________</p>
</td>
</tr>
</table>
</div>
<div class="footer">
<p><strong>CHÁVEZ VIVE LA PATRIA SIGUE INDEPENDENCIA Y PATRIA SOCIALISTA… VIVIREMOS Y VENCEREMOS</strong></p>
</div>
</body>
</html>

View File

@ -0,0 +1,162 @@
{% extends 'base.html' %}
{% block titulo %}Servicio de Transporte{% endblock %}
{% block seccion %}
<div class="d-flex align-items-left align-items-md-center flex-column flex-md-row pt-2 pb-4">
<div>
<h3 class="fw-bold mb-3">Panel Principal</h3>
<h6 class="op-7 mb-2">Servicio de Transporte del Ejército Bolivariano</h6>
</div>
</div>
{% endblock %}
{% block contenido %}
{% load humanize %}
<div class="row">
<div class="col-sm-7 col-md-4">
<div class="card card-stats card-round">
<div class="card-body">
<div class="row align-items-center">
<div class="col-icon">
<div class="icon-big text-center icon-primary bubble-shadow-small">
<i class="fas fa-car"></i>
</div>
</div>
<div class="col col-stats ms-3 ms-sm-0">
<div class="numbers">
<p class="card-category">Vehículos Administrativos</p>
<h4 class="card-title">{{ vehiculos_administrativos_count|intcomma }}</h4>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-7 col-md-4">
<div class="card card-stats card-round">
<div class="card-body">
<div class="row align-items-center">
<div class="col-icon">
<div class="icon-big text-center icon-info bubble-shadow-small">
<i class="fas fa-truck-monster"></i>
</div>
</div>
<div class="col col-stats ms-3 ms-sm-0">
<div class="numbers">
<p class="card-category">Vehículos Tácticos</p>
<h4 class="card-title">{{ vehiculos_tacticos_count|intcomma }}</h4>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-7 col-md-4">
<div class="card card-stats card-round">
<div class="card-body">
<div class="row align-items-center">
<div class="col-icon">
<div class="icon-big text-center icon-success bubble-shadow-small">
<i class="fas fa-shield-alt"> + </i> <i class="fas fa-crosshairs"></i>
</div>
</div>
<div class="col col-stats ms-3 ms-sm-0">
<div class="numbers">
<p class="card-category">Unidades</p>
<h4 class="card-title">{{ unidades_count|intcomma }}</h4>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-12">
<div class="card">
<div class="card-header">
<div class="card-title">Grafico de Cantidades Comparativas</div>
</div>
<div class="card-body">
<div class="chart-container">
<canvas
id="pieChart"
style="width: 50%; height: 50%"
></canvas>
</div>
</div>
</div>
</div>
{% endblock %}
{% block js %}
<script>
$(document).ready(function() {
$.ajax({
url: '/api/datos/', // Ruta correcta al endpoint
method: 'GET',
success: function(data) {
var ctx = document.getElementById('pieChart').getContext('2d');
var myPieChart = new Chart(ctx, {
type: "pie",
data: {
datasets: [
{
data: [data.vehiculos_administrativos, data.vehiculos_tacticos, data.unidades], // Asegúrate de que estos campos existan en la respuesta
backgroundColor: ["#1d7af3", "#f3545d", "#fdaf4b"],
borderWidth: 0,
},
],
labels: ['Vehículos Administrativos', 'Vehículos Tácticos', 'Unidades'],
},
options: {
responsive: true,
maintainAspectRatio: false,
legend: {
position: "bottom",
labels: {
fontColor: "rgb(154, 154, 154)",
fontSize: 11,
usePointStyle: true,
padding: 20,
},
},
pieceLabel: {
render: "percentage",
fontColor: "white",
fontSize: 14,
},
tooltips: false,
layout: {
padding: {
left: 20,
right: 20,
top: 20,
bottom: 20,
},
},
},
});
},
error: function(xhr, status, error) {
console.error("Error al obtener los datos: ", error);
}
});
});
</script>
{% endblock %}

View File

@ -0,0 +1,165 @@
{% load static %}
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Acceso al Sistema - Ejército Bolivariano</title>
<link rel="stylesheet" href="/static/assets/css/bootstrap.min.css">
<!-- Font Awesome 6.x (última versión estable) -->
<link rel="stylesheet" href="/static/assets/fonts/iconos/css/all.min.css">
<style>
.preloader {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, 0.9);
z-index: 9999;
display: none;
}
.spinner {
animation: rotate 2s linear infinite;
width: 50px;
height: 50px;
}
@keyframes rotate {
100% { transform: rotate(360deg); }
}
.login-card {
transition: transform 0.3s ease;
background: rgba(255, 255, 255, 0.95);
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
}
.login-card:hover {
transform: translateY(-5px);
}
</style>
</head>
<body class="bg-gradient-primary d-flex justify-content-center align-items-center vh-100" style="background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);">
<!-- Preloader -->
<!-- <div class="preloader d-flex justify-content-center align-items-center">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Cargando...</span>
</div>
</div>
-->
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8 col-lg-6">
<!-- Tarjeta de Login -->
<div class="login-card card p-4">
<div class="card-body">
<!-- Encabezado -->
<div class="text-center mb-4">
<img src="{% static 'img/transporte.png' %}" alt="Escudo Militar" class="mb-3" width="120">
<h2 class="fw-bold text-uppercase mb-1" style="color: #1e3c72;">Servicio de Transporte</h2>
<h4 class="text-muted">Ejército Bolivariano</h4>
</div>
<!-- Mensajes -->
{% if messages %}
<div class="alert-container">
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endfor %}
</div>
{% endif %}
<!-- Formulario -->
<!-- <div class="alert alert-info mt-3">
<small>Licencia válida hasta: {{ expiration_date|date:"d/m/Y" }}</small>
</div> -->
<form method="POST" id="loginForm">
{% csrf_token %}
<!-- Usuario -->
<div class="mb-3">
<label class="form-label text-dark">Usuario</label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-user"></i></span>
<input type="text" name="username" class="form-control" placeholder="Nombre de usuario" required>
</div>
</div>
<!-- Contraseña -->
<div class="mb-4">
<label class="form-label text-dark">Contraseña</label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-lock"></i></span>
<input
type="password"
name="password"
id="password"
class="form-control"
placeholder="Contraseña"
required
autocomplete="current-password"
>
<button class="btn btn-outline-secondary" type="button" id="togglePassword">
<i class="fas fa-eye"></i>
</button>
</div>
</div>
<!-- Botón de acceso -->
<button type="submit" class="btn btn-primary w-100 py-2 fw-bold">
<span class="submit-text">Acceder al Sistema</span>
<div class="spinner-border spinner-border-sm d-none" role="status">
<span class="visually-hidden">Cargando...</span>
</div>
</button>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- Scripts -->
<script src="/static/assets/js/bootstrap.bundle.min.js"></script>
<script>
// Mostrar/ocultar contraseña
document.getElementById('togglePassword').addEventListener('click', function() {
const password = document.getElementById('password');
const icon = this.querySelector('i');
if (password.type === 'password') {
password.type = 'text';
icon.classList.replace('fa-eye', 'fa-eye-slash');
} else {
password.type = 'password';
icon.classList.replace('fa-eye-slash', 'fa-eye');
}
});
// Manejar el preloader
document.getElementById('loginForm').addEventListener('submit', function(e) {
const btn = this.querySelector('button[type="submit"]');
const preloader = document.querySelector('.preloader');
btn.disabled = true;
btn.querySelector('.submit-text').classList.add('d-none');
btn.querySelector('.spinner-border').classList.remove('d-none');
preloader.style.display = 'flex';
});
// Animación de entrada
window.addEventListener('load', () => {
document.querySelector('.login-card').style.opacity = '1';
document.querySelector('.login-card').style.transform = 'translateY(0)';
});
</script>
</body>
</html>

View File

@ -0,0 +1,83 @@
{% extends 'base.html' %}
{% block titulo %} Registrar Unidad {% endblock %}
{% block vertical %} {% include 'partes/vertical.html' %} {% endblock %}
{% block horizontal %} {% include 'partes/horizontal.html' %} {% endblock %}
{% block seccion %}
<div class="d-flex align-items-left align-items-md-center flex-column flex-md-row pt-2 pb-4">
<div>
<h3 class="fw-bold mb-3">Crear Unidad</h3>
<h6 class="op-7 mb-2">Servicio de Transporte del Ejército Bolivariano</h6>
</div>
</div>
{% endblock %}
{% block contenido %}
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-8 col-lg-6">
<div class="card shadow-lg border-primary">
<div class="card-header bg-primary text-white py-3">
<h3 class="mb-0 text-center">
<i class="bi bi-building me-2"></i>Registro de Unidad
</h3>
</div>
<div class="card-body p-4">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endfor %}
{% endif %}
<form method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<div class="mb-3">
<label for="{{ field.id_for_label }}" class="form-label fw-semibold">
{{ field.label }}
{% if field.field.required %}<span class="text-danger">*</span>{% endif %}
</label>
{{ field }}
{% if field.help_text %}
<div class="form-text text-muted">{{ field.help_text }}</div>
{% endif %}
{% for error in field.errors %}
<div class="invalid-feedback d-block">{{ error }}</div>
{% endfor %}
</div>
{% endfor %}
<div class="d-grid gap-2 d-md-flex justify-content-md-end mt-4 border-top pt-4">
<button type="reset" class="btn btn-outline-danger me-md-2">
<i class="bi bi-eraser me-2"></i>Limpiar
</button>
<button type="submit" class="btn btn-primary px-4">
<i class="bi bi-save me-2"></i>Guardar
</button>
<a href="{% url 'unidad_list' %}" class="btn btn-black px-4">
<i class="bi bi-arrow-left me-2"></i>Volver
</a>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<style>
.card { border-radius: 15px; }
.card-header { border-radius: 15px 15px 0 0 !important; }
.form-control:focus, .form-select:focus {
border-color: #0d6efd;
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
}
</style>
{% endblock %}
{% block pie %} {% include 'partes/pie.html' %} {% endblock %}

View File

@ -0,0 +1,201 @@
{% extends 'base.html' %}
{% block titulo %}Detalles de la Unidad{% endblock %}
{% block vertical %} {% include 'partes/vertical.html' %} {% endblock %}
{% block horizontal %} {% include 'partes/horizontal.html' %} {% endblock %}
{% block pie %} {% include 'partes/pie.html' %} {% endblock %}
{% block css %}
<style>
#administrativoTable th, #tacticoTable th,
#administrativoTable td, #tacticoTable td {
vertical-align: middle;
max-width: 150px;
word-wrap: break-word;
white-space: normal;
}
</style>
{% endblock %}
{% block seccion %}
<div class="d-flex align-items-left align-items-md-center flex-column flex-md-row pt-2 pb-5">
<div>
<h3 class="fw-bold mb-1">Información de Vehículos</h3>
<h6 class="op-7 mb-0">Servicio de Transporte del Ejército Bolivariano</h6>
</div>
<div class="ms-md-auto py-1">
<a href="{% url 'unidad_list' %}" class="btn btn-dark fw-bold ">Atrás</a>
</div>
</div>
{% endblock %}
{% block contenido %}
<div class="card card-stats card-round mb-2">
<div class="card-header bg-primary py-1 rounded-top">
<div class="d-flex justify-content-between align-items-center">
<h4 class="card-title mb-0 text-white fw-bold ">Vehículos Administrativos</h4>
<a href="{% url 'pdf_admin' unidad.pk %}" class="btn btn-primary btn-sm">
<i class="fas fa-file-pdf me-1"></i>Exportar PDF
</a>
</div>
</div>
<div class="card-body p-1">
<div class="table-responsive">
<table id="administrativoTable" class="table table-sm table-striped">
<thead style="background-color: #f8f9fa;">
<tr>
<th class="text-center py-0 px-1"></th>
<th class="py-0 px-1">Placa Militar</th>
<th class="py-0 px-1">Clase</th>
<th class="py-0 px-1">Tipo</th>
<th class="py-0 px-1">Marca</th>
<th class="py-0 px-1">Modelo</th>
<th class="py-0 px-1">Serial de Carrocería</th>
<th class="py-0 px-1">Fecha de Asignación</th>
</tr>
</thead>
<tbody>
{% for administrativo in administrativo %}
<tr>
<td class="text-center py-0 px-1">{{ forloop.counter }}</td>
<td class="py-0 px-1">{{ administrativo.vehiculo.placa_militar|default:"-" }}</td>
<td class="py-0 px-1">{{ administrativo.vehiculo.clase|default:"-" }}</td>
<td class="py-0 px-1">{{ administrativo.vehiculo.tipo|default:"-" }}</td>
<td class="py-0 px-1">{{ administrativo.vehiculo.marca|default:"-" }}</td>
<td class="py-0 px-1">{{ administrativo.vehiculo.modelo|default:"-" }}</td>
<td class="py-0 px-1">{{ administrativo.vehiculo.serial_carroceria|default:"-" }}</td>
<td class="py-0 px-1">{{ administrativo.fecha_creacion|date:"d-m-Y"|default:"-" }}</td>
<td class="py-0 px-1">
<form action="{% url 'eliminar_asignacion_admin' administrativo.pk %}" method="post">
{% csrf_token %}
<button type="submit" class="btn btn-danger btn-sm">Eliminar</button>
</form>
</td>
</tr>
{% empty %}
<tr>
<td colspan="8" class="text-center py-0 px-1">No hay vehículos administrativos asignados</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<div class="card card-stats card-round">
<div class="card-header bg-primary py-1 rounded-top">
<div class="d-flex justify-content-between align-items-center">
<h4 class="card-title mb-0 text-white fw-bold">Vehículos Tácticos</h4>
<a href="{% url 'pdf_tactico' unidad.pk %}" class="btn btn-primary ">
<i class="fas fa-file-pdf me-1"></i>Exportar PDF
</a>
</div>
</div>
<div class="card-body p-1">
<div class="table-responsive">
<table id="tacticoTable" class="table table-sm table-striped fs-6">
<thead style="background-color: #f8f9fa;">
<tr>
<th class="text-center py-0 px-1"></th>
<th class="py-0 px-1">Tipo de Vehículo</th>
<th class="py-0 px-1">Modelo</th>
<th class="py-0 px-1">Marca</th>
<th class="py-0 px-1">Serial Chasis</th>
<th class="py-0 px-1">Serial Motor</th>
<th class="py-0 px-1">Fecha de Asignación</th>
</tr>
</thead>
<tbody>
{% for tactico in tactico %}
<tr>
<td class="text-center py-0 px-1">{{ forloop.counter }}</td>
<td class="py-0 px-1">{{ tactico.vehiculo.tipo_vehiculo|default:"-" }}</td>
<td class="py-0 px-1">{{ tactico.vehiculo.modelo|default:"-" }}</td>
<td class="py-0 px-1">{{ tactico.vehiculo.marca|default:"-" }}</td>
<td class="py-0 px-1">{{ tactico.vehiculo.serial_chasis|default:"-" }}</td>
<td class="py-0 px-1">{{ tactico.vehiculo.serial_motor|default:"-" }}</td>
<td class="py-0 px-1">{{ tactico.fecha_creacion|date:"d-m-Y"|default:"-" }}</td>
<td>
<form action="{% url 'eliminar_asignacion_tactica' tactico.pk %}" method="post">
{% csrf_token %}
<button type="submit" class="btn btn-danger btn-sm">Eliminar</button>
</form>
</td>
</tr>
{% empty %}
<tr>
<td colspan="7" class="text-center py-0 px-1">No hay vehículos tácticos asignados</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}
{% block js %}
<script>
$(document).ready(function() {
$('#administrativoTable').DataTable({
"language": {
"decimal": "",
"emptyTable": "No hay datos disponibles",
"info": "Mostrando _START_ a _END_ de _TOTAL_ registros",
"infoEmpty": "Mostrando 0 a 0 de 0 registros",
"infoFiltered": "(filtrado de _MAX_ registros totales)",
"lengthMenu": "Mostrar _MENU_ registros",
"loadingRecords": "Cargando...",
"processing": "Procesando...",
"search": "Buscar:",
"zeroRecords": "No se encontraron registros",
"paginate": {
"first": "Primero",
"last": "Último",
"next": "Siguiente",
"previous": "Anterior"
}
},
"pageLength": 10,
"searching": true,
"paging": true,
"ordering": false,
"info": false,
"lengthChange": true
});
$('#tacticoTable').DataTable({
"language": {
"decimal": "",
"emptyTable": "No hay datos disponibles",
"info": "Mostrando _START_ a _END_ de _TOTAL_ registros",
"infoEmpty": "Mostrando 0 a 0 de 0 registros",
"infoFiltered": "(filtrado de _MAX_ registros totales)",
"lengthMenu": "Mostrar _MENU_ registros",
"loadingRecords": "Cargando...",
"processing": "Procesando...",
"search": "Buscar:",
"zeroRecords": "No se encontraron registros",
"paginate": {
"first": "Primero",
"last": "Último",
"next": "Siguiente",
"previous": "Anterior"
}
},
"pageLength": 10,
"searching": true,
"paging": true,
"ordering": false,
"info": false,
"lengthChange": true
});
});
</script>
{% endblock %}

View File

@ -0,0 +1,80 @@
{% extends 'base.html' %}
{% block titulo %} Editar Unidad {% endblock %}
{% block vertical %} {% include 'partes/vertical.html' %} {% endblock %}
{% block horizontal %} {% include 'partes/horizontal.html' %} {% endblock %}
{% block seccion %}
<div class="d-flex align-items-left align-items-md-center flex-column flex-md-row pt-2 pb-4">
<div>
<h3 class="fw-bold mb-3">Editar Unidad</h3>
<h6 class="op-7 mb-2">Servicio de Transporte del Ejército Bolivariano</h6>
</div>
</div>
{% endblock %}
{% block contenido %}
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-8 col-lg-6">
<div class="card shadow-lg border-primary">
<div class="card-header bg-primary text-white py-3">
<h3 class="mb-0 text-center">
<i class="bi bi-building me-2"></i>Editar Unidad
</h3>
</div>
<div class="card-body p-4">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endfor %}
{% endif %}
<form method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<div class="mb-3">
<label for="{{ field.id_for_label }}" class="form-label fw-semibold">
{{ field.label }}
{% if field.field.required %}<span class="text-danger">*</span>{% endif %}
</label>
{{ field }}
{% if field.help_text %}
<div class="form-text text-muted">{{ field.help_text }}</div>
{% endif %}
{% for error in field.errors %}
<div class="invalid-feedback d-block">{{ error }}</div>
{% endfor %}
</div>
{% endfor %}
<div class="d-grid gap-2 d-md-flex justify-content-md-end mt-4 border-top pt-4">
<button type="submit" class="btn btn-primary px-4">
<i class="bi bi-save me-2"></i>Guardar
</button>
<a href="{% url 'unidad_list' %}" class="btn btn-secondary px-4">
<i class="bi bi-arrow-left me-2"></i>Volver
</a>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<style>
.card { border-radius: 15px; }
.card-header { border-radius: 15px 15px 0 0 !important; }
.form-control:focus, .form-select:focus {
border-color: #0d6efd;
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
}
</style>
{% endblock %}
{% block pie %} {% include 'partes/pie.html' %} {% endblock %}

View File

@ -0,0 +1,60 @@
{% extends 'base.html' %}
{% block titulo %} Eliminar Unidad {% endblock %}
{% block vertical %} {% include 'partes/vertical.html' %} {% endblock %}
{% block horizontal %} {% include 'partes/horizontal.html' %} {% endblock %}
{% block seccion %}
<div class="d-flex align-items-left align-items-md-center flex-column flex-md-row pt-2 pb-4">
<div>
<h3 class="fw-bold mb-3">Eliminar Unidad</h3>
<h6 class="op-7 mb-2">Servicio de Transporte del Ejército Bolivariano</h6>
</div>
</div>
{% endblock %}
{% block contenido %}
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-8 col-lg-6">
<div class="card shadow-lg border-danger">
<div class="card-header bg-danger text-white py-3">
<h3 class="mb-0 text-center">
<i class="bi bi-trash me-2"></i>Confirmar Eliminación
</h3>
</div>
<div class="card-body p-4">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endfor %}
{% endif %}
<p class="text-center">¿Estás seguro de que deseas eliminar la unidad <strong>{{ unidades.nombre }}</strong>?</p>
<form method="post" class="text-center">
{% csrf_token %}
<button type="submit" class="btn btn-danger px-4">
<i class="bi bi-trash me-2"></i>Eliminar
</button>
<a href="{% url 'unidad_list' %}" class="btn btn-secondary px-4">
<i class="bi bi-x-circle me-2"></i>Cancelar
</a>
</form>
</div>
</div>
</div>
</div>
</div>
<style>
.card { border-radius: 15px; }
.card-header { border-radius: 15px 15px 0 0 !important; }
</style>
{% endblock %}
{% block pie %} {% include 'partes/pie.html' %} {% endblock %}

View File

@ -0,0 +1,139 @@
{% extends 'base.html' %}
{% block css %}
<style>
#unidadesTable th, #unidadesTable td {
vertical-align: middle;
max-width: 150px;
word-wrap: break-word;
white-space: normal;
}
.card { border-radius: 15px; overflow: hidden; }
.table-hover tbody tr:hover { background-color: rgba(13, 110, 253, 0.03) !important; }
.dataTables_wrapper .dataTables_filter input {
border: 1px solid #dee2e6; border-radius: 20px; padding: 0.375rem 1.75rem;
}
.dataTables_wrapper .dataTables_paginate .paginate_button {
border-radius: 50% !important; margin: 0 3px; border: none !important;
}
</style>
{% endblock %}
{% block titulo %}Unidades{% endblock %}
{% block vertical %} {% include 'partes/vertical.html' %} {% endblock %}
{% block horizontal %} {% include 'partes/horizontal.html' %} {% endblock %}
{% block pie %} {% include 'partes/pie.html' %} {% endblock %}
{% block seccion %}
<div class="d-flex align-items-left align-items-md-center flex-column flex-md-row pt-2 pb-5">
<div>
<h3 class="fw-bold mb-1">Unidades</h3>
<h6 class="op-7 mb-0">Servicio de Transporte del Ejército Bolivariano</h6>
</div>
<div class="ms-md-auto py-1">
<a href="{% url 'unidad_create' %}" class="btn btn-primary fw-bold rounded-3 ">Agregar Unidad</a>
</div>
</div>
{% endblock %}
{% block contenido %}
<div class="card shadow-sm rounded-3 border-0">
<div class="card-header bg-primary py-1 rounded-top">
<h5 class="card-title mb-0 text-white fw-bold "><i class="fas fa-users-cog me-3"></i>Inventario de Unidades</h5>
</div>
<div class="card-body p-1">
<div class="table-responsive">
<table id="unidadesTable" class="table table-sm table-striped fs-6" style="width:100%">
<thead style="background-color: #f8f9fa;">
<tr>
<th class="text-center py-0 px-1"></th>
<th class="py-0 px-1">Nombre de la Unidad</th>
<th class="text-center py-0 px-1">Acciones</th>
</tr>
</thead>
<tbody>
{% for unidad in unidades %}
<tr>
<td class="text-center py-0 px-1 fw-bold">{{ forloop.counter }}</td>
<td class="py-0 px-1">{{ unidad.nombre|default:"-" }}</td>
<td class="text-center py-0 px-1">
<div class="d-flex justify-content-center gap-1">
<a href="{% url 'detalle' unidad.pk %}"
class="btn btn-info btn-xs"
data-bs-toggle="tooltip"
title="Ver detalle">
<i class="far fa-eye fa-xs"></i>
</a>
<a href="{% url 'unidad_edit' unidad.pk %}"
class="btn btn-primary btn-xs"
data-bs-toggle="tooltip"
title="Editar">
<i class="fas fa-pen fa-xs"></i>
</a>
<form action="{% url 'unidad_delete' unidad.pk %}" method="post" class="d-inline">
{% csrf_token %}
<button type="submit"
class="btn btn-danger btn-xs"
data-bs-toggle="tooltip"
title="Eliminar">
<i class="fas fa-trash fa-xs"></i>
</button>
</form>
</div>
</td>
</tr>
{% empty %}
<tr>
<td colspan="3" class="text-center py-0 px-1">No hay unidades registradas</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<!-- DataTables mínimo -->
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/dataTables.bootstrap5.min.css">
<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.13.6/js/dataTables.bootstrap5.min.js"></script>
<script>
$(document).ready(function() {
$('#unidadesTable').DataTable({
"language": {
"decimal": "",
"emptyTable": "No hay datos disponibles",
"info": "Mostrando _START_ a _END_ de _TOTAL_ registros",
"infoEmpty": "Mostrando 0 a 0 de 0 registros",
"infoFiltered": "(filtrado de _MAX_ registros totales)",
"lengthMenu": "Mostrar _MENU_ registros",
"loadingRecords": "Cargando...",
"processing": "Procesando...",
"search": "Buscar:",
"zeroRecords": "No se encontraron registros",
"paginate": {
"first": "Primero",
"last": "Último",
"next": "Siguiente",
"previous": "Anterior"
}
},
"pageLength": 10,
"searching": true,
"paging": true,
"ordering": false,
"info": false,
"lengthChange": false,
"autoWidth": false
});
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl));
});
</script>
{% endblock %}

View File

View File

@ -0,0 +1,32 @@
from django import template
register = template.Library()
@register.filter
def get_item(dictionary, key):
"""Devuelve el valor de un diccionario para una clave, o 0 si no es válido."""
if isinstance(dictionary, dict):
return dictionary.get(key, 0)
return 0 # Retorna 0 si no es un diccionario
@register.filter
def sum_values(dictionary):
"""Suma los valores de un diccionario. Si no es un diccionario, devuelve 0."""
if isinstance(dictionary, dict):
return sum(dictionary.values())
return 0 # Retorna 0 si el argumento no es un diccionario
def get_total_semana_anterior(datos):
# Flatten nested dictionaries and sum their values
if all(isinstance(value, dict) for value in datos.values()):
return sum(sum(inner_dict.values()) for inner_dict in datos.values())
elif all(isinstance(value, (int, float)) for value in datos.values()):
return sum(datos.values())
else:
raise ValueError("Estructura inesperada en 'datos': los valores deben ser dictados o números.")
@register.filter
def get_total_semana_actual(datos):
return sum(datos.values())

3
intendencia/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

71
intendencia/urls.py Normal file
View File

@ -0,0 +1,71 @@
from django.urls import path
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.contrib.auth.views import LoginView, LogoutView
from django.contrib.auth import views as auth_views
from intendencia import views
from django.views.decorators.http import require_POST
urlpatterns = [
path('', views.principal, name='principal'),
path('api/datos/', views.obtener_datos, name='obtener_datos'),
# Autenticación
path('login/', views.CustomLoginView.as_view(), name='login'),
path('logout/', LogoutView.as_view(next_page=settings.LOGOUT_REDIRECT_URL), name='logout'),
# Directores
path('directores/', views.DirectoresListView.as_view(), name='listar_directores'),
path('directores/crear/', views.DirectoresCreateView.as_view(), name='crear_director'),
path('directores/editar/<int:pk>/', views.DirectoresUpdateView.as_view(), name='editar_director'),
path('directores/eliminar/<int:pk>/', views.DirectoresDeleteView.as_view(), name='eliminar_director'),
# Subjefes
path('jefes/', views.SubjefesListView.as_view(), name='listar_subjefes'),
path('jefes/crear/', views.SubjefesCreateView.as_view(), name='crear_subjefe'),
path('jefes/editar/<int:pk>/', views.SubjefesUpdateView.as_view(), name='editar_subjefe'),
path('jefes/eliminar/<int:pk>/', views.SubjefesDeleteView.as_view(), name='eliminar_subjefe'),
# Inventario Administrativo
path('inventario_administrativo/', views.InventarioAdministrativoListView.as_view(), name='listar_inventario_administrativo'),
path('inventario_administrativo/crear/', views.InventarioAdministrativoCreateView.as_view(), name='crear_inventario_administrativo'),
path('inventario_administrativo/editar/<int:pk>/', views.InventarioAdministrativoUpdateView.as_view(), name='editar_inventario'),
path('inventario_administrativo/eliminar/<int:pk>/', views.InventarioAdministrativoDeleteView.as_view(), name='eliminar_inventario'),
# Inventario Táctico
path('inventario_tactico/', views.InventarioTacticoListView.as_view(), name='listar_inventario_tactico'),
path('inventario_tactico/crear/', views.InventarioTacticoCreateView.as_view(), name='crear_inventario_tactico'),
path('inventario_tactico/editar/<int:pk>/', views.InventarioTacticoUpdateView.as_view(), name='editar_inventario_tactico'),
path('inventario_tactico/eliminar/<int:pk>/', views.InventarioTacticoDeleteView.as_view(), name='eliminar_inventario_tactico'),
# Unidades
path('unidad/', views.UnidadListView.as_view(), name='unidad_list'),
path('unidad/crear/', views.UnidadCreateView.as_view(), name='unidad_create'),
path('unidad/detalle/<int:id>/', views.detalle_unidad, name='detalle'),
path('unidad/editar/<int:pk>/', views.UnidadUpdateView.as_view(), name='unidad_edit'),
path('unidad/eliminar/<int:pk>/', views.UnidadDeleteView.as_view(), name='unidad_delete'),
# Asignaciones
path('asignacion_administrativa/', views.asignacion_administrativa, name='asignacion_administrativa'),
path('asignacion_tactica/', views.asignacion_tactica, name='asignacion_tactica'),
path('eliminar_todo/', views.eliminar_todo, name='eliminar_todo'),
path('eliminar_todo_tactico/', views.eliminar_todo_tactico, name='eliminar_todo_tactico'),
path('generar_pdf/', views.generar_pdf, name='generar_pdf'),
path('generar_pdf_tactico/', views.generar_pdf_tactico, name='generar_pdf_tactico'),
path('pdf/administrativo/<int:unidad_id>/', views.detalle_pdf, {'tipo': 'administrativo'}, name='pdf_admin'),
path('pdf/tactico/<int:unidad_id>/', views.detalle_pdf, {'tipo': 'tactico'}, name='pdf_tactico'),
path('eliminar_asignacion_admin/<int:pk>/', views.eliminar_asignacion_admin, name='eliminar_asignacion_admin'),
path('eliminar_asignacion_tactica/<int:pk>/', views.eliminar_asignacion_tactica, name='eliminar_asignacion_tactica'),
path('expired/', views.expired_page, name='expired'),
]
if settings.DEBUG:
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

613
intendencia/views.py Normal file
View File

@ -0,0 +1,613 @@
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)

51
launcher.py Normal file
View File

@ -0,0 +1,51 @@
import os
import sys
import threading
import subprocess
import time
from waitress import serve
from django.core.wsgi import get_wsgi_application
# Configuración básica de rutas
if getattr(sys, 'frozen', False):
BASE_DIR = os.path.dirname(sys.executable)
APP_DATA_DIR = os.path.join(os.environ['APPDATA'], 'Intendencia')
else:
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
APP_DATA_DIR = os.path.join(BASE_DIR, 'data')
os.makedirs(APP_DATA_DIR, exist_ok=True)
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'sistema.settings')
def start_server():
"""Inicia el servidor WSGI con Waitress"""
application = get_wsgi_application()
serve(application, host='127.0.0.1', port=8000, threads=4)
if __name__ == '__main__':
# Iniciar el servidor en un hilo
server_thread = threading.Thread(target=start_server, daemon=True)
server_thread.start()
# Esperar un momento para asegurarse de que el servidor esté listo
time.sleep(1)
# Ruta por defecto de Chrome en Windows (ajústala si usas otro sistema o instalación personalizada)
chrome_path = r"C:\Program Files\Google\Chrome\Application\chrome.exe"
url = "http://127.0.0.1:8000"
# Abrir Chrome en modo aplicación
try:
subprocess.Popen([chrome_path, f"--app={url}"])
except FileNotFoundError:
print("No se encontró Google Chrome. Abriendo el navegador predeterminado...")
import webbrowser
webbrowser.open(url)
# Mantener la aplicación viva
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print("Cerrando la aplicación...")
sys.exit(0)

22
manage.py Normal file
View File

@ -0,0 +1,22 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'sistema.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()

5
runtime-hook.py Normal file
View File

@ -0,0 +1,5 @@
# Fix circular imports
import sys
import http.cookies
sys.modules['http.cookies'] = http.cookies

0
sistema/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More