first commit: sistema de gestión de inventario y asignación de vehículos
This commit is contained in:
commit
92428903a2
73
README.md
Normal file
73
README.md
Normal 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
46
ServTransporte.iss
Normal 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
BIN
db.sqlite3
Normal file
Binary file not shown.
3
hook/hook-http.py
Normal file
3
hook/hook-http.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from PyInstaller.utils.hooks import collect_submodules
|
||||||
|
|
||||||
|
hiddenimports = collect_submodules('http')
|
||||||
4
hook/hook-jaraco.py
Normal file
4
hook/hook-jaraco.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
hiddenimports = [
|
||||||
|
'jaraco.context',
|
||||||
|
'jaraco.text'
|
||||||
|
]
|
||||||
97
intendencia.spec
Normal file
97
intendencia.spec
Normal 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
0
intendencia/__init__.py
Normal file
BIN
intendencia/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
intendencia/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
intendencia/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
intendencia/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
intendencia/__pycache__/__init__.cpython-314.pyc
Normal file
BIN
intendencia/__pycache__/__init__.cpython-314.pyc
Normal file
Binary file not shown.
BIN
intendencia/__pycache__/admin.cpython-313.pyc
Normal file
BIN
intendencia/__pycache__/admin.cpython-313.pyc
Normal file
Binary file not shown.
BIN
intendencia/__pycache__/admin.cpython-314.pyc
Normal file
BIN
intendencia/__pycache__/admin.cpython-314.pyc
Normal file
Binary file not shown.
BIN
intendencia/__pycache__/apps.cpython-313.pyc
Normal file
BIN
intendencia/__pycache__/apps.cpython-313.pyc
Normal file
Binary file not shown.
BIN
intendencia/__pycache__/apps.cpython-314.pyc
Normal file
BIN
intendencia/__pycache__/apps.cpython-314.pyc
Normal file
Binary file not shown.
BIN
intendencia/__pycache__/context_processors.cpython-313.pyc
Normal file
BIN
intendencia/__pycache__/context_processors.cpython-313.pyc
Normal file
Binary file not shown.
BIN
intendencia/__pycache__/context_processors.cpython-314.pyc
Normal file
BIN
intendencia/__pycache__/context_processors.cpython-314.pyc
Normal file
Binary file not shown.
BIN
intendencia/__pycache__/forms.cpython-313.pyc
Normal file
BIN
intendencia/__pycache__/forms.cpython-313.pyc
Normal file
Binary file not shown.
BIN
intendencia/__pycache__/forms.cpython-314.pyc
Normal file
BIN
intendencia/__pycache__/forms.cpython-314.pyc
Normal file
Binary file not shown.
BIN
intendencia/__pycache__/middleware.cpython-313.pyc
Normal file
BIN
intendencia/__pycache__/middleware.cpython-313.pyc
Normal file
Binary file not shown.
BIN
intendencia/__pycache__/middleware.cpython-314.pyc
Normal file
BIN
intendencia/__pycache__/middleware.cpython-314.pyc
Normal file
Binary file not shown.
BIN
intendencia/__pycache__/models.cpython-313.pyc
Normal file
BIN
intendencia/__pycache__/models.cpython-313.pyc
Normal file
Binary file not shown.
BIN
intendencia/__pycache__/models.cpython-314.pyc
Normal file
BIN
intendencia/__pycache__/models.cpython-314.pyc
Normal file
Binary file not shown.
BIN
intendencia/__pycache__/urls.cpython-313.pyc
Normal file
BIN
intendencia/__pycache__/urls.cpython-313.pyc
Normal file
Binary file not shown.
BIN
intendencia/__pycache__/urls.cpython-314.pyc
Normal file
BIN
intendencia/__pycache__/urls.cpython-314.pyc
Normal file
Binary file not shown.
BIN
intendencia/__pycache__/views.cpython-313.pyc
Normal file
BIN
intendencia/__pycache__/views.cpython-313.pyc
Normal file
Binary file not shown.
BIN
intendencia/__pycache__/views.cpython-314.pyc
Normal file
BIN
intendencia/__pycache__/views.cpython-314.pyc
Normal file
Binary file not shown.
3
intendencia/admin.py
Normal file
3
intendencia/admin.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
6
intendencia/apps.py
Normal file
6
intendencia/apps.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class IntendenciaConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'intendencia'
|
||||||
15
intendencia/context_processors.py
Normal file
15
intendencia/context_processors.py
Normal 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
143
intendencia/forms.py
Normal 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
33
intendencia/middleware.py
Normal 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)
|
||||||
143
intendencia/migrations/0001_initial.py
Normal file
143
intendencia/migrations/0001_initial.py
Normal 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')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
0
intendencia/migrations/__init__.py
Normal file
0
intendencia/migrations/__init__.py
Normal file
BIN
intendencia/migrations/__pycache__/0001_initial.cpython-313.pyc
Normal file
BIN
intendencia/migrations/__pycache__/0001_initial.cpython-313.pyc
Normal file
Binary file not shown.
BIN
intendencia/migrations/__pycache__/0001_initial.cpython-314.pyc
Normal file
BIN
intendencia/migrations/__pycache__/0001_initial.cpython-314.pyc
Normal file
Binary file not shown.
BIN
intendencia/migrations/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
intendencia/migrations/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
intendencia/migrations/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
intendencia/migrations/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
intendencia/migrations/__pycache__/__init__.cpython-314.pyc
Normal file
BIN
intendencia/migrations/__pycache__/__init__.cpython-314.pyc
Normal file
Binary file not shown.
137
intendencia/models.py
Normal file
137
intendencia/models.py
Normal 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}"
|
||||||
155
intendencia/templates/asignacion_administrativa/index.html
Normal file
155
intendencia/templates/asignacion_administrativa/index.html
Normal 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>N°</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 %}
|
||||||
157
intendencia/templates/asignacion_tactica/index.html
Normal file
157
intendencia/templates/asignacion_tactica/index.html
Normal 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>N°</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 %}
|
||||||
312
intendencia/templates/base.html
Normal file
312
intendencia/templates/base.html
Normal 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>
|
||||||
80
intendencia/templates/directores/crear.html
Normal file
80
intendencia/templates/directores/crear.html
Normal 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 %}
|
||||||
|
|
||||||
107
intendencia/templates/directores/directores.html
Normal file
107
intendencia/templates/directores/directores.html
Normal 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">N°</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 %}
|
||||||
72
intendencia/templates/directores/editar.html
Normal file
72
intendencia/templates/directores/editar.html
Normal 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 %}
|
||||||
|
|
||||||
27
intendencia/templates/directores/eliminar.html
Normal file
27
intendencia/templates/directores/eliminar.html
Normal 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>
|
||||||
1
intendencia/templates/errores/error_404.html
Normal file
1
intendencia/templates/errores/error_404.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
404
|
||||||
1
intendencia/templates/errores/error_500.html
Normal file
1
intendencia/templates/errores/error_500.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
500
|
||||||
94
intendencia/templates/expired.html
Normal file
94
intendencia/templates/expired.html
Normal 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>
|
||||||
104
intendencia/templates/inventario_administrativo/crear.html
Normal file
104
intendencia/templates/inventario_administrativo/crear.html
Normal 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 %}
|
||||||
62
intendencia/templates/inventario_administrativo/editar.html
Normal file
62
intendencia/templates/inventario_administrativo/editar.html
Normal 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 %}
|
||||||
144
intendencia/templates/inventario_administrativo/listar.html
Normal file
144
intendencia/templates/inventario_administrativo/listar.html
Normal 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">N°</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 %}
|
||||||
107
intendencia/templates/inventario_tactico/crear.html
Normal file
107
intendencia/templates/inventario_tactico/crear.html
Normal 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 %}
|
||||||
80
intendencia/templates/inventario_tactico/editar.html
Normal file
80
intendencia/templates/inventario_tactico/editar.html
Normal 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 %}
|
||||||
139
intendencia/templates/inventario_tactico/listar.html
Normal file
139
intendencia/templates/inventario_tactico/listar.html
Normal 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">N°</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 %}
|
||||||
104
intendencia/templates/jefes/crear.html
Normal file
104
intendencia/templates/jefes/crear.html
Normal 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 %}
|
||||||
|
|
||||||
91
intendencia/templates/jefes/editar.html
Normal file
91
intendencia/templates/jefes/editar.html
Normal 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 %}
|
||||||
|
|
||||||
36
intendencia/templates/jefes/eliminar.html
Normal file
36
intendencia/templates/jefes/eliminar.html
Normal 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>
|
||||||
106
intendencia/templates/jefes/jefes.html
Normal file
106
intendencia/templates/jefes/jefes.html
Normal 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">N°</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 %}
|
||||||
165
intendencia/templates/partes/horizontal.html
Normal file
165
intendencia/templates/partes/horizontal.html
Normal 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>
|
||||||
23
intendencia/templates/partes/pie.html
Normal file
23
intendencia/templates/partes/pie.html
Normal 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>
|
||||||
93
intendencia/templates/partes/vertical.html
Normal file
93
intendencia/templates/partes/vertical.html
Normal 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 -->
|
||||||
159
intendencia/templates/pdf/administrativo.html
Normal file
159
intendencia/templates/pdf/administrativo.html
Normal 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>N°</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>
|
||||||
215
intendencia/templates/pdf/pdf_asignacion_tactica.html
Normal file
215
intendencia/templates/pdf/pdf_asignacion_tactica.html
Normal 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>N°</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>
|
||||||
206
intendencia/templates/pdf/pdf_asinacion.html
Normal file
206
intendencia/templates/pdf/pdf_asinacion.html
Normal 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>N°</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>
|
||||||
162
intendencia/templates/principal/principal.html
Normal file
162
intendencia/templates/principal/principal.html
Normal 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 %}
|
||||||
|
|
||||||
165
intendencia/templates/registration/login.html
Normal file
165
intendencia/templates/registration/login.html
Normal 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>
|
||||||
83
intendencia/templates/unidad/crear.html
Normal file
83
intendencia/templates/unidad/crear.html
Normal 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 %}
|
||||||
201
intendencia/templates/unidad/detalle.html
Normal file
201
intendencia/templates/unidad/detalle.html
Normal 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">N°</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">N°</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 %}
|
||||||
80
intendencia/templates/unidad/editar.html
Normal file
80
intendencia/templates/unidad/editar.html
Normal 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 %}
|
||||||
60
intendencia/templates/unidad/eliminar.html
Normal file
60
intendencia/templates/unidad/eliminar.html
Normal 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 %}
|
||||||
139
intendencia/templates/unidad/listar.html
Normal file
139
intendencia/templates/unidad/listar.html
Normal 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">N°</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 %}
|
||||||
0
intendencia/templatetags/__init__.py
Normal file
0
intendencia/templatetags/__init__.py
Normal file
BIN
intendencia/templatetags/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
intendencia/templatetags/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
intendencia/templatetags/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
intendencia/templatetags/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
intendencia/templatetags/__pycache__/__init__.cpython-314.pyc
Normal file
BIN
intendencia/templatetags/__pycache__/__init__.cpython-314.pyc
Normal file
Binary file not shown.
BIN
intendencia/templatetags/__pycache__/filtrado.cpython-312.pyc
Normal file
BIN
intendencia/templatetags/__pycache__/filtrado.cpython-312.pyc
Normal file
Binary file not shown.
BIN
intendencia/templatetags/__pycache__/filtrado.cpython-313.pyc
Normal file
BIN
intendencia/templatetags/__pycache__/filtrado.cpython-313.pyc
Normal file
Binary file not shown.
BIN
intendencia/templatetags/__pycache__/filtrado.cpython-314.pyc
Normal file
BIN
intendencia/templatetags/__pycache__/filtrado.cpython-314.pyc
Normal file
Binary file not shown.
32
intendencia/templatetags/filtrado.py
Normal file
32
intendencia/templatetags/filtrado.py
Normal 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
3
intendencia/tests.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
71
intendencia/urls.py
Normal file
71
intendencia/urls.py
Normal 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
613
intendencia/views.py
Normal 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
51
launcher.py
Normal 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
22
manage.py
Normal 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
5
runtime-hook.py
Normal 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
0
sistema/__init__.py
Normal file
BIN
sistema/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
sistema/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
sistema/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
sistema/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
sistema/__pycache__/__init__.cpython-314.pyc
Normal file
BIN
sistema/__pycache__/__init__.cpython-314.pyc
Normal file
Binary file not shown.
BIN
sistema/__pycache__/settings.cpython-312.pyc
Normal file
BIN
sistema/__pycache__/settings.cpython-312.pyc
Normal file
Binary file not shown.
BIN
sistema/__pycache__/settings.cpython-313.pyc
Normal file
BIN
sistema/__pycache__/settings.cpython-313.pyc
Normal file
Binary file not shown.
BIN
sistema/__pycache__/settings.cpython-314.pyc
Normal file
BIN
sistema/__pycache__/settings.cpython-314.pyc
Normal file
Binary file not shown.
BIN
sistema/__pycache__/urls.cpython-312.pyc
Normal file
BIN
sistema/__pycache__/urls.cpython-312.pyc
Normal file
Binary file not shown.
BIN
sistema/__pycache__/urls.cpython-313.pyc
Normal file
BIN
sistema/__pycache__/urls.cpython-313.pyc
Normal file
Binary file not shown.
BIN
sistema/__pycache__/urls.cpython-314.pyc
Normal file
BIN
sistema/__pycache__/urls.cpython-314.pyc
Normal file
Binary file not shown.
BIN
sistema/__pycache__/wsgi.cpython-312.pyc
Normal file
BIN
sistema/__pycache__/wsgi.cpython-312.pyc
Normal file
Binary file not shown.
BIN
sistema/__pycache__/wsgi.cpython-313.pyc
Normal file
BIN
sistema/__pycache__/wsgi.cpython-313.pyc
Normal file
Binary file not shown.
BIN
sistema/__pycache__/wsgi.cpython-314.pyc
Normal file
BIN
sistema/__pycache__/wsgi.cpython-314.pyc
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user