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:
- Capa de Presentación: Maneja la interacción con el usuario (entradas y salidas).
- Capa de Lógica de Negocio: Contiene toda la lógica central, como la verificación de contraseñas.
- 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.