Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,4 @@ __pycache__/
*.txt
logs/
NOTES.md
AUDITORIA_TECNICA_ALKE_CRM_CLAUDE.md
AUDITORIA_TECNICA_ALKE_CRM_CHATGPT.md
18 changes: 16 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# Alke CRM - Sistema de Gestión de Clientes (MVP) 🚀

Curso: Desarrollo de Aplicaciones Fullstack Python Trainee (AM)
Institución: SENCE
OTEC: Pixelab
Profesor: Ariel Rosenamnn
Alumno: Roberto Otárola

> ⚠️ **Nota de Versión (MVP):**
> Este proyecto se entrega bajo la modalidad de **Producto Mínimo Viable**.
> Siguiendo principios de desarrollo ágil, se ha priorizado la **robustez del Backend**, la **arquitectura Clean Code** y la **integridad de datos** (SQLite/Validaciones).
> Las interfaces gráficas (GUI) y conexiones a APIs externas están planificadas para la "Fase 2" del roadmap, permitiendo una entrega funcional y testeable dentro del deadline crítico.

## 📋 Descripción del Proyecto
**Alke CRM** es una solución de software robusta y modular desarrollada en Python 3 para la gestión eficiente de clientes de la empresa **Solution Tech**.

Expand Down Expand Up @@ -37,8 +48,8 @@ Proveer una herramienta de línea de comandos (CLI) que permita el ciclo de vida

1. **Clonar el repositorio:**
```bash
git clone [https://github.com/tu-usuario/alke-crm-mvp.git](https://github.com/tu-usuario/alke-crm-mvp.git)
cd alke-crm-mvp
git clone [https://github.com/RobertoOtarola/alke-crm](https://github.com/RobertoOtarola/alke-crm)
cd alke-crm
```

2. **Crear entorno virtual:**
Expand Down Expand Up @@ -74,3 +85,6 @@ El proyecto sigue una arquitectura por capas para garantizar escalabilidad y man
Para ejecutar las pruebas unitarias:
```bash
pytest tests/ -v

## 📊 Cobertura de Testing
pytest --cov=src --cov-report=term
6 changes: 6 additions & 0 deletions data/clientes.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
uid,nombre,email,telefono,tipo,empresa,descuento
1,Juan,juan@mail.com,123,ClienteRegular,,
2,María,maria@mail.com,456,ClienteRegular,,
3,Pedro,pedro@mail.com,789,ClienteRegular,,
4,Ana,ana@mail.com,135,ClientePremium,,0.1
5,Sostener,sostener@mail.com,246,ClienteCorporativo,Sostener Ltda.,
39 changes: 39 additions & 0 deletions data/clientes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
[
{
"uid": 1,
"nombre": "Juan",
"email": "juan@mail.com",
"telefono": "123",
"tipo": "ClienteRegular"
},
{
"uid": 2,
"nombre": "María",
"email": "maria@mail.com",
"telefono": "456",
"tipo": "ClienteRegular"
},
{
"uid": 3,
"nombre": "Pedro",
"email": "pedro@mail.com",
"telefono": "789",
"tipo": "ClienteRegular"
},
{
"uid": 4,
"nombre": "Ana",
"email": "ana@mail.com",
"telefono": "135",
"tipo": "ClientePremium",
"descuento": 0.1
},
{
"uid": 5,
"nombre": "Sostener",
"email": "sostener@mail.com",
"telefono": "246",
"tipo": "ClienteCorporativo",
"empresa": "Sostener Ltda."
}
]
Binary file added data/database.db
Binary file not shown.
158 changes: 158 additions & 0 deletions docs/POO_ALKE_CRM.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
## Programación Orientada a Objetos en el Proyecto Alke CRM (MVP)

Curso: Desarrollo de Aplicaciones Fullstack Python Trainee (AM)
Institución: SENCE
OTEC: Pixelab
Profesor: Ariel Rosenamnn
Alumno: Roberto Otárola

1. Introducción

La Programación Orientada a Objetos (POO) es un paradigma de desarrollo de software basado en la modelación de entidades del dominio mediante objetos que encapsulan estado y comportamiento. En el proyecto Alke CRM (MVP), la POO constituye el eje estructural del sistema, permitiendo construir una arquitectura modular, extensible, mantenible y escalable.

El sistema modela clientes de distintos tipos (Regular, Premium y Corporativo) aplicando principios formales de abstracción, encapsulación, herencia y polimorfismo.

2. Principios Fundamentales Aplicados
2.1 Abstracción

Se define una clase abstracta Cliente como representación genérica del concepto cliente:

class Cliente(ABC):

Esto permite:

Modelar atributos comunes (uid, nombre, email, teléfono)

Definir contrato obligatorio mediante métodos abstractos

Desacoplar lógica de negocio de implementación concreta

2.2 Encapsulación

Los atributos se definen como privados:

self._nombre
self._email

El acceso se controla mediante @property, garantizando:

Protección de invariantes

Validaciones internas

Integridad del dominio

Esto evita que la UI o la capa de persistencia violen reglas del negocio.

2.3 Herencia

Se implementan subclases:

ClienteRegular

ClientePremium

ClienteCorporativo

Todas heredan de Cliente:

class ClientePremium(Cliente):

Esto permite reutilización de código y especialización de comportamiento.

2.4 Polimorfismo

Cada subclase implementa comportamiento diferenciado:

def mostrar_info(self):

El sistema trata objetos como tipo base Cliente, pero ejecuta comportamiento específico según el tipo real.

Esto es evidente en el método _mapear_fila_a_objeto() del Repository, donde se reconstruyen objetos dinámicamente.

3. Patrones de Diseño Aplicados
3.1 Repository Pattern

Se implementa ClienteRepository como capa de acceso a datos.

Responsabilidades:

Aislar SQLite del dominio

Centralizar operaciones CRUD

Traducir errores técnicos a excepciones de negocio

Esto cumple con el principio de separación de responsabilidades.

3.2 Factory Interno

El método _mapear_fila_a_objeto() actúa como factory:

if tipo == 'ClienteCorporativo':

Permite reconstrucción polimórfica desde persistencia.

4. Manejo de Excepciones como Modelo de Dominio

Se define una jerarquía de excepciones:

ClientAlreadyExistsError

ClientNotFoundError

Esto desacopla el dominio de errores técnicos de SQLite y mejora la claridad semántica.

5. Validaciones como Protección del Dominio

Las validaciones de email y teléfono se ejecutan en el constructor del modelo:

validar_email(self._email)

Esto garantiza que ningún objeto inválido pueda existir en memoria.

Es una aplicación práctica del principio:

“Los objetos deben proteger su propio estado.”

6. Beneficios Arquitectónicos Obtenidos

La aplicación de POO en el proyecto permitió:

Alta cohesión

Bajo acoplamiento

Extensibilidad futura (ej: nuevos tipos de cliente)

Testabilidad independiente

Claridad estructural

7. Justificación de Enfoque MVP

Debido a restricción temporal, se priorizó:

Núcleo de negocio

Persistencia

Validaciones

Arquitectura sólida

Se postergaron:

GUI

Integraciones externas

Sin comprometer la calidad del diseño orientado a objetos.

8. Conclusión

El proyecto Alke CRM (MVP) implementa de manera rigurosa los principios de la POO, aplicando además patrones arquitectónicos modernos como Repository y Factory.

La estructura lograda permite evolucionar el sistema hacia una versión completa con interfaz y servicios externos sin necesidad de reestructuración profunda del núcleo.

El diseño es consistente con buenas prácticas profesionales y cumple los objetivos académicos del módulo #4.
32 changes: 32 additions & 0 deletions src/config/logging_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import os
import logging
from logging.handlers import RotatingFileHandler
from src.core.constants import FilePaths

def setup_logging():
"""Configura el logging con rotación de archivos."""

# Asegurar que el directorio logs existe
log_dir = os.path.dirname(FilePaths.LOG_PATH)
os.makedirs(log_dir, exist_ok=True)

# Configurar Handler con Rotación (Max 5MB, guarda 3 archivos previos)
handler = RotatingFileHandler(
FilePaths.LOG_PATH,
maxBytes=5*1024*1024,
backupCount=3,
encoding='utf-8'
)

formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(name)s - %(message)s')
handler.setFormatter(formatter)

# Logger raíz
logger = logging.getLogger()
logger.setLevel(logging.INFO)
logger.addHandler(handler)

# Evitar duplicidad de handlers si se llama varias veces
if logger.hasHandlers():
logger.handlers.clear()
logger.addHandler(handler)
Loading