Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Guía de Referencia 4: El Mostrador

Toda la maquinaria de las guías anteriores existe en el vacío hasta que alguien puede usarla. Esta guía construye el mostrador del almacén: el menú infinito, la entrada de datos a prueba de errores y la central de mando que conecta cada opción con su función.

Porteros Robustos para la Entrada (Bucle de Validación)

Recuerda la lección del Capítulo 10: nada de lo que teclea el usuario es de fiar. Va a escribir "caro" cuando le pidas un precio. Si pasas eso directo a float(), el programa colapsa. La solución es el patrón del portero robusto: un while True que insiste hasta recibir un dato válido.

def pedir_decimal(mensaje: str) -> float:
    """Insiste hasta recibir un número decimal válido y no negativo."""
    while True:
        try:
            numero = float(input(mensaje))
            if numero < 0:
                print("No se permiten cantidades negativas.")
                continue
            return numero
        except ValueError:
            print("Eso no es un número. Intenta de nuevo (p. ej. 5.50).")


def pedir_entero(mensaje: str) -> int:
    """Insiste hasta recibir un número entero válido y no negativo."""
    while True:
        try:
            numero = int(input(mensaje))
            if numero < 0:
                print("No se permiten cantidades negativas.")
                continue
            return numero
        except ValueError:
            print("Eso no es un número entero. Intenta de nuevo (p. ej. 100).")

El flujo tiene dos defensas. La primera es el try/except: si la conversión falla, el except atrapa el ValueError y el bucle vuelve a preguntar. La segunda es la regla de negocio: un almacén no maneja precios ni stocks negativos, así que el continue rechaza esos valores y repite la pregunta. La función solo termina por el return, y al return solo se llega con un dato sano.

El Reporte del Almacén

def mostrar_reporte(inventario: list[dict]) -> None:
    """Imprime el estado actual del almacén."""
    print(f"\n--- INVENTARIO ({len(inventario)} productos) ---")
    if len(inventario) == 0:
        print("El almacén está vacío.")
    for producto in inventario:
        ficha = f"[{producto['id']}] {producto['nombre']}"
        print(f"{ficha} | ${producto['precio']} | stock: {producto['stock']}")

Nada exótico: un for sobre la lista y un f-string por producto. El caso del almacén vacío merece su propio mensaje; una pantalla en blanco deja al usuario preguntándose si el programa se rompió.

El Menú

def mostrar_menu() -> None:
    """Imprime las opciones disponibles."""
    print("\n=== SGI v1.0: ALMACÉN DIGITAL ===")
    print("1. Mostrar inventario")
    print("2. Agregar producto")
    print("3. Buscar producto")
    print("4. Eliminar producto")
    print("5. Guardar y salir")

La Central de Mando: main()

Aquí se conecta todo. Un bucle while True muestra el menú, lee la opción y la clasifica con match-case:

def main() -> None:
    """La central de mando del sistema: carga los datos, atiende y guarda."""
    inventario = cargar_datos(RUTA_INVENTARIO)
    print(f"Sistema iniciado: {len(inventario)} productos en el almacén.")

    while True:
        mostrar_menu()
        opcion = input("Elige una opción: ")

        match opcion:
            case "1":
                mostrar_reporte(inventario)
            case "2":
                nombre = input("Nombre del producto: ")
                precio = pedir_decimal("Precio unitario: ")
                stock = pedir_entero("Stock inicial: ")
                producto = agregar_producto(inventario, nombre, precio, stock)
                print(
                    f"Registrado '{producto['nombre']}' "
                    f"con ID {producto['id']}."
                )
            case "3":
                print("Opción en construcción. Este reto es tuyo, Gerente.")
            case "4":
                print("Opción en construcción. Este reto es tuyo, Gerente.")
            case "5":
                guardar_datos(RUTA_INVENTARIO, inventario)
                print("Inventario guardado. Hasta pronto, Gerente.")
                break
            case _:
                print("Opción no válida. Elige un número del 1 al 5.")


main()

Puntos clave del diseño:

  • Cargar primero: la primera línea de main reconstruye el inventario desde el disco. Todo lo que pase después trabaja sobre esa lista.
  • Guardar antes de salir: la opción 5 llama a guardar_datos y después hace break. Si rompes el bucle sin guardar, todo el trabajo de la sesión se evapora con la RAM.
  • El comodín case _ atrapa cualquier disparate que escriba el usuario, sin colapsar.
  • La última línea del archivo, main(), es la que enciende el sistema. Sin ella, definiste todo y no ejecutaste nada. (En el ensamblaje final esa llamada gana un guardián; la Guía 5 explica por qué.)

Cuidado: match compara texto. input() siempre devuelve str, así que los patrones son "1", "2"… con comillas. Si escribes case 1: sin comillas, ninguna opción coincidirá jamás.

Nota: te toca conectar las opciones 3 y 4. Las opciones “Buscar producto” y “Eliminar producto” aparecen en el menú, pero ahora mismo solo imprimen un aviso. Las funciones buscar_producto() y eliminar_producto() ya existen desde la Guía 3, probadas y listas. Cablearlas es tu trabajo:

  • Para buscar: pide el ID con input(), llama a buscar_producto() y revisa el resultado con is None. Si existe, muéstralo; si no, dilo claramente.
  • Para eliminar: recuerda que eliminar_producto() devuelve una lista nueva. Si no haces inventario = eliminar_producto(...), el producto seguirá ahí.

Pista extra: ¿cómo confirmas que la eliminación funcionó? Compara len() antes y después, o busca el ID tras eliminar.

Si te atascas, el archivo completo de la Guía 5 trae ambas opciones resueltas para comparar. Inténtalo tú primero.

Todas las piezas están sobre la mesa. Falta verlas juntas en un solo archivo ejecutable y ponerlo a prueba: sigue con la Guía de Referencia 5, donde ensamblas inventario.py completo y lo sometes a tres pruebas de funcionalidad.