Del CRUD al mantenimiento real

Aplicaciones, juegos y utilidades desarrolladas en Delphi Heaven, con capturas, avances, código fuente y versiones compiladas para Windows.
jaoj
Site Admin
Mensajes: 49
Registrado: Mar Jun 30, 2026 10:27 am

Del CRUD al mantenimiento real

Mensaje por jaoj »

trazabilidad, transacciones y auditoría con Delphi + SQLite

Durante las últimas sesiones de PC Shop he trabajado en algo que va más allá de añadir una nueva función: hacer evolucionar una aplicación que ya contiene datos sin romper su histórico.

Hasta ahora PRODUCTOS.Stock indicaba cuántas unidades quedaban, pero no explicaba cómo se había llegado hasta ese valor.

Para resolverlo se añadió MOVIMIENTOS_STOCK:

Código: Seleccionar todo

ENTRADA +5
SALIDA  -2
AJUSTE  +3
AJUSTE  -1
La regla utilizada es:

Código: Seleccionar todo

StockNuevo = StockActual + Cantidad
Además, modificación de stock y registro del movimiento forman una única transacción:

Código: Seleccionar todo

BEGIN TRANSACTION
        ↓
UPDATE PRODUCTOS.Stock
        ↓
INSERT MOVIMIENTOS_STOCK
        ↓
COMMIT
Si algo falla, se ejecuta ROLLBACK.

Integración con las ventas

Las ventas generan ahora automáticamente una SALIDA de stock:

Código: Seleccionar todo

INSERT VENTA
     ↓
obtener IdVenta
     ↓
UPDATE STOCK
     ↓
INSERT MOVIMIENTO_STOCK
     ↓
COMMIT
El identificador se obtiene mediante:

Código: Seleccionar todo

SELECT last_insert_rowid();
y queda relacionado así:

Código: Seleccionar todo

VENTAS.Id = 37
MOVIMIENTOS_STOCK.IdVenta = 37
Los movimientos manuales mantienen IdVenta = NULL.

Auditoría

Después se añadió una comprobación capaz de detectar:

- ventas sin movimiento;
- movimientos duplicados;
- movimientos asociados a ventas inexistentes;
- diferencias entre cantidad vendida y salida de stock.

La primera auditoría produjo:

Código: Seleccionar todo

Ventas revisadas: 9
Sin movimiento: 8
Duplicados: 0
Huérfanos: 0
Cantidades incoherentes: 0
Pero esas 8 ventas no eran errores: eran ventas históricas creadas antes de existir MOVIMIENTOS_STOCK.

Para distinguirlas se añadió:

Código: Seleccionar todo

Trazable INTEGER NOT NULL DEFAULT 0
De esta forma:

Código: Seleccionar todo

Trazable = 0 → venta histórica
Trazable = 1 → venta sometida a las reglas actuales
La migración se realiza sin borrar datos mediante:

Código: Seleccionar todo

PRAGMA table_info(VENTAS)

ALTER TABLE VENTAS
ADD COLUMN Trazable INTEGER NOT NULL DEFAULT 0;
Después del cambio:

Código: Seleccionar todo

Ventas revisadas: 1
Sin movimiento: 0
Duplicados: 0
Huérfanos: 0
Cantidades incoherentes: 0

TRAZABILIDAD CORRECTA
Finalmente se añadió una protección adicional en SQLite:

Código: Seleccionar todo

CREATE UNIQUE INDEX IF NOT EXISTS UX_MOVIMIENTOS_STOCK_IdVenta
ON MOVIMIENTOS_STOCK(IdVenta)
WHERE IdVenta IS NOT NULL;
Así una venta no puede terminar accidentalmente con dos movimientos asociados, mientras que los movimientos manuales pueden seguir utilizando IdVenta = NULL.

El resultado combina varias capas:

Código: Seleccionar todo

Lógica Delphi
      +
Transacciones
      +
Auditoría
      +
Restricciones SQLite
      ↓
Mayor integridad de datos
Este bloque me ha servido especialmente para trabajar mantenimiento de aplicaciones existentes, migraciones de esquema, compatibilidad con datos antiguos, FireDAC, SQLite, transacciones, logging, auditoría e integridad de datos.

Artículo completo en InformatOLI . org

[]
Responder