Plataforma e-commerce | INACAP TI3V21

Recorrido del codigo, desde cero

Esta pagina explica el proyecto a alguien que nunca lo ha visto: en que orden leer los archivos y que hace cada pieza importante.

Paso 0. Estructura del repositorio

ecommerce/
  main.py              menu de consola (punto de entrada)
  requirements.txt     dependencias (requests, flask, waitress)
  README.md            guia de instalacion, seguridad y uso de IA
  model/               clases del negocio, una por archivo
  dao/                 acceso a datos SQLite
  servicios/           validacion de entradas e indicador del dolar
  tests/               guion de pruebas P01 a P19 automatizado
  web/                 sitio de documentacion y consola en vivo
  docs/                informes PDF, diagrama UML y evidencias
  datos/               base de datos SQLite (se crea sola)

Paso 1. El modelo: un dato con validacion obligatoria

El atributo identificador del cliente tiene property y validacion en el setter. Si el RUT no cumple el Modulo 11 o el correo esta mal escrito, el objeto ni siquiera se construye.

@identificador.setter
def identificador(self, valor: str) -> None:
    valor = str(valor).strip()
    if self._tipo_identificador == "EMAIL":
        if not validar_email(valor):
            raise IdentificadorInvalidoError(
                f"El correo '{valor}' no tiene un formato valido."
            )
    else:
        if not validar_rut(valor):
            raise IdentificadorInvalidoError(
                f"El RUT '{valor}' no supera la validacion Modulo 11."
            )
    self._identificador = valor

Paso 2. Herencia y polimorfismo

Los tres subtipos heredan de Producto con super().__init__() y sobrescriben procesar_entrega(). El pedido recorre sus lineas sin preguntar de que tipo son: cada objeto sabe como se entrega.

class ProductoDigital(Producto):
    def procesar_entrega(self, detalle) -> dict:
        return {
            "tipo": "DESCARGA_DIGITAL",
            "flete_calculado": 0.0,
            "enlace": self._enlace_descarga,
            "mensaje": "Entrega inmediata sin despacho (flete $0).",
        }

# En Pedido, el polimorfismo ocurre sin un solo if sobre el tipo:
def procesar_despacho_entregas(self) -> list:
    return [d.producto.procesar_entrega(d) for d in self._detalles]

Paso 3. Las dos reglas del negocio como excepciones propias

def confirmar_pedido(self) -> bool:
    ...
    for detalle in self._detalles:
        producto = detalle.producto
        if not producto.tiene_stock_suficiente(detalle.cantidad):
            raise StockInsuficienteError(
                producto.nombre, detalle.cantidad, producto.stock
            )
    return True

def despachar(self, operador: Trabajador) -> bool:
    if not operador.puede_despachar():
        raise SinPermisoError("despachar pedidos", operador.rol)
    if self._estado_pago != "PAGADO":
        raise PedidoNoPagadoError(self._id_pedido, self._estado_pago)
    self._estado_entrega = "DESPACHADO"
    return True

En main.py esas excepciones se capturan de forma especifica y el menu continua funcionando: except ClickAndGoError as error: print(...).

Paso 4. Persistencia con DAO y consultas parametrizadas

El pedido y sus lineas de detalle se guardan en una sola transaccion SQL. Ese es el requisito de la transaccion multi-linea.

with self._bd.conectar() as conexion:
    conexion.execute(
        "INSERT INTO pedidos (id_pedido, id_cliente, ...) "
        "VALUES (:id_pedido, :id_cliente, ...)",
        pedido.to_dict(),
    )
    for detalle in pedido.detalles:
        conexion.execute(
            "INSERT INTO detalle_pedido (id_pedido, id_producto, cantidad, "
            "precio_unitario_congelado) VALUES (:id_pedido, :id_producto, "
            ":cantidad, :precio)",
            {...},
        )
    conexion.commit()

Paso 5. Consumo de la API externa

def obtener_valor_dolar(self) -> ValorDolar:
    try:
        respuesta = requests.get(self._url, timeout=self._timeout)
        respuesta.raise_for_status()
        serie = respuesta.json().get("serie") or []
        ultimo = serie[0]
        return ValorDolar(float(ultimo["valor"]), str(ultimo["fecha"])[:10],
                          "mindicador.cl (API en linea)")
    except (requests.RequestException, ValueError, KeyError) as error:
        raise IndicadorNoDisponibleError(
            f"No fue posible obtener el dolar: {error}"
        ) from error

Paso 6. Recorrido del menu principal

OpcionQue permite demostrar
1. ClientesValidacion obligatoria de RUT o correo y CRUD
2. ProductosCRUD completo del catalogo y los cuatro subtipos
3. PedidosTransaccion multi-linea, detalle, reglas de negocio y entrega polimorfica
4. Dolar del diaPrecio con indicador externo y continuidad si la API falla
5. TrabajadoresPermisos distintos por rol (RBAC)
6. Iniciar sesionAutenticacion con contrasena cifrada
7. Datos de demostracionCarga de un catalogo de ejemplo

Ejecutarlo en tu computador

git clone https://github.com/MiguelTroncoso/ecommerce.git
cd ecommerce
python -m venv .venv
source .venv/bin/activate        # En Windows: .venv\Scripts\activate
pip install -r requirements.txt
python main.py