// Expediente de evidencia
Sistema de Inventarios 3DFAB
Inventario para el taller 3DFAB: componentes por ubicación, movimientos de existencias, materiales de órdenes de trabajo y alertas de reposición. Desarrollo integral propio y sistema en operación activa; esta ficha distingue ese uso de las comprobaciones locales.
- Para quién
Taller 3DFAB: consulta de componentes físicos, ubicaciones y materiales para trabajos de fabricación.
- Mi responsabilidad
Desarrollo completo: modelo de datos, backend Django, interfaz, QR, reportes, pruebas y documentación. Proyecto de autoría y desarrollo propio.
- Estado actual
Operativo en producción activa. Validado en una copia local aislada con SQLite y datos sintéticos; producción no inspeccionada.
- Qué permite hacer
Consultar existencias, registrar su uso y reconocer necesidades de reposición. Sin métricas acreditadas de ahorro, rendimiento o disponibilidad.
Contexto
3DFAB trabaja con componentes físicos y materiales de fabricación. El inventario agrupa la información de cada pieza con su categoría, ubicación principal, cantidad disponible y umbral de reposición. Las órdenes de trabajo relacionan los materiales utilizados con un trabajo concreto.
Desarrollé todo el sistema y se encuentra actualmente operativo. No se dispone de un número confirmado de usuarios ni de mediciones de uso real; la ejecución descrita aquí corresponde a un laboratorio local.
Problema
Consultar una cantidad aislada no explica dónde está el componente ni qué operación la cambió. El problema del dominio es reunir catálogo, ubicación, movimientos y consumo en trabajos, además de señalar existencias que necesitan reposición.
La consistencia requiere distinguir un movimiento pendiente de uno aplicado y comprobar las operaciones repetidas o simultáneas. Un historial disponible no demuestra, por sí solo, que todo saldo físico tenga una apertura documentada.
Mi rol
Diseñé y desarrollé la solución completa en Django: modelos y relaciones, formularios, vistas, reglas de existencias, interfaz, QR, alertas, reportes, pruebas y documentación. La atribución procede de mi confirmación, no de inferir responsabilidades a partir de Git.
Esta revisión del caso de estudio inspecciona y prueba una copia del producto. No incorpora reparaciones funcionales al repositorio fuente.
Restricciones
La validación debía funcionar en local sin tocar datos ni servicios del taller. Se excluyeron credenciales, base de datos existente, archivos subidos y registros; únicamente se copiaron código, documentación y archivos de configuración necesarios.
No hay lockfile de Python y algunas dependencias no tienen versión exacta. Se reutilizó el entorno instalado, registrando sus versiones, sin actualizar paquetes para conseguir pruebas aprobadas. PostgreSQL, correo externo, lectores físicos y recuperación de respaldos quedan fuera de la ejecución.
Solución
El catálogo reúne componentes, categorías, ubicaciones, fotografías y especificaciones. Las búsquedas combinan nombre, código, categoría, ubicación y estado de stock; las listas usan paginación y relaciones precargadas.
Los movimientos permiten entrada, salida, traslado y ajuste por conteo. El consumo en órdenes de trabajo genera salidas. Las señales crean alertas y códigos QR; los reportes producen PDF, Excel y CSV. El QR contiene una instantánea informativa del componente y no prueba un flujo integrado de escaneo móvil. Los indicadores se calculan al consultar la página, sin acreditar actualización continua.
Arquitectura y modelo de datos
Monolito Django 5: autenticación y sesiones → vistas y formularios → modelos de dominio → persistencia. Las páginas se renderizan en el servidor, con HTMX para consultas y Alpine.js para interacción. Tailwind y Chart.js se cargan desde servicios externos en las plantillas revisadas.
El modelo incluye categorías, ubicaciones, componentes, historial de movimientos, órdenes, uso de componentes y alertas. QR e imágenes se almacenan como archivos, fuera de las transacciones de la base de datos.
SQLite es el motor predeterminado. DATABASE_URL contempla PostgreSQL, con paquetes adicionales descritos en la guía y ausentes del manifiesto principal. No se ejecutó ese motor. La presencia de select_for_update() no aporta bloqueo de fila en SQLite.
Leer el diagrama y sus límites
Cómo leer el recorrido: El usuario del taller accede a la interfaz web (Django con HTMX y Alpine.js) para registrar y consultar componentes. En paralelo, el administrador gestiona modelos y usuarios directamente desde el panel nativo Django Admin. La creación de órdenes de trabajo dispara movimientos de consumo de materiales hacia el dominio de inventario.
Componentes y persistencia: La capa central agrupa los modelos Django y las reglas de negocio del inventario, gestionando componentes, ubicaciones y movimientos. Esta capa interactúa con SQLite local mediante el ORM. Las señales post_save ejecutan la lógica de alertas e intentan generar etiquetas QR, cuyos archivos se almacenan en disco.
Simplificaciones del diagrama: En la vista móvil se condensan los actores y las interfaces en bloques combinados para mantener la legibilidad de las relaciones esenciales sin forzar scroll horizontal ni compresión de etiquetas.
Evidencia y alcance comprobado: La lógica central, movimientos y catálogos fueron ejecutados y verificados localmente con SQLite (32/37 pruebas aprobadas). La exportación de datos en formato CSV se comprobó satisfactoriamente. PostgreSQL está contemplado en la configuración arquitectónica, pero no fue ejecutado ni probado en un entorno productivo real.
Límites del sistema: El almacenamiento de archivos e imágenes QR opera fuera de la transacción de la base de datos (un fallo en disco no revierte el registro). La reedición de órdenes de trabajo presentó efectos duplicados en las pruebas de consumo. El sistema no incluye colas asíncronas ni transacciones distribuidas, y la concurrencia en alta demanda no fue evaluada.
Mapa de implementación y evidencia técnica. Pruebas unitarias y de stock ejecutadas localmente sobre SQLite; el motor PostgreSQL y la concurrencia en producción no fueron validados en entorno real.
Flujo principal de inventario
Recorrido comprobado mediante el cliente HTTP de Django, con CSRF activo: iniciar una sesión de prueba, crear un componente sin existencias, registrar una entrada de diez unidades, aplicar el movimiento y consultar su ficha y respuesta de stock. Se verificaron cantidad persistida, estado aplicado y usuario asignado por el servidor.
La prueba de dominio recorrió salida, reposición, traslado y ajuste: comprobó saldos, cambio de ubicación y creación/resolución de alertas. El navegador confirmó inicio de sesión y presentación del catálogo sintético. Son recorridos locales, no evidencia de operación continua ni pruebas de un lector físico.
Concurrencia e integridad de existencias
La aplicación del movimiento agrupa actualización de stock y estado del historial en una transacción. Una salida superior al saldo se rechazó sin cambios. Al simular un fallo después de escribir el stock, se revirtieron saldo, movimiento y alerta; una nueva lectura permitió reintentar correctamente.
Dos retiradas solapadas sobre diez unidades usaron hilos y conexiones reales a SQLite, sincronizados durante la lectura: una se aplicó, la otra recibió un error de bloqueo; quedaron dos unidades y un movimiento aplicado. Esto demuestra contención, no un manejo satisfactorio de concurrencia ni un resultado trasladable a PostgreSQL.
Las pruebas detectaron repetición de efectos con objetos desactualizados y al volver a guardar consumos de órdenes. También comprobaron que un saldo inicial introducido directamente no genera un movimiento de apertura. Por ello, idempotencia completa y conciliación exhaustiva permanecen pendientes de refuerzo.
Decisiones de arquitectura
Django concentra formularios, ORM, autenticación y administración en una aplicación mantenible. HTMX permite enriquecer consultas sin duplicar toda la interfaz en un cliente separado; es una decisión coherente con el producto, no una comparación de rendimiento entre frameworks.
Las transacciones atómicas limitan escrituras parciales de movimientos. El bloqueo solicitado por el ORM depende del motor: PostgreSQL contempla bloqueo de fila, SQLite no implementa esa operación. Cambiar de motor requiere pruebas propias.
Las señales automatizan QR y alertas. El QR se guarda sin disparar otra cadena de señales, pero un fallo de archivo conserva el componente sin etiqueta y no establece recuperación automática.
La documentación describe correcciones previas en QR, stock, formularios y consultas. No se encontraron ADR numerados que respalden las referencias anteriores de la ficha.
Seguridad y control de acceso
Implementado: autenticación Django, contraseñas PBKDF2, cookies de sesión HttpOnly, middleware CSRF y controles de configuración para HTTPS. Comprobado localmente: redirección de visitantes sin sesión, rechazo de POST sin CSRF y acceso con una cuenta sintética.
La separación por rol de la interfaz general no quedó aprobada. La existencia de grupos y permisos en la administración no equivale a su aplicación en cada vista. El inventario es compartido y no se presenta como aislamiento entre clientes.
El rate limiting existe y una prueba con caché limpia observó el bloqueo esperado; los casos originales comparten estado de caché y una ejecución adicional varió entre ventanas del limitador. No se acredita resistencia global a abuso. La configuración local conserva cinco advertencias de despliegue; producción no fue auditada. No se publican rutas operativas, secretos ni registros del taller.
Pruebas y alcance de validación
Se ejecutaron módulos reales en una copia aislada: Python 3.12.10, Django 5.0.1 y SQLite en archivo, sin copiar el entorno operativo. El arranque local, las migraciones, el análisis sintáctico y la comprobación de dependencias se completaron. No existe una tarea dedicada de lint ni de tipos en el proyecto.
La primera ejecución original dio 30 aprobados y 7 errores. Tras collectstatic, sin cambiar aserciones ni código, quedaron 32 aprobados, 4 fallos y 1 error. Los cuatro fallos corresponden al limitador y su presentación; el error combina un cliente sin enforcement CSRF con validación de precio ausente.
Las 15 comprobaciones adicionales terminaron con 9 aprobadas y 6 fallidas. La tabla separa lo comprobado de los incumplimientos: reproducir un defecto no lo convierte en una garantía aprobada. No se midió cobertura, carga máxima ni latencia productiva.
| Comprobación | Resultado | Alcance y Evidencia |
|---|---|---|
| Suite original | 32/37; 4 fallos, 1 error | Con estáticos preparados. Caché compartida en pruebas del limitador; error de formulario con precio ausente. |
| Pruebas adicionales | 9/15; 6 fallos | Módulos reales: recorrido HTTP, dominio, controles de acceso, integridad, concurrencia y archivos. |
| Alta → entrada → consulta | Aprobado localmente | CSRF activo; saldo 0 → 10, movimiento aplicado y respuesta presentada. |
| Salida, traslado y ajuste | Aprobados localmente | Saldos y ubicación comprobados; alertas creadas y resueltas. |
| Fallos parciales | Rollback comprobado | Fallo de guardado inyectado. Fallo de QR simulado: componente conservado, etiqueta ausente. |
| Concurrencia SQLite | Contención reproducida | 2 retiradas solapadas: 1 aplicada, 1 error de bloqueo. PostgreSQL no ejecutado. |
| Integridad y permisos | Refuerzo pendiente | Fallaron aislamiento por rol, reenvío con estado antiguo, reedición de consumo y apertura del historial. |
| Exportaciones | Resultado parcial | CSV conserva código y cantidad; PDF generado y cabecera válida. Excel conserva valores como texto: prueba numérica fallida. |
| Configuración y arranque | Completados en local | check sin incidencias; migraciones, sintaxis y dependencias aprobadas. 5 advertencias de despliegue. |
Despliegue y entorno
Operativo en el taller 3DFAB. La guía plantea Gunicorn y WhiteNoise, proxy Nginx/Apache, HTTPS, PostgreSQL y respaldos. Son instrucciones de despliegue, no evidencia de que esa topología describa la instancia actual.
Se arrancó únicamente el servidor de desarrollo en loopback sobre la base sintética. Se generaron los estáticos exigidos por la configuración y se verificó el acceso desde el navegador. No se inspeccionaron infraestructura productiva, correo, restauraciones, disponibilidad ni dispositivos del taller.
Resultado y estado actual
El resultado entregado es una aplicación que reúne consulta de piezas, ubicación, movimientos, alertas y reportes. Desarrollé la totalidad del sistema y se encuentra en operación activa. La ejecución respalda el recorrido básico de inventario y aporta límites concretos para priorizar mejoras.
No hay evidencia suficiente para cuantificar ahorro, rapidez de búsqueda, reducción de errores o disponibilidad. Tampoco se afirma equivalencia absoluta entre stock físico e historial. El valor demostrado es centralizar el trabajo y hacer visibles sus reglas y estados.
Evidencia y límites
Implementado: modelos, formularios, vistas, señales y exportaciones inspeccionados. Comprobado localmente: suite original, comprobaciones adicionales, arranque, recorrido HTTP y presentación en navegador. Simulado: fallo al guardar el movimiento y fallo de generación de QR. Alcance: desarrollo propio y estado operativo. No comprobado: entorno productivo, PostgreSQL, lectura física de QR, correo y restauración.
La revisión incluyó los cambios locales de configuración numérica y dashboard. Las copias se contrastaron mediante SHA-256; el repositorio fuente se conservó intacto. El registro saneado y el entorno reproducible permanecen internos; esta página explica método y resultados sin ofrecer descargas.
Comprobaciones de la base sintética
La base utilizada se creó exclusivamente con datos sintéticos. No se abrió la base del taller. Se verificaron saldos, estados de movimientos, alertas y efectos de recuperación. La creación directa con saldo positivo carece de apertura automática; no se extrapola este resultado a registros reales.
Concurrencia y operaciones repetidas
Dos conexiones reales a SQLite solaparon la lectura del mismo componente antes de escribir. El saldo final fue coherente en ese escenario, pero la segunda operación recibió un bloqueo. El escenario no mide capacidad máxima y no simula PostgreSQL. Las pruebas de reenvío y consumo repetido encontraron límites separados de la contención.
Calidad de software y UX de taller
El catálogo y los formularios reúnen las operaciones del taller; mensajes, estados pendientes y alertas facilitan interpretar qué quedó registrado. La búsqueda y la paginación limitan el volumen presentado; select_related reduce consultas repetidas en listados.
Calidad de software significa comprobar las reglas de inventario y sus fallos, no sumar pruebas aprobadas. La revisión añade escenarios de reenvío, concurrencia, rollback y exportaciones que el recorrido feliz no cubría.
La UX del producto depende de recursos externos para estilos e interacción; no se acredita funcionamiento completo sin red. Esta ficha ofrece índice, navegación entre proyectos, diagrama legible y código ajustable en móvil. No se presenta una puntuación de accesibilidad ni un benchmark.
Muestra de código y prueba de ingeniería
Adaptación pública y autocontenida de una regla de salida: validar cantidad, rechazar sobregiro y calcular si se alcanza el umbral. Requiere Python 3 y solo su biblioteca estándar. Se ejecutaron exactamente los dos bloques concatenados. No importa código reservado ni sustituye las transacciones reales del sistema.
def plan_dispatch(stock, quantity, minimum): |
"""Regla pura de stock: sin base de datos, red ni código reservado.""" |
values = (stock, quantity, minimum) |
if any(type(value) is not int for value in values): |
raise ValueError("Se requieren valores enteros") |
if stock < 0 or minimum < 0 or quantity <= 0: |
raise ValueError("Stock o cantidad no válidos") |
if quantity > stock: |
raise ValueError("Stock insuficiente") |
remaining = stock - quantity |
return remaining, remaining <= minimum |
Prueba de ingeniería aislada
import unittest |
|
class StockRuleTest(unittest.TestCase): |
def test_dispatch_and_threshold(self): |
self.assertEqual(plan_dispatch(10, 8, 3), (2, True)) |
|
def test_threshold_boundary(self): |
self.assertEqual(plan_dispatch(10, 7, 3), (3, True)) |
|
def test_above_threshold(self): |
self.assertEqual(plan_dispatch(10, 2, 3), (8, False)) |
|
def test_exact_stock(self): |
self.assertEqual(plan_dispatch(10, 10, 0), (0, True)) |
|
def test_reject_invalid_inputs(self): |
for values in [(10, 11, 3), (10, 0, 3), (-1, 1, 0), |
(10, 1, -1), (10, True, 3), (10, 1.5, 3)]: |
with self.subTest(values=values): |
with self.assertRaises(ValueError): |
plan_dispatch(*values) |
|
unittest.main() |
5 pruebas aprobadas; el caso de entradas inválidas incluye 6 subcasos. Comprueba exclusivamente esta regla pura: no demuestra concurrencia, permisos, persistencia ni operación productiva.