Proyecto Práctico: Cajero Automático y Carrito de Compras

Desarrollo del Proyecto

En el desarrollo real de software, se suelen pasar por etapas de análisis de requisitos, diseño de arquitectura, desarrollo en equipo, pruebas y entrega. Como programadores, frecuentmeente participamos en las primeras tres fases.

Análisis de Requisitos

El proyecto requiere dos módulos principales: gestión de cuentas y gestión del carrito de compras, que se desglosan en las siguientes funcionalidades:

  • Registro de usuario
  • Inicio de sesión
  • Consulta de saldo
  • Depósito en cuenta
  • Retiro de efectivo
  • Transferencia entre cuentas
  • Consulta de movimientos
  • Agregar productos al carrito
  • Ver carrito de compras
  • Pagar el carrito

Las funciones de registro e inicio de sesión gestionan los datos de usuario, que se almacenan en archivos. Las demás funciones modifican esos datos.

Diseño de Arquitectura

El proyecto adopta una arquitectura de tres capas:

  1. Capa de Presentación: Maneja la interacción con el usuario (entradas y salidas).
  2. Capa de Lógica de Negocio: Contiene toda la lógica central, como la verificación de contraseñas.
  3. Capa de Acceso a Datos: Se encarga exclusivamente de leer y escribir datos.

Esta separación mejora la seguridad y el mantenimiento, aunque en este proyecto no se implementará la capa de red.

Estructura del Proyecto


proyecto_atm  # Carpeta raíz
├── inicio.py  # Punto de entrada
├── config     # Configuración
│   └── settings.py
├── logica     # Capa de lógica de negocio
│   ├── usuario.py
│   ├── cuenta.py
│   └── compras.py
├── datos      # Capa de acceso a datos
│   ├── archivos/
│   └── gestor_datos.py
├── vistas     # Capa de presentación
│   ├── menu.py
│   └── autenticacion.py
└── utilidades
    └── helpers.py

Implementación del Registro

Versión Inicial (Monolítica)


RUTA_DATOS = "datos/archivos"

def registrar_usuario():
    while True:
        nombre = input("Nombre de usuario: ").strip()
        ruta_archivo = f"{RUTA_DATOS}/{nombre}.json"
        if os.path.exists(ruta_archivo):
            print("El usuario ya existe.")
            continue
        clave = input("Contraseña: ").strip()
        clave2 = input("Confirmar contraseña: ").strip()
        if clave != clave2:
            print("Las contraseñas no coinciden.")
            continue
        datos = {
            "nombre": nombre,
            "clave": clave,
            "saldo": 15000.0,
            "carrito": {},
            "movimientos": []
        }
        with open(ruta_archivo, "w") as f:
            json.dump(datos, f)
        print("Registro exitoso.")
        break

Versión Modular (Arquitectura por Capas)

La capa de presentación gestiona la interacción:


from logica.usuario import existe_usuario, crear_usuario

def menu_registro():
    while True:
        nombre = input("Nombre de usuario: ").strip()
        if existe_usuario(nombre):
            print("El nombre ya está en uso.")
            continue
        clave = input("Contraseña: ").strip()
        clave2 = input("Confirmar: ").strip()
        if clave != clave2:
            print("No coinciden.")
            continue
        exito, mensaje = crear_usuario(nombre, clave)
        print(mensaje)
        if exito:
            break

La capa de lógica verifica las reglas de negocio:


from datos.gestor_datos import leer_usuario, guardar_usuario

def existe_usuario(nombre):
    return leer_usuario(nombre) is not None

def crear_usuario(nombre, clave):
    if existe_usuario(nombre):
        return False, "El usuario ya existe."
    datos = {
        "nombre": nombre,
        "clave": clave,
        "saldo": 15000.0,
        "carrito": {},
        "movimientos": []
    }
    guardar_usuario(datos)
    return True, "Cuenta creada exitosamente."

La capa de datos maneja el almacenamiento:


import json
from config.settings import RUTA_ARCHIVOS

def leer_usuario(nombre):
    ruta = f"{RUTA_ARCHIVOS}/{nombre}.json"
    try:
        with open(ruta, "r") as f:
            return json.load(f)
    except FileNotFoundError:
        return None

def guardar_usuario(datos):
    nombre = datos["nombre"]
    ruta = f"{RUTA_ARCHIVOS}/{nombre}.json"
    with open(ruta, "w") as f:
        json.dump(datos, f)

Implementación de Funcionalidades

Inicio de Sesión

La capa de presentación maneja el flujo:


from logica.autenticacion import verificar_credenciales
from utilidades.helpers import cifrar_contrasena

estado_sesion = {"usuario": None}

def menu_login():
    if estado_sesion["usuario"]:
        print("Ya hay una sesión activa.")
        return
    usuario = input("Usuario: ").strip()
    clave = input("Contraseña: ").strip()
    clave_cifrada = cifrar_contrasena(clave)
    exito, msg = verificar_credenciales(usuario, clave_cifrada)
    if exito:
        estado_sesion["usuario"] = usuario
    print(msg)

La lógica central valida las credenciales:


from datos.gestor_datos import leer_usuario

def verificar_credenciales(usuario, clave_cifrada):
    datos = leer_usuario(usuario)
    if not datos:
        return False, "Usuario no encontrado."
    if datos["clave"] == clave_cifrada:
        return True, f"Bienvenido, {usuario}."
    return False, "Contraseña incorrecta."

Funcinoes de Cuenta

Para consultar el saldo:


from vistas.autenticacion import requiere_login
from logica.cuenta import obtener_saldo
from utilidades.helpers import registrar_movimiento

@requiere_login
def ver_saldo():
    usuario = estado_sesion["usuario"]
    saldo = obtener_saldo(usuario)
    print(f"Saldo disponible: ${saldo:.2f}")

La lógica para depositar fondos:


from datos.gestor_datos import leer_usuario, guardar_usuario

def depositar(usuario, monto):
    if monto <= 0 or monto > 100000:
        return False, "Monto inválido."
    datos = leer_usuario(usuario)
    datos["saldo"] += monto
    guardar_usuario(datos)
    return True, f"Depósito de ${monto:.2f} exitoso."

Gestión del Carrito

Para agregar productos:


from vistas.autenticacion import requiere_login
from logica.compras import agregar_al_carrito, mostrar_catalogo

@requiere_login
def agregar_producto():
    catalogo = mostrar_catalogo()
    print(catalogo)
    producto_id = input("ID del producto: ").strip()
    cantidad = int(input("Cantidad: ").strip())
    usuario = estado_sesion["usuario"]
    exito, msg = agregar_al_carrito(usuario, producto_id, cantidad)
    print(msg)

Lógica de negocio para el carrito:


from datos.gestor_datos import leer_usuario, guardar_usuario, leer_productos

def agregar_al_carrito(usuario, producto_id, cantidad):
    productos = leer_productos()
    if producto_id not in productos:
        return False, "Producto no válido."
    datos = leer_usuario(usuario)
    carrito = datos["carrito"]
    nombre_prod = productos[producto_id]["nombre"]
    if nombre_prod in carrito:
        carrito[nombre_prod]["cantidad"] += cantidad
    else:
        carrito[nombre_prod] = {
            "cantidad": cantidad,
            "precio": productos[producto_id]["precio"]
        }
    guardar_usuario(datos)
    return True, "Producto agregado."

Utilidades Comunes

Función para cifrado de contraseñas:


import hashlib

def cifrar_contrasena(contrasena):
    hasher = hashlib.sha256()
    hasher.update(contrasena.encode())
    return hasher.hexdigest()

Decorador para autenticación:


def requiere_login(func):
    def envoltura(*args, **kwargs):
        if not estado_sesion["usuario"]:
            print("Debe iniciar sesión primero.")
            menu_login()
            return
        return func(*args, **kwargs)
    return envoltura

Funcionalidades de Administrador

Los administradores pueden agregar productos al sistema y bloquear usuarios, siguiendo la misma arquitectura de capas.

Etiquetas: Python json arquitectura-capas cajero-automatico carrito-compras

Publicado el 6-14 06:58