feat: roadmap y diseño inicial — iteración 1 antes de feedback del usuario
Inventario de dispositivos electrónicos no prestables para administración pública. Esta primera iteración contiene el diseño y la planificación antes de contactar con el usuario para su validación. Contenido: - especificaciones.md: requisitos detallados del programa - docs/arquitectura.md: propuesta de arquitectura con alternativas - docs/roadmap-iteracion-1.md: plan de desarrollo iteración 1 - docs/arquitectura-diagrama.html: diagrama visual de arquitectura - sketches/: mockups de las 4 pantallas principales - 001-dashboard: vista resumen - 002-inventario: tabla de dispositivos - 003-formulario: alta/edición de dispositivos - 004-configuracion: ajustes generales Stack propuesto: Python + Flask + SQLite + Alpine.js Distribución: PyInstaller → un único .exe autocontenido Entorno: Windows 11 Pro unida a dominio, sin privilegios de admin
This commit is contained in:
commit
ee849eb9a8
14 changed files with 3761 additions and 0 deletions
342
docs/arquitectura-diagrama.html
Normal file
342
docs/arquitectura-diagrama.html
Normal file
|
|
@ -0,0 +1,342 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Inventschario — Diagrama de Arquitectura</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
background: #020617;
|
||||
min-height: 100vh;
|
||||
padding: 2rem;
|
||||
color: white;
|
||||
}
|
||||
.container { max-width: 1200px; margin: 0 auto; }
|
||||
.header { margin-bottom: 2rem; }
|
||||
.header-row { display: flex; align-items: center; gap: 1rem; margin-bottom: 0.5rem; }
|
||||
.pulse-dot {
|
||||
width: 12px; height: 12px; background: #22d3ee;
|
||||
border-radius: 50%; animation: pulse 2s infinite;
|
||||
}
|
||||
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
|
||||
h1 { font-size: 1.5rem; font-weight: 700; letter-spacing: -0.025em; }
|
||||
.subtitle { color: #94a3b8; font-size: 0.875rem; margin-left: 1.75rem; }
|
||||
.diagram-container {
|
||||
background: rgba(15, 23, 42, 0.5);
|
||||
border-radius: 1rem;
|
||||
border: 1px solid #1e293b;
|
||||
padding: 1.5rem;
|
||||
overflow-x: auto;
|
||||
}
|
||||
svg { width: 100%; min-width: 900px; display: block; }
|
||||
.cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
gap: 1rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
.card {
|
||||
background: rgba(15, 23, 42, 0.5);
|
||||
border-radius: 0.75rem;
|
||||
border: 1px solid #1e293b;
|
||||
padding: 1.25rem;
|
||||
}
|
||||
.card-header { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.75rem; }
|
||||
.card-dot { width: 8px; height: 8px; border-radius: 50%; }
|
||||
.card-dot.cyan { background: #22d3ee; }
|
||||
.card-dot.emerald { background: #34d399; }
|
||||
.card-dot.violet { background: #a78bfa; }
|
||||
.card-dot.amber { background: #fbbf24; }
|
||||
.card-dot.rose { background: #fb7185; }
|
||||
.card h3 { font-size: 0.875rem; font-weight: 600; }
|
||||
.card ul { list-style: none; color: #94a3b8; font-size: 0.75rem; }
|
||||
.card li { margin-bottom: 0.375rem; }
|
||||
.footer { text-align: center; margin-top: 1.5rem; color: #475569; font-size: 0.75rem; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<div class="header-row">
|
||||
<div class="pulse-dot"></div>
|
||||
<h1>Inventschario — Arquitectura del Sistema</h1>
|
||||
</div>
|
||||
<p class="subtitle">Aplicación de inventario de dispositivos electrónicos • Python + Flask + SQLite • Local-first</p>
|
||||
</div>
|
||||
|
||||
<div class="diagram-container">
|
||||
<svg viewBox="0 0 1000 700">
|
||||
<defs>
|
||||
<marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#64748b" />
|
||||
</marker>
|
||||
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
|
||||
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="#1e293b" stroke-width="0.5"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
|
||||
<!-- Background Grid -->
|
||||
<rect width="100%" height="100%" fill="url(#grid)" />
|
||||
|
||||
<!-- ===== MACHINE BOUNDARY ===== -->
|
||||
<rect x="20" y="20" width="960" height="660" rx="12" fill="rgba(251, 191, 36, 0.03)" stroke="#fbbf24" stroke-width="1" stroke-dasharray="8,4"/>
|
||||
<text x="32" y="38" fill="#fbbf24" font-size="10" font-weight="600">Windows 11 Pro — Máquina Local (127.0.0.1)</text>
|
||||
|
||||
<!-- ===== BROWSER (FRONTEND) ===== -->
|
||||
<rect x="40" y="55" width="920" height="180" rx="8" fill="rgba(8, 51, 68, 0.15)" stroke="#22d3ee" stroke-width="1" stroke-dasharray="4,4"/>
|
||||
<text x="52" y="73" fill="#22d3ee" font-size="10" font-weight="600">NAVEGADOR (Edge)</text>
|
||||
|
||||
<!-- SPA Shell -->
|
||||
<rect x="60" y="85" width="140" height="55" rx="6" fill="rgba(8, 51, 68, 0.4)" stroke="#22d3ee" stroke-width="1.5"/>
|
||||
<text x="130" y="108" fill="white" font-size="11" font-weight="600" text-anchor="middle">SPA Shell</text>
|
||||
<text x="130" y="124" fill="#94a3b8" font-size="8" text-anchor="middle">index.html + Router</text>
|
||||
|
||||
<!-- Components -->
|
||||
<rect x="220" y="85" width="140" height="55" rx="6" fill="rgba(8, 51, 68, 0.4)" stroke="#22d3ee" stroke-width="1.5"/>
|
||||
<text x="290" y="108" fill="white" font-size="11" font-weight="600" text-anchor="middle">Componentes</text>
|
||||
<text x="290" y="124" fill="#94a3b8" font-size="8" text-anchor="middle">Table | Form | Modal</text>
|
||||
|
||||
<!-- API Client -->
|
||||
<rect x="380" y="85" width="140" height="55" rx="6" fill="rgba(8, 51, 68, 0.4)" stroke="#22d3ee" stroke-width="1.5"/>
|
||||
<text x="450" y="108" fill="white" font-size="11" font-weight="600" text-anchor="middle">API Client</text>
|
||||
<text x="450" y="124" fill="#94a3b8" font-size="8" text-anchor="middle">fetch wrapper + errors</text>
|
||||
|
||||
<!-- Pages -->
|
||||
<rect x="540" y="85" width="140" height="55" rx="6" fill="rgba(8, 51, 68, 0.4)" stroke="#22d3ee" stroke-width="1.5"/>
|
||||
<text x="610" y="108" fill="white" font-size="11" font-weight="600" text-anchor="middle">Páginas</text>
|
||||
<text x="610" y="124" fill="#94a3b8" font-size="8" text-anchor="middle">Dashboard | Inventory</text>
|
||||
|
||||
<!-- Setup Wizard -->
|
||||
<rect x="700" y="85" width="140" height="55" rx="6" fill="rgba(8, 51, 68, 0.4)" stroke="#22d3ee" stroke-width="1.5"/>
|
||||
<text x="770" y="108" fill="white" font-size="11" font-weight="600" text-anchor="middle">Setup Wizard</text>
|
||||
<text x="770" y="124" fill="#94a3b8" font-size="8" text-anchor="middle">6 pasos de config</text>
|
||||
|
||||
<!-- Frontend labels -->
|
||||
<text x="60" y="170" fill="#22d3ee" font-size="9">HTML + CSS + Alpine.js</text>
|
||||
<text x="60" y="185" fill="#94a3b8" font-size="8">Sin build step • System fonts • WCAG 2.1 AA</text>
|
||||
<text x="60" y="200" fill="#94a3b8" font-size="8">Router hash-based • Componentes vanilla JS</text>
|
||||
|
||||
<!-- ===== HTTP ARROW ===== -->
|
||||
<line x1="450" y1="145" x2="450" y2="260" stroke="#22d3ee" stroke-width="2" marker-end="url(#arrowhead)"/>
|
||||
<rect x="395" y="190" width="110" height="22" rx="4" fill="#020617" stroke="#1e293b" stroke-width="1"/>
|
||||
<text x="450" y="205" fill="#22d3ee" font-size="9" text-anchor="middle" font-weight="600">HTTP localhost</text>
|
||||
|
||||
<!-- ===== API GATEWAY ===== -->
|
||||
<rect x="40" y="255" width="920" height="130" rx="8" fill="rgba(6, 78, 59, 0.15)" stroke="#34d399" stroke-width="1" stroke-dasharray="4,4"/>
|
||||
<text x="52" y="273" fill="#34d399" font-size="10" font-weight="600">API GATEWAY — Flask REST/JSON</text>
|
||||
|
||||
<!-- Device Routes -->
|
||||
<rect x="60" y="285" width="150" height="45" rx="6" fill="rgba(6, 78, 59, 0.4)" stroke="#34d399" stroke-width="1.5"/>
|
||||
<text x="135" y="305" fill="white" font-size="10" font-weight="600" text-anchor="middle">/api/v1/devices</text>
|
||||
<text x="135" y="319" fill="#94a3b8" font-size="8" text-anchor="middle">CRUD + History</text>
|
||||
|
||||
<!-- Import/Export Routes -->
|
||||
<rect x="230" y="285" width="150" height="45" rx="6" fill="rgba(6, 78, 59, 0.4)" stroke="#34d399" stroke-width="1.5"/>
|
||||
<text x="305" y="305" fill="white" font-size="10" font-weight="600" text-anchor="middle">/api/v1/import|export</text>
|
||||
<text x="305" y="319" fill="#94a3b8" font-size="8" text-anchor="middle">CSV operations</text>
|
||||
|
||||
<!-- Backup Routes -->
|
||||
<rect x="400" y="285" width="150" height="45" rx="6" fill="rgba(6, 78, 59, 0.4)" stroke="#34d399" stroke-width="1.5"/>
|
||||
<text x="475" y="305" fill="white" font-size="10" font-weight="600" text-anchor="middle">/api/v1/backups</text>
|
||||
<text x="475" y="319" fill="#94a3b8" font-size="8" text-anchor="middle">Create | Restore | List</text>
|
||||
|
||||
<!-- Config Routes -->
|
||||
<rect x="570" y="285" width="150" height="45" rx="6" fill="rgba(6, 78, 59, 0.4)" stroke="#34d399" stroke-width="1.5"/>
|
||||
<text x="645" y="305" fill="white" font-size="10" font-weight="600" text-anchor="middle">/api/v1/config</text>
|
||||
<text x="645" y="319" fill="#94a3b8" font-size="8" text-anchor="middle">Settings + Labels</text>
|
||||
|
||||
<!-- Middleware -->
|
||||
<rect x="740" y="285" width="200" height="45" rx="6" fill="rgba(6, 78, 59, 0.4)" stroke="#34d399" stroke-width="1.5"/>
|
||||
<text x="840" y="305" fill="white" font-size="10" font-weight="600" text-anchor="middle">Middleware</text>
|
||||
<text x="840" y="319" fill="#94a3b8" font-size="8" text-anchor="middle">CORS • JSON parse • Errors</text>
|
||||
|
||||
<!-- Gateway details -->
|
||||
<text x="60" y="355" fill="#34d399" font-size="9">Blueprint pattern • Solo escucha 127.0.0.1 • Sin exposición a red</text>
|
||||
|
||||
<!-- ===== SERVICE LAYER ===== -->
|
||||
<rect x="40" y="400" width="920" height="100" rx="8" fill="rgba(251, 146, 60, 0.1)" stroke="#fb923c" stroke-width="1" stroke-dasharray="4,4"/>
|
||||
<text x="52" y="418" fill="#fb923c" font-size="10" font-weight="600">CAPA DE SERVICIOS — Lógica de Negocio</text>
|
||||
|
||||
<rect x="60" y="430" width="140" height="45" rx="6" fill="rgba(251, 146, 60, 0.3)" stroke="#fb923c" stroke-width="1.5"/>
|
||||
<text x="130" y="450" fill="white" font-size="10" font-weight="600" text-anchor="middle">DeviceService</text>
|
||||
<text x="130" y="464" fill="#94a3b8" font-size="8" text-anchor="middle">CRUD + Lifecycle</text>
|
||||
|
||||
<rect x="220" y="430" width="140" height="45" rx="6" fill="rgba(251, 146, 60, 0.3)" stroke="#fb923c" stroke-width="1.5"/>
|
||||
<text x="290" y="450" fill="white" font-size="10" font-weight="600" text-anchor="middle">BackupService</text>
|
||||
<text x="290" y="464" fill="#94a3b8" font-size="8" text-anchor="middle">Schedule + Gzip</text>
|
||||
|
||||
<rect x="380" y="430" width="140" height="45" rx="6" fill="rgba(251, 146, 60, 0.3)" stroke="#fb923c" stroke-width="1.5"/>
|
||||
<text x="450" y="450" fill="white" font-size="10" font-weight="600" text-anchor="middle">ImportService</text>
|
||||
<text x="450" y="464" fill="#94a3b8" font-size="8" text-anchor="middle">CSV + Mapper</text>
|
||||
|
||||
<rect x="540" y="430" width="140" height="45" rx="6" fill="rgba(251, 146, 60, 0.3)" stroke="#fb923c" stroke-width="1.5"/>
|
||||
<text x="610" y="450" fill="white" font-size="10" font-weight="600" text-anchor="middle">ExportService</text>
|
||||
<text x="610" y="464" fill="#94a3b8" font-size="8" text-anchor="middle">CSV + Labels</text>
|
||||
|
||||
<rect x="700" y="430" width="140" height="45" rx="6" fill="rgba(251, 146, 60, 0.3)" stroke="#fb923c" stroke-width="1.5"/>
|
||||
<text x="770" y="450" fill="white" font-size="10" font-weight="600" text-anchor="middle">ConfigService</text>
|
||||
<text x="770" y="464" fill="#94a3b8" font-size="8" text-anchor="middle">KV Store + Labels</text>
|
||||
|
||||
<!-- Arrows from Gateway to Services -->
|
||||
<line x1="130" y1="335" x2="130" y2="428" stroke="#fb923c" stroke-width="1.5" marker-end="url(#arrowhead)"/>
|
||||
<line x1="305" y1="335" x2="290" y2="428" stroke="#fb923c" stroke-width="1.5" marker-end="url(#arrowhead)"/>
|
||||
<line x1="475" y1="335" x2="450" y2="428" stroke="#fb923c" stroke-width="1.5" marker-end="url(#arrowhead)"/>
|
||||
<line x1="645" y1="335" x2="610" y2="428" stroke="#fb923c" stroke-width="1.5" marker-end="url(#arrowhead)"/>
|
||||
<line x1="840" y1="335" x2="770" y2="428" stroke="#fb923c" stroke-width="1.5" marker-end="url(#arrowhead)"/>
|
||||
|
||||
<!-- ===== DATA LAYER ===== -->
|
||||
<rect x="40" y="515" width="920" height="140" rx="8" fill="rgba(76, 29, 149, 0.15)" stroke="#a78bfa" stroke-width="1" stroke-dasharray="4,4"/>
|
||||
<text x="52" y="533" fill="#a78bfa" font-size="10" font-weight="600">CAPA DE DATOS — SQLite (WAL mode)</text>
|
||||
|
||||
<rect x="60" y="545" width="130" height="45" rx="6" fill="rgba(76, 29, 149, 0.4)" stroke="#a78bfa" stroke-width="1.5"/>
|
||||
<text x="125" y="565" fill="white" font-size="10" font-weight="600" text-anchor="middle">DeviceRepo</text>
|
||||
<text x="125" y="579" fill="#94a3b8" font-size="8" text-anchor="middle">devices table</text>
|
||||
|
||||
<rect x="210" y="545" width="130" height="45" rx="6" fill="rgba(76, 29, 149, 0.4)" stroke="#a78bfa" stroke-width="1.5"/>
|
||||
<text x="275" y="565" fill="white" font-size="10" font-weight="600" text-anchor="middle">HistoryRepo</text>
|
||||
<text x="275" y="579" fill="#94a3b8" font-size="8" text-anchor="middle">device_history</text>
|
||||
|
||||
<rect x="360" y="545" width="130" height="45" rx="6" fill="rgba(76, 29, 149, 0.4)" stroke="#a78bfa" stroke-width="1.5"/>
|
||||
<text x="425" y="565" fill="white" font-size="10" font-weight="600" text-anchor="middle">ConfigRepo</text>
|
||||
<text x="425" y="579" fill="#94a3b8" font-size="8" text-anchor="middle">app_config + labels</text>
|
||||
|
||||
<rect x="510" y="545" width="130" height="45" rx="6" fill="rgba(76, 29, 149, 0.4)" stroke="#a78bfa" stroke-width="1.5"/>
|
||||
<text x="575" y="565" fill="white" font-size="10" font-weight="600" text-anchor="middle">BackupRepo</text>
|
||||
<text x="575" y="579" fill="#94a3b8" font-size="8" text-anchor="middle">backup_log</text>
|
||||
|
||||
<!-- SQLite Database -->
|
||||
<rect x="680" y="545" width="260" height="90" rx="6" fill="rgba(76, 29, 149, 0.4)" stroke="#a78bfa" stroke-width="1.5"/>
|
||||
<text x="810" y="565" fill="white" font-size="11" font-weight="600" text-anchor="middle">SQLite</text>
|
||||
<text x="810" y="582" fill="#94a3b8" font-size="8" text-anchor="middle">inventschario.db</text>
|
||||
<text x="810" y="598" fill="#a78bfa" font-size="8" text-anchor="middle">WAL mode • Foreign Keys</text>
|
||||
<text x="810" y="614" fill="#94a3b8" font-size="8" text-anchor="middle">6 tablas • 4 índices únicos</text>
|
||||
<text x="810" y="628" fill="#94a3b8" font-size="8" text-anchor="middle">%APPDATA%/inventschario/</text>
|
||||
|
||||
<!-- Arrows from Services to Repos -->
|
||||
<line x1="130" y1="478" x2="125" y2="543" stroke="#a78bfa" stroke-width="1.5" marker-end="url(#arrowhead)"/>
|
||||
<line x1="290" y1="478" x2="275" y2="543" stroke="#a78bfa" stroke-width="1.5" marker-end="url(#arrowhead)"/>
|
||||
<line x1="450" y1="478" x2="425" y2="543" stroke="#a78bfa" stroke-width="1.5" marker-end="url(#arrowhead)"/>
|
||||
<line x1="610" y1="478" x2="575" y2="543" stroke="#a78bfa" stroke-width="1.5" marker-end="url(#arrowhead)"/>
|
||||
|
||||
<!-- Arrows from Repos to SQLite -->
|
||||
<line x1="190" y1="567" x2="678" y2="580" stroke="#a78bfa" stroke-width="1" stroke-dasharray="3,3"/>
|
||||
<line x1="340" y1="567" x2="678" y2="585" stroke="#a78bfa" stroke-width="1" stroke-dasharray="3,3"/>
|
||||
<line x1="490" y1="567" x2="678" y2="590" stroke="#a78bfa" stroke-width="1" stroke-dasharray="3,3"/>
|
||||
<line x1="640" y1="567" x2="678" y2="595" stroke="#a78bfa" stroke-width="1" stroke-dasharray="3,3"/>
|
||||
|
||||
<!-- ===== LEGEND ===== -->
|
||||
<text x="60" y="680" fill="white" font-size="10" font-weight="600">Leyenda:</text>
|
||||
|
||||
<rect x="130" y="672" width="14" height="8" rx="2" fill="rgba(8, 51, 68, 0.4)" stroke="#22d3ee" stroke-width="1"/>
|
||||
<text x="150" y="680" fill="#94a3b8" font-size="8">Frontend</text>
|
||||
|
||||
<rect x="210" y="672" width="14" height="8" rx="2" fill="rgba(6, 78, 59, 0.4)" stroke="#34d399" stroke-width="1"/>
|
||||
<text x="230" y="680" fill="#94a3b8" font-size="8">API Gateway</text>
|
||||
|
||||
<rect x="310" y="672" width="14" height="8" rx="2" fill="rgba(251, 146, 60, 0.3)" stroke="#fb923c" stroke-width="1"/>
|
||||
<text x="330" y="680" fill="#94a3b8" font-size="8">Servicios</text>
|
||||
|
||||
<rect x="400" y="672" width="14" height="8" rx="2" fill="rgba(76, 29, 149, 0.4)" stroke="#a78bfa" stroke-width="1"/>
|
||||
<text x="420" y="680" fill="#94a3b8" font-size="8">Datos</text>
|
||||
|
||||
<rect x="480" y="672" width="14" height="8" rx="2" fill="rgba(251, 191, 36, 0.05)" stroke="#fbbf24" stroke-width="1" stroke-dasharray="4,4"/>
|
||||
<text x="500" y="680" fill="#94a3b8" font-size="8">Límite de máquina</text>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<!-- Info Cards -->
|
||||
<div class="cards">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-dot cyan"></div>
|
||||
<h3>Frontend</h3>
|
||||
</div>
|
||||
<ul>
|
||||
<li>• HTML + CSS + Alpine.js (sin framework pesado)</li>
|
||||
<li>• Router hash-based para SPA</li>
|
||||
<li>• Componentes vanilla JS reutilizables</li>
|
||||
<li>• WCAG 2.1 AA — contraste, teclado, labels</li>
|
||||
<li>• CSS custom properties para theming</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-dot emerald"></div>
|
||||
<h3>Backend</h3>
|
||||
</div>
|
||||
<ul>
|
||||
<li>• Python 3.11+ con Flask</li>
|
||||
<li>• API REST JSON sobre localhost</li>
|
||||
<li>• Blueprint pattern por dominio</li>
|
||||
<li>• Service layer separada de HTTP</li>
|
||||
<li>• Schedule library para backups</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-dot violet"></div>
|
||||
<h3>Datos</h3>
|
||||
</div>
|
||||
<ul>
|
||||
<li>• SQLite con WAL mode</li>
|
||||
<li>• Repository pattern por tabla</li>
|
||||
<li>• Foreign keys + índices</li>
|
||||
<li>• Migraciones versionadas</li>
|
||||
<li>• Backups gzip en %APPDATA%</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-dot amber"></div>
|
||||
<h3>Empaquetado</h3>
|
||||
</div>
|
||||
<ul>
|
||||
<li>• PyInstaller → un único .exe (~30MB)</li>
|
||||
<li>• Frontend empaquetado en el bundle</li>
|
||||
<li>• Instalación en perfil de usuario</li>
|
||||
<li>• Acceso directo en Menú Inicio</li>
|
||||
<li>• Sin dependencias de runtime</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-dot rose"></div>
|
||||
<h3>Seguridad</h3>
|
||||
</div>
|
||||
<ul>
|
||||
<li>• Servidor solo en 127.0.0.1</li>
|
||||
<li>• Sin exposición a la red</li>
|
||||
<li>• Validación en backend (no confiar en frontend)</li>
|
||||
<li>• Datos en %APPDATA% (aceso restringido)</li>
|
||||
<li>• Foreign keys para integridad</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-dot cyan"></div>
|
||||
<h3>Modularidad</h3>
|
||||
</div>
|
||||
<ul>
|
||||
<li>• API REST como contrato frontend↔backend</li>
|
||||
<li>• Frontend reemplazable sin tocar backend</li>
|
||||
<li>• Plugin architecture para import/export</li>
|
||||
<li>• Migración a .NET 8 o Go documentada</li>
|
||||
<li>• Cada service es independiente</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="footer">
|
||||
Inventschario v1.0.0 — Iteración 1 — 2026-08-03 — Python + Flask + SQLite
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
411
docs/arquitectura.md
Normal file
411
docs/arquitectura.md
Normal file
|
|
@ -0,0 +1,411 @@
|
|||
# Inventschario — Documento de Arquitectura
|
||||
|
||||
**Versión:** 1.0.0-iteracion1
|
||||
**Fecha:** 2026-08-03
|
||||
**Estado:** Propuesta para revisión
|
||||
|
||||
---
|
||||
|
||||
## 1. Visión General
|
||||
|
||||
Inventschario采用 arquitectura de tres capas con separación clara entre
|
||||
presentación, lógica de negocio y persistencia. La capa de presentación se
|
||||
ejecuta en el navegador del usuario; la capa de negocio y persistencia corren
|
||||
como un servidor local HTTP que solo escucha en `127.0.0.1`.
|
||||
|
||||
La pasarela entre frontend y backend es una API REST documentada, de forma que
|
||||
el frontend puede sustituirse completamente sin modificar ninguna línea del
|
||||
backend, y viceversa.
|
||||
|
||||
---
|
||||
|
||||
## 2. Diagrama de Componentes
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ NAVEGADOR (Edge) │
|
||||
│ │
|
||||
│ ┌───────────────────────────────────────────────────────────┐ │
|
||||
│ │ FRONTEND │ │
|
||||
│ │ │ │
|
||||
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
|
||||
│ │ │Dashboard │ │Inventario│ │ Formular │ │ Config │ │ │
|
||||
│ │ │ Page │ │ Page │ │ ios │ │ Page │ │ │
|
||||
│ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │
|
||||
│ │ │ │ │ │ │ │
|
||||
│ │ ┌────┴──────────────┴──────────────┴──────────────┴────┐ │
|
||||
│ │ │ Router (SPA) │ │
|
||||
│ │ └────────────────────┬─────────────────────────────────┘ │ │
|
||||
│ │ │ │ │
|
||||
│ │ ┌────────────────────┴─────────────────────────────────┐ │
|
||||
│ │ │ API Client (fetch wrapper) │ │
|
||||
│ │ └────────────────────┬─────────────────────────────────┘ │ │
|
||||
│ │ │ │ │
|
||||
│ │ ┌────────────────────┴─────────────────────────────────┐ │
|
||||
│ │ │ Component Library │ │
|
||||
│ │ │ Table | Form | Modal | Toast | Sidebar | Charts │ │
|
||||
│ │ └──────────────────────────────────────────────────────┘ │ │
|
||||
│ └───────────────────────────┬───────────────────────────────┘ │
|
||||
│ │ HTTP localhost:PORT │
|
||||
├──────────────────────────────┼──────────────────────────────────┤
|
||||
│ │ │
|
||||
│ ┌───────────────────────────┴───────────────────────────────┐ │
|
||||
│ │ API GATEWAY │ │
|
||||
│ │ │ │
|
||||
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
|
||||
│ │ │ /devices │ │ /import │ │ /backups │ │ /config │ │ │
|
||||
│ │ │ /history │ │ /export │ │ │ │ /labels │ │ │
|
||||
│ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │
|
||||
│ │ │ │ │ │ │ │
|
||||
│ │ ┌────┴──────────────┴──────────────┴──────────────┴────┐ │ │
|
||||
│ │ │ Middleware Layer │ │ │
|
||||
│ │ │ CORS (localhost) | JSON parsing | Error handling │ │ │
|
||||
│ │ └────────────────────┬─────────────────────────────────┘ │ │
|
||||
│ └───────────────────────┼───────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌───────────────────────┴───────────────────────────────────┐ │
|
||||
│ │ SERVICE LAYER │ │
|
||||
│ │ │ │
|
||||
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
|
||||
│ │ │DeviceService │ │BackupService │ │ImportService │ │ │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
│ │ │- CRUD │ │- crear │ │- parse CSV │ │ │
|
||||
│ │ │- buscar │ │- restaurar │ │- mapear │ │ │
|
||||
│ │ │- filtrar │ │- eliminar │ │- validar │ │ │
|
||||
│ │ │- baja │ │- verificar │ │- duplicados │ │ │
|
||||
│ │ │- enajenar │ │ antigüedad │ │ │ │ │
|
||||
│ │ │- historial │ │ │ │ │ │ │
|
||||
│ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ │
|
||||
│ │ │ │ │ │ │
|
||||
│ │ ┌──────┴──────────────────┴──────────────────┴───────┐ │ │
|
||||
│ │ │ ExportService | ConfigService │ │ │
|
||||
│ │ └──────────────────────┬─────────────────────────────┘ │ │
|
||||
│ └─────────────────────────┼─────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌─────────────────────────┴─────────────────────────────────┐ │
|
||||
│ │ DATA LAYER │ │
|
||||
│ │ │ │
|
||||
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
|
||||
│ │ │ DeviceRepo │ │ ConfigRepo │ │ BackupRepo │ │ │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
│ │ │ SQLite CRUD │ │ KV Store │ │ Log CRUD │ │ │
|
||||
│ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ │
|
||||
│ │ │ │ │ │ │
|
||||
│ │ ┌──────┴──────────────────┴──────────────────┴───────┐ │ │
|
||||
│ │ │ SQLite (WAL mode) │ │ │
|
||||
│ │ │ inventschario.db │ │ │
|
||||
│ │ └────────────────────────────────────────────────────┘ │ │
|
||||
│ └───────────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Alternativas de Arquitectura
|
||||
|
||||
### Alternativa A: Python + Flask (Recomendada)
|
||||
|
||||
```
|
||||
[Browser] ──HTTP──► [Flask server] ──► [SQLite]
|
||||
│ │
|
||||
│ HTML/CSS/JS │ Python services
|
||||
│ (Alpine.js) │ (DeviceService, etc.)
|
||||
│ │
|
||||
◄──── JSON ──────────►│
|
||||
```
|
||||
|
||||
**Por qué se recomienda:**
|
||||
- Python stdlib incluye sqlite3, http.server, json, gzip, csv, shutil
|
||||
- Flask es ligero (~500KB) y no necesita build step
|
||||
- PyInstaller empaqueta todo en un .exe autocontenido
|
||||
- El ecosistema Python tiene librerías para todo lo necesario
|
||||
- Desarrollo rápido, fácil de mantener
|
||||
|
||||
**Componentes:**
|
||||
- `flask` — Servidor web minimalista
|
||||
- `schedule` — Programador de tareas (backups diarios)
|
||||
- `pyinstaller` — Empaquetado en .exe
|
||||
|
||||
### Alternativa B: .NET 8 Minimal API
|
||||
|
||||
```
|
||||
[Browser] ──HTTP──► [Kestrel server] ──► [SQLite via EF Core]
|
||||
│ │
|
||||
│ HTML/CSS/JS │ C# services
|
||||
│ (embebido) │ (DeviceService, etc.)
|
||||
│ │
|
||||
◄──── JSON ──────────►│
|
||||
```
|
||||
|
||||
**Por qué es alternativa válida:**
|
||||
- `dotnet publish -r win-x64 --self-contained` produce .exe sin runtime
|
||||
- Rendimiento superior a Python en operaciones intensivas
|
||||
- Integración nativa con Windows
|
||||
- Entity Framework Core para ORM robusto
|
||||
|
||||
**Componentes:**
|
||||
- ASP.NET Core Minimal API
|
||||
- Microsoft.Data.Sqlite
|
||||
- Hangfire (tareas programadas)
|
||||
|
||||
### Alternativa C: Go + net/http
|
||||
|
||||
```
|
||||
[Browser] ──HTTP──► [Go HTTP server] ──► [SQLite via CGO]
|
||||
│ │
|
||||
│ HTML/CSS/JS │ Go handlers
|
||||
│ (embed.FS) │ (DeviceService, etc.)
|
||||
│ │
|
||||
◄──── JSON ──────────►│
|
||||
```
|
||||
|
||||
**Por qué es alternativa válida:**
|
||||
- Binario más pequeño (10-15 MB)
|
||||
- Rendimiento excepcional
|
||||
- Sin dependencias en runtime
|
||||
- `embed.FS` embebe archivos estáticos directamente en el binario
|
||||
|
||||
**Componentes:**
|
||||
- `net/http` (stdlib)
|
||||
- `mattn/go-sqlite3` (CGO)
|
||||
- `embed.FS` (archivos estáticos)
|
||||
|
||||
### Decisión: Python + Flask
|
||||
|
||||
Para la primera iteración, Python + Flask es la opción que mejor equilibra:
|
||||
- Velocidad de desarrollo
|
||||
- Facilidad de distribución (PyInstaller)
|
||||
- Modularidad del frontend (API REST pura)
|
||||
- Ecosistema de herramientas
|
||||
|
||||
Si en iteraciones futuras el tamaño del .exe o la detección por antivirus
|
||||
se convierten en problemas, se documentará la migración a .NET 8.
|
||||
|
||||
---
|
||||
|
||||
## 4. Patrones de Diseño
|
||||
|
||||
### 4.1 Repository Pattern (Capa de Datos)
|
||||
|
||||
Cada tabla tiene su repository que encapsula las consultas SQL:
|
||||
|
||||
```python
|
||||
class DeviceRepository:
|
||||
def __init__(self, db_path):
|
||||
self.db_path = db_path
|
||||
|
||||
def get_all(self, filters=None): ...
|
||||
def get_by_id(self, device_id): ...
|
||||
def create(self, device_data): ...
|
||||
def update(self, device_id, device_data): ...
|
||||
def delete(self, device_id): ...
|
||||
def search(self, query): ...
|
||||
```
|
||||
|
||||
### 4.2 Service Layer (Lógica de Negocio)
|
||||
|
||||
Los services orquestan la lógica sin conocer la API HTTP:
|
||||
|
||||
```python
|
||||
class DeviceService:
|
||||
def __init__(self, device_repo, history_repo):
|
||||
self.device_repo = device_repo
|
||||
self.history_repo = history_repo
|
||||
|
||||
def create_device(self, data, user):
|
||||
device = self.device_repo.create(data)
|
||||
self.history_repo.log(device.id, 'created', user=user)
|
||||
return device
|
||||
|
||||
def decommission(self, device_id, reason, user):
|
||||
device = self.device_repo.get_by_id(device_id)
|
||||
self.device_repo.update_status(device_id, 'decommissioned')
|
||||
self.history_repo.log(device_id, 'decommissioned',
|
||||
notes=reason, user=user)
|
||||
```
|
||||
|
||||
### 4.3 API Gateway (Capa HTTP)
|
||||
|
||||
Las rutas API son delgadas — solo parsean la request, llaman al service,
|
||||
y formatean la response:
|
||||
|
||||
```python
|
||||
@bp.route('/api/v1/devices', methods=['POST'])
|
||||
def create_device():
|
||||
data = request.get_json()
|
||||
device = device_service.create_device(data, current_user)
|
||||
return jsonify(device.to_dict()), 201
|
||||
```
|
||||
|
||||
### 4.4 Frontend Component Architecture
|
||||
|
||||
El frontend usa un patrón de componentes simples sin framework pesado:
|
||||
|
||||
```javascript
|
||||
// Cada componente es un objeto con render() y bind()
|
||||
const DeviceTable = {
|
||||
render(devices) { return `<table>...</table>`; },
|
||||
bind(container) { /* event listeners */ }
|
||||
};
|
||||
|
||||
// El router cambia de página
|
||||
const Router = {
|
||||
routes: { '/': Dashboard, '/inventory': Inventory },
|
||||
navigate(path) { /* render the page */ }
|
||||
};
|
||||
```
|
||||
|
||||
### 4.5 Import/Export Plugin Architecture
|
||||
|
||||
El sistema de importación exportación es un plugin architecture:
|
||||
|
||||
```python
|
||||
class ImporterBase:
|
||||
"""Clase abstracta para todos los importadores"""
|
||||
def parse(self, file_path) -> list[dict]: ...
|
||||
def validate(self, records) -> ValidationResult: ...
|
||||
def import_records(self, records, strategy) -> ImportResult: ...
|
||||
|
||||
class CSVImporter(ImporterBase):
|
||||
"""Implementación CSV"""
|
||||
def parse(self, file_path):
|
||||
# Lee CSV, mapea columnas, retorna lista de dicts
|
||||
...
|
||||
|
||||
class FieldMapper:
|
||||
"""Motor de mapeo de campos"""
|
||||
def __init__(self, mapping_config):
|
||||
self.mapping = mapping_config
|
||||
|
||||
def map_record(self, raw_record) -> dict:
|
||||
# Aplica el mapeo configurado
|
||||
...
|
||||
```
|
||||
|
||||
Para añadir un nuevo formato (ej: Excel), se crea una nueva clase que
|
||||
herede de `ImporterBase` sin tocar el resto del sistema.
|
||||
|
||||
---
|
||||
|
||||
## 5. Seguridad
|
||||
|
||||
### 5.1 Red
|
||||
|
||||
- El servidor Flask escucha SOLO en `127.0.0.1:PORT`
|
||||
- No hay exposición a la red local o internet
|
||||
- El puerto se asigna dinámicamente o se configura en el wizard
|
||||
|
||||
### 5.2 Datos
|
||||
|
||||
- SQLite WAL mode para integridad
|
||||
- Foreign keys habilitadas
|
||||
- Validación de datos en backend (no confiar en el frontend)
|
||||
- Backups almacenados en `%APPDATA%` (acceso restringido al usuario)
|
||||
|
||||
### 5.3 Archivos
|
||||
|
||||
- No se ejecutan comandos del sistema
|
||||
- No se escriben archivos fuera de `%APPDATA%/inventschario/`
|
||||
- La BD se almacena en `%APPDATA%/inventschario/inventschario.db`
|
||||
- Los logs se almacenan en `%APPDATA%/inventschario/logs/`
|
||||
|
||||
---
|
||||
|
||||
## 6. Rendimiento
|
||||
|
||||
### 6.1 Consideraciones
|
||||
|
||||
- Todo es local: latencia de red ~0ms
|
||||
- SQLite es suficiente para inventarios de hasta 100,000 dispositivos
|
||||
- WAL mode permite lecturas concurrentes
|
||||
- Índices en campos de búsqueda frecuentes
|
||||
|
||||
### 6.2 Métricas Objetivo
|
||||
|
||||
| Operación | Tiempo objetivo |
|
||||
|--------------------------------|-----------------|
|
||||
| Carga de página inicial | < 2s |
|
||||
| Búsqueda en inventario | < 500ms |
|
||||
| Crear dispositivo | < 100ms |
|
||||
| Exportar 1,000 dispositivos | < 3s |
|
||||
| Importar 1,000 dispositivos | < 5s |
|
||||
| Backup de BD de 10MB | < 2s |
|
||||
|
||||
---
|
||||
|
||||
## 7. Estrategia de Testing
|
||||
|
||||
### 7.1 Niveles
|
||||
|
||||
1. **Unit Tests** — Services, repositories, utilidades
|
||||
2. **Integration Tests** — API endpoints con BD en memoria
|
||||
3. **E2E Tests** — Flujo completo (futuro, iteración 2)
|
||||
|
||||
### 7.2 Herramientas
|
||||
|
||||
- `pytest` — Framework de testing
|
||||
- `pytest-flask` — Fixtures para Flask
|
||||
- `sqlite3` en memoria — BD de test aislada
|
||||
|
||||
### 7.3 Cobertura
|
||||
|
||||
Objetivo iteración 1: > 80% cobertura en services y repositories.
|
||||
|
||||
---
|
||||
|
||||
## 8. Despliegue
|
||||
|
||||
### 8.1 Distribución
|
||||
|
||||
```
|
||||
inventschario/
|
||||
├── inventschario.exe # Ejecutable empaquetado (PyInstaller)
|
||||
├── inventschario.ico # Icono de la aplicación
|
||||
└── README.md # Instrucciones de uso
|
||||
```
|
||||
|
||||
### 8.2 Instalación (Usuario)
|
||||
|
||||
1. El usuario ejecuta `inventschario.exe`
|
||||
2. PyInstaller extrae a un directorio temporal
|
||||
3. La aplicación se instala en `%LOCALAPPDATA%/inventschario/`
|
||||
4. Se crea un acceso directo en el Menú Inicio del usuario
|
||||
5. Se lanza el navegador con la aplicación
|
||||
|
||||
### 8.3 Start Menu
|
||||
|
||||
El acceso directo se crea en:
|
||||
```
|
||||
%APPDATA%/Microsoft/Windows/Start Menu/Programs/Inventschario/Inventschario.lnk
|
||||
```
|
||||
|
||||
Esto requiere privilegios de instalador (que el usuario tiene).
|
||||
|
||||
---
|
||||
|
||||
## 9. Plan de Migración (Si es necesario)
|
||||
|
||||
Si Python + Flask no cumple los requisitos en producción:
|
||||
|
||||
### De Python a .NET 8
|
||||
|
||||
1. Mantener la misma API REST (los endpoints no cambian)
|
||||
2. Reescribir los services en C#
|
||||
3. Usar Entity Framework Core con SQLite
|
||||
4. Publicar como self-contained: `dotnet publish -r win-x64 --self-contained`
|
||||
5. El frontend NO cambia (solo se sirve desde wwwroot)
|
||||
|
||||
### De Python a Go
|
||||
|
||||
1. Mantener la misma API REST
|
||||
2. Reescribir los services en Go
|
||||
3. Usar `mattn/go-sqlite3`
|
||||
4. Compilar como binario estático
|
||||
5. Embeber frontend con `embed.FS`
|
||||
|
||||
En ambos casos, la API REST como contrato permite el cambio de backend
|
||||
sin modificar el frontend.
|
||||
|
||||
---
|
||||
|
||||
*Documento generado como parte de la primera iteración del proyecto Inventschario.*
|
||||
184
docs/roadmap-iteracion-1.md
Normal file
184
docs/roadmap-iteracion-1.md
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
# Inventschario — Roadmap Iteración 1
|
||||
|
||||
**Fecha:** 2026-08-03
|
||||
**Objetivo:** MVP funcional con gestión de dispositivos, copias de seguridad
|
||||
e importación/exportación CSV.
|
||||
|
||||
---
|
||||
|
||||
## Fase 0: Infraestructura del Proyecto
|
||||
|
||||
- [ ] Inicializar repositorio git
|
||||
- [ ] Crear estructura de directorios
|
||||
- [ ] Configurar `pyproject.toml` con dependencias
|
||||
- [ ] Crear `requirements.txt`
|
||||
- [ ] Configurar `.gitignore` (Python, SQLite, backups, __pycache__)
|
||||
- [ ] Escribir `README.md` con instrucciones de desarrollo
|
||||
|
||||
## Fase 1: Capa de Datos
|
||||
|
||||
- [ ] Implementar `schema.sql` con el esquema completo
|
||||
- [ ] Implementar `connection.py` (conexión SQLite, WAL mode, foreign keys)
|
||||
- [ ] Implementar `migrations.py` (creación de tablas, upgrades futuros)
|
||||
- [ ] Implementar `DeviceRepository` (CRUD completo)
|
||||
- [ ] Implementar `HistoryRepository` (log de cambios)
|
||||
- [ ] Implementar `ConfigRepository` (key-value store)
|
||||
- [ ] Implementar `BackupRepository` (log de backups)
|
||||
- [ ] Tests unitarios para todos los repositories
|
||||
|
||||
## Fase 2: Capa de Servicios
|
||||
|
||||
- [ ] Implementar `DeviceService` (CRUD, baja, enajenación, historial)
|
||||
- [ ] Implementar `BackupService` (crear, restaurar, eliminar, verificar antigüedad)
|
||||
- [ ] Implementar `ConfigService` (leer/actualizar config, labels, setup)
|
||||
- [ ] Implementar `ImportService` (parse CSV, mapeo, validación, duplicados)
|
||||
- [ ] Implementar `ExportService` (generar CSV con etiquetas renombradas)
|
||||
- [ ] Tests unitarios para todos los services
|
||||
|
||||
## Fase 3: API Gateway
|
||||
|
||||
- [ ] Configurar Flask app con Blueprint pattern
|
||||
- [ ] Implementar CORS para localhost
|
||||
- [ ] Implementar middleware de errores
|
||||
- [ ] Rutas: `/api/v1/devices` (CRUD, búsqueda, filtros)
|
||||
- [ ] Rutas: `/api/v1/devices/:id/decommission`, `/dispose`
|
||||
- [ ] Rutas: `/api/v1/devices/:id/history`
|
||||
- [ ] Rutas: `/api/v1/config`, `/api/v1/config/labels`
|
||||
- [ ] Rutas: `/api/v1/config/initialize`
|
||||
- [ ] Rutas: `/api/v1/backups` (listar, crear, restaurar, eliminar)
|
||||
- [ ] Rutas: `/api/v1/import/csv`, `/api/v1/export/csv`
|
||||
- [ ] Rutas: `/api/v1/locations`, `/api/v1/departments`
|
||||
- [ ] Tests de integración para todos los endpoints
|
||||
|
||||
## Fase 4: Frontend — Estructura Base
|
||||
|
||||
- [ ] Crear `index.html` con estructura SPA
|
||||
- [ ] Implementar `app.js` (router hash-based)
|
||||
- [ ] Implementar `api.js` (cliente HTTP con manejo de errores)
|
||||
- [ ] Implementar sistema de componentes (table, form, modal, toast, sidebar)
|
||||
- [ ] Crear estilos globales con CSS custom properties
|
||||
- [ ] Implementar responsive layout (sidebar collapse)
|
||||
|
||||
## Fase 5: Frontend — Páginas
|
||||
|
||||
### 5.1 Dashboard
|
||||
- [ ] Estadísticas: total dispositivos, por estado, por tipo
|
||||
- [ ] Últimos dispositivos añadidos
|
||||
- [ ] Alertas de garantía próxima (futuro, placeholder)
|
||||
- [ ] Recordatorio de backups antiguos
|
||||
|
||||
### 5.2 Inventario (Tabla)
|
||||
- [ ] Tabla paginada con sorting por columnas
|
||||
- [ ] Barra de búsqueda full-text
|
||||
- [ ] Filtros: tipo, estado, departamento, ubicación
|
||||
- [ ] Acciones por fila: ver, editar, baja, enajenar
|
||||
- [ ] Selección múltiple para exportación
|
||||
|
||||
### 5.3 Formulario de Dispositivo
|
||||
- [ ] Campos obligatorios marcados
|
||||
- [ ] Validación en tiempo real
|
||||
- [ ] Autocompletado de campos de registro
|
||||
- [ ] Selector de tipo de dispositivo
|
||||
- [ ] Selector de ubicación / departamento
|
||||
- [ ] Modo creación y modo edición
|
||||
|
||||
### 5.4 Detalle del Dispositivo
|
||||
- [ ] Ficha completa con todos los campos
|
||||
- [ ] Historial de cambios cronológico
|
||||
- [ ] Botones de acción: editar, baja, enajenar
|
||||
- [ ] Foto del dispositivo (futuro)
|
||||
|
||||
### 5.5 Baja / Enajenación
|
||||
- [ ] Modal con tipo de baja/enajenación
|
||||
- [ ] Campo de fecha efectiva
|
||||
- [ ] Campo de motivo / observaciones
|
||||
- [ ] Campo de responsable autorizador
|
||||
- [ ] Confirmación antes de ejecutar
|
||||
|
||||
### 5.6 Importar / Exportar
|
||||
- [ ] Selector de archivo CSV para importar
|
||||
- [ ] Vista previa de los primeros 10 registros
|
||||
- [ ] Mapeo de columnas (automático por nombre/posición)
|
||||
- [ ] Opciones ante duplicados
|
||||
- [ ] Registro de errores por fila
|
||||
- [ ] Botón de exportación con filtros actuales
|
||||
- [ ] Selector de separador CSV
|
||||
|
||||
### 5.7 Configuración
|
||||
- [ ] Nombre de institución
|
||||
- [ ] Edición de etiquetas de campos de registro
|
||||
- [ ] Configuración de backups (hora, retención)
|
||||
- [ ] Gestión de ubicaciones
|
||||
- [ ] Gestión de departamentos
|
||||
|
||||
### 5.8 Wizard de Configuración Inicial
|
||||
- [ ] Detección de primera ejecución
|
||||
- [ ] 6 pasos del wizard
|
||||
- [ ] Validación en cada paso
|
||||
- [ ] Creación de la BD al finalizar
|
||||
|
||||
## Fase 6: Copias de Seguridad
|
||||
|
||||
- [ ] Implementar scheduler con `schedule` library
|
||||
- [ ] Backup automático a la hora configurada
|
||||
- [ ] Compresión gzip del archivo .db
|
||||
- [ ] Registro en `backup_log`
|
||||
- [ ] Verificación de backups antiguos al iniciar
|
||||
- [ ] Toast de recordatorio al usuario
|
||||
- [ ] Función de restaurar desde backup
|
||||
|
||||
## Fase 7: Empaquetado
|
||||
|
||||
- [ ] Configurar PyInstaller spec file
|
||||
- [ ] Incluir frontend estático en el bundle
|
||||
- [ ] Crear icono de la aplicación (.ico)
|
||||
- [ ] Script de instalación (crear directorio + acceso directo Menú Inicio)
|
||||
- [ ] Test de distribución: ejecutar en máquina limpia de Windows
|
||||
|
||||
## Fase 8: Testing y Calidad
|
||||
|
||||
- [ ] Tests unitarios: > 80% cobertura en services
|
||||
- [ ] Tests de integración: todos los endpoints API
|
||||
- [ ] Prueba manual de flujo completo
|
||||
- [ ] Revisión de accesibilidad (WCAG 2.1 AA)
|
||||
- [ ] Prueba de rendimiento con 1,000 dispositivos
|
||||
|
||||
---
|
||||
|
||||
## Estimación de Esfuerzo
|
||||
|
||||
| Fase | Días estimados | Dependencias |
|
||||
|-------|:--------------:|-----------------|
|
||||
| 0 | 0.5 | — |
|
||||
| 1 | 2 | Fase 0 |
|
||||
| 2 | 2 | Fase 1 |
|
||||
| 3 | 2 | Fase 2 |
|
||||
| 4 | 2 | Fase 0 |
|
||||
| 5 | 5 | Fases 3, 4 |
|
||||
| 6 | 1 | Fase 1 |
|
||||
| 7 | 1 | Fases 3-6 |
|
||||
| 8 | 1 | Fases 3-7 |
|
||||
| **Total** | **~16 días** | |
|
||||
|
||||
---
|
||||
|
||||
## Criterios de Salida de la Iteración 1
|
||||
|
||||
1. El usuario puede instalar la app en su perfil de Windows
|
||||
2. El wizard de configuración crea la BD correctamente
|
||||
3. Se pueden crear, editar, ver y eliminar dispositivos
|
||||
4. Se pueden dar de baja y enajenar dispositivos
|
||||
5. La tabla de inventario tiene búsqueda, filtros y ordenación
|
||||
6. Las etiquetas de campos son renombrables
|
||||
7. Los backups se ejecutan diariamente
|
||||
8. Se muestra recordatorio de backups antiguos
|
||||
9. Se puede exportar a CSV
|
||||
10. Se puede importar desde CSV
|
||||
11. El historial de cambios se registra
|
||||
12. La interfaz es navegable por teclado
|
||||
13. Los colores cumplen WCAG 2.1 AA
|
||||
|
||||
---
|
||||
|
||||
*Roadmap generado como parte de la primera iteración del proyecto Inventschario.*
|
||||
*Este documento es la base para el feedback del usuario antes de comenzar la implementación.*
|
||||
Loading…
Add table
Add a link
Reference in a new issue