DEV Community

 AppInitDev
AppInitDev

Posted on

Cómo evitar vender inventario inexistente: actualización condicional en SQLite

Cómo evitar vender inventario inexistente: actualización condicional en SQLite

Una venta debería descontar existencias solo si queda cantidad suficiente. El clásico patrón «leer stock, restar y guardar» puede fallar cuando dos operaciones se ejecutan casi a la vez.

El problema y el alcance del ejemplo

La ficha de POSManager: TPV y Caja Ventas resume su función así: «TPV y caja para comercios: controla ventas, stock y tickets desde el móvil.» POSManager describe cobros, ventas, tickets y control de stock. Para estudiar una decisión de diseño vinculada al inventario, usamos una actualización SQL condicionada dentro de una transacción. No estamos describiendo la base de datos real del producto.

Cómo reproducir la idea

Puedes ejecutarlo con Python 3, que incluye SQLite. Guarda el archivo como venta.py y ejecútalo. La base está en memoria para que cada prueba comience desde cero.

import sqlite3

db = sqlite3.connect(":memory:")
db.execute("CREATE TABLE productos(id INTEGER PRIMARY KEY, stock INTEGER NOT NULL)")
db.execute("CREATE TABLE ventas(producto INTEGER, cantidad INTEGER)")
db.execute("INSERT INTO productos VALUES(1, 3)")
db.commit()

def vender(producto, cantidad):
    if cantidad <= 0:
        raise ValueError("Cantidad inválida")
    with db:  # transacción y rollback automático ante error
        cur = db.execute(
            "UPDATE productos SET stock=stock-? WHERE id=? AND stock>=?",
            (cantidad, producto, cantidad),
        )
        if cur.rowcount != 1:
            raise ValueError("Stock insuficiente o producto inexistente")
        db.execute("INSERT INTO ventas VALUES(?, ?)", (producto, cantidad))

vender(1, 2)
print("Stock restante:", db.execute("SELECT stock FROM productos").fetchone()[0])
try:
    vender(1, 2)
except ValueError as e:
    print("Venta rechazada:", e)
Enter fullscreen mode Exit fullscreen mode

Qué demuestra el código

La condición stock >= ? se verifica en la misma instrucción que descuenta inventario. Si ninguna fila fue modificada, la venta no se inserta. La transacción relaciona los dos efectos: movimiento de stock y registro de venta.

Pruebas recomendadas

Deberías obtener stock 1 tras la primera venta y rechazo de la segunda. Prueba vender 0 unidades y un identificador desconocido. Comprueba que la tabla de ventas contiene únicamente una fila.

Límites que conviene reconocer

La prueba no modela devoluciones, cobros, sincronización offline, precios, impuestos ni múltiples dispositivos con inventarios divergentes. En un sistema distribuido la transacción local no resuelve por sí sola conflictos entre terminales.

Qué aprendemos

El stock es una invariante de negocio; conviene protegerla en la operación de escritura, no solo mediante validaciones en la interfaz.

Contexto y enlaces del autor

El tema se relaciona con las funciones declaradas públicamente de POSManager: TPV y Caja Ventas, una aplicación Android de AppInitDev. El código anterior es un ejemplo independiente: no se ha inspeccionado el repositorio privado ni se afirma que sea la implementación interna de la app. La referencia aparece al final para ofrecer contexto, no como requisito para completar el tutorial.

APPS

Referencia técnica

SQLite: transacciones

Top comments (0)