mirror of
https://github.com/marcogll/telegram_new_socias.git
synced 2026-01-13 21:25:16 +00:00
feat: Implementar registro de usuarios en base de datos dual
Este commit introduce las siguientes mejoras en el proceso de onboarding:
1. **Registro en Dos Fases**: El flujo de onboarding ahora registra a las nuevas usuarias en dos bases de datos distintas para mejorar la seguridad y la integridad de los datos:
* ****: Se crea un registro básico para la autenticación y el control de acceso del bot.
* ****: Se guarda un perfil completo y detallado de la empleada en la tabla .
2. **Modelos SQLAlchemy**: Se han actualizado los modelos de SQLAlchemy ( y ) para reflejar la estructura de las tablas de la base de datos.
3. **Lógica de Base de Datos Centralizada**: El módulo ahora contiene la lógica para gestionar el registro dual, asegurando que ambas operaciones se realicen de forma atómica.
4. **Flujo de Onboarding Actualizado**: El script de ha sido modificado para recopilar la información necesaria y pasarla al nuevo sistema de registro.
5. **Configuración de Docker**: Se ha ajustado el y los scripts de inicialización de la base de datos ( y ) para soportar el nuevo esquema de base de datos dual.
Estos cambios aseguran un proceso de registro más robusto, seguro y escalable, sentando las bases para futuras funcionalidades de RRHH.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import logging
|
||||
import os
|
||||
from datetime import datetime, date
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from models.users_alma_models import Base as BaseUsersAlma, User
|
||||
@@ -38,6 +39,73 @@ SessionVanityAttendance = sessionmaker(autocommit=False, autoflush=False, bind=e
|
||||
# --- GOOGLE SHEETS SETUP (REMOVED) ---
|
||||
# Duplicate checking is now done via database.
|
||||
|
||||
def _parse_date(value):
|
||||
if not value:
|
||||
return None
|
||||
if isinstance(value, datetime):
|
||||
return value.date()
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
for parser in (
|
||||
lambda v: datetime.fromisoformat(v),
|
||||
lambda v: datetime.strptime(v, "%Y-%m-%d"),
|
||||
lambda v: datetime.strptime(v, "%d/%m/%Y"),
|
||||
):
|
||||
try:
|
||||
return parser(str(value)).date()
|
||||
except Exception:
|
||||
continue
|
||||
return None
|
||||
|
||||
def _parse_datetime(value):
|
||||
if not value:
|
||||
return None
|
||||
if isinstance(value, datetime):
|
||||
return value
|
||||
if isinstance(value, date):
|
||||
return datetime.combine(value, datetime.min.time())
|
||||
try:
|
||||
return datetime.fromisoformat(str(value))
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
return datetime.strptime(str(value), "%Y-%m-%d %H:%M:%S")
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def _build_full_address(domicilio: dict) -> str:
|
||||
if not domicilio:
|
||||
return ""
|
||||
partes = []
|
||||
calle = domicilio.get("calle")
|
||||
num_ext = domicilio.get("num_ext")
|
||||
num_int = domicilio.get("num_int")
|
||||
colonia = domicilio.get("colonia")
|
||||
cp = domicilio.get("cp")
|
||||
ciudad = domicilio.get("ciudad")
|
||||
estado = domicilio.get("estado")
|
||||
|
||||
if calle:
|
||||
linea = f"{calle}"
|
||||
if num_ext:
|
||||
linea += f" {num_ext}"
|
||||
if num_int and num_int not in ("0", "N/A"):
|
||||
linea += f" Int {num_int}"
|
||||
partes.append(linea)
|
||||
if colonia:
|
||||
partes.append(colonia)
|
||||
if ciudad or estado:
|
||||
partes.append(", ".join(filter(None, [ciudad, estado])))
|
||||
if cp:
|
||||
partes.append(f"CP {cp}")
|
||||
return " - ".join(partes)
|
||||
|
||||
def _references_with_padding(referencias: list) -> list:
|
||||
refs = referencias or []
|
||||
if len(refs) < 3:
|
||||
refs = refs + [{}] * (3 - len(refs))
|
||||
return refs[:3]
|
||||
|
||||
def chat_id_exists(chat_id: int) -> bool:
|
||||
"""Checks if a Telegram chat_id already exists in the USERS_ALMA.users table."""
|
||||
if not SessionUsersAlma:
|
||||
@@ -55,30 +123,149 @@ def chat_id_exists(chat_id: int) -> bool:
|
||||
session.close()
|
||||
|
||||
def register_user(user_data: dict) -> bool:
|
||||
"""Registers a new user in the USERS_ALMA.users table."""
|
||||
if not SessionUsersAlma:
|
||||
logging.warning("SessionUsersAlma not initialized. Cannot register user.")
|
||||
"""
|
||||
Persists a new colaboradora across the USERS_ALMA.users and vanity_hr.data_empleadas tables.
|
||||
|
||||
Expected structure (all keys optional but recommended):
|
||||
{
|
||||
"meta": {...}, "metadata": {...}, "candidato": {...}, "contacto": {...},
|
||||
"domicilio": {...}, "laboral": {...}, "referencias": [...], "emergencia": {...}
|
||||
}
|
||||
"""
|
||||
if not SessionUsersAlma or not SessionVanityHr:
|
||||
logging.warning("Database sessions not initialized. Cannot register user.")
|
||||
return False
|
||||
|
||||
session = SessionUsersAlma()
|
||||
meta = user_data.get("meta") or {}
|
||||
metadata = user_data.get("metadata") or {}
|
||||
candidato = user_data.get("candidato") or {}
|
||||
contacto = user_data.get("contacto") or {}
|
||||
domicilio = user_data.get("domicilio") or {}
|
||||
laboral = user_data.get("laboral") or {}
|
||||
referencias = _references_with_padding(user_data.get("referencias") or [])
|
||||
emergencia = user_data.get("emergencia") or {}
|
||||
|
||||
telegram_id = metadata.get("chat_id") or meta.get("telegram_id")
|
||||
if not telegram_id:
|
||||
logging.error("register_user: missing telegram_id; aborting persist.")
|
||||
return False
|
||||
|
||||
# --- USERS_ALMA.users ---
|
||||
session_users = SessionUsersAlma()
|
||||
try:
|
||||
new_user = User(
|
||||
telegram_id=str(user_data.get("chat_id")),
|
||||
username=user_data.get("telegram_user"),
|
||||
first_name=user_data.get("first_name"),
|
||||
last_name=f"{user_data.get('apellido_paterno', '')} {user_data.get('apellido_materno', '')}".strip(),
|
||||
email=user_data.get("email"),
|
||||
cell_phone=user_data.get("celular"),
|
||||
role='user' # Default role
|
||||
)
|
||||
session.add(new_user)
|
||||
session.commit()
|
||||
logging.info(f"User {user_data.get('chat_id')} registered successfully in DB.")
|
||||
return True
|
||||
except Exception as e:
|
||||
session.rollback()
|
||||
logging.error(f"Error registering user in DB: {e}")
|
||||
user_record = session_users.query(User).filter(User.telegram_id == str(telegram_id)).first()
|
||||
if user_record:
|
||||
user_record.username = metadata.get("telegram_user") or meta.get("username")
|
||||
user_record.first_name = candidato.get("nombre_preferido") or meta.get("first_name")
|
||||
apellidos = f"{candidato.get('apellido_paterno', '')} {candidato.get('apellido_materno', '')}".strip()
|
||||
user_record.last_name = apellidos or user_record.last_name
|
||||
user_record.email = contacto.get("email") or user_record.email
|
||||
user_record.cell_phone = contacto.get("celular") or user_record.cell_phone
|
||||
else:
|
||||
user_record = User(
|
||||
telegram_id=str(telegram_id),
|
||||
username=metadata.get("telegram_user") or meta.get("username"),
|
||||
first_name=candidato.get("nombre_preferido") or meta.get("first_name"),
|
||||
last_name=f"{candidato.get('apellido_paterno', '')} {candidato.get('apellido_materno', '')}".strip(),
|
||||
email=contacto.get("email"),
|
||||
cell_phone=contacto.get("celular"),
|
||||
role='user'
|
||||
)
|
||||
session_users.add(user_record)
|
||||
session_users.commit()
|
||||
except Exception as exc:
|
||||
session_users.rollback()
|
||||
logging.error(f"Error persisting user in USERS_ALMA: {exc}")
|
||||
return False
|
||||
finally:
|
||||
session.close()
|
||||
session_users.close()
|
||||
|
||||
# --- vanity_hr.data_empleadas ---
|
||||
numero_empleado = laboral.get("numero_empleado") or f"T{telegram_id}"
|
||||
fecha_registro = _parse_datetime(metadata.get("fecha_registro")) or datetime.utcnow()
|
||||
fecha_procesamiento = datetime.utcnow()
|
||||
fecha_ingreso = _parse_date(laboral.get("fecha_inicio"))
|
||||
fecha_nacimiento = _parse_date(candidato.get("fecha_nacimiento"))
|
||||
tiempo_registro_minutos = None
|
||||
try:
|
||||
duracion_segundos = float(metadata.get("duracion_segundos", 0))
|
||||
tiempo_registro_minutos = int(round(duracion_segundos / 60))
|
||||
except Exception:
|
||||
tiempo_registro_minutos = None
|
||||
|
||||
nombre = candidato.get("nombre_oficial") or ""
|
||||
apellido_paterno = candidato.get("apellido_paterno") or ""
|
||||
apellido_materno = candidato.get("apellido_materno") or ""
|
||||
nombre_completo = " ".join(filter(None, [nombre, apellido_paterno, apellido_materno])).strip()
|
||||
|
||||
domicilio_completo = _build_full_address(domicilio)
|
||||
telegram_username = metadata.get("telegram_user") or meta.get("username")
|
||||
try:
|
||||
telegram_chat_id = int(telegram_id)
|
||||
except Exception:
|
||||
telegram_chat_id = None
|
||||
|
||||
empleada_payload = {
|
||||
"numero_empleado": numero_empleado,
|
||||
"puesto": laboral.get("rol_id"),
|
||||
"sucursal": laboral.get("sucursal_id"),
|
||||
"fecha_ingreso": fecha_ingreso,
|
||||
"estatus": "activo",
|
||||
"nombre_completo": nombre_completo or nombre,
|
||||
"nombre": nombre or meta.get("first_name"),
|
||||
"nombre_preferido": candidato.get("nombre_preferido"),
|
||||
"apellido_paterno": apellido_paterno,
|
||||
"apellido_materno": apellido_materno,
|
||||
"fecha_nacimiento": fecha_nacimiento,
|
||||
"lugar_nacimiento": candidato.get("lugar_nacimiento"),
|
||||
"rfc": candidato.get("rfc"),
|
||||
"curp": candidato.get("curp"),
|
||||
"email": contacto.get("email"),
|
||||
"telefono_celular": contacto.get("celular"),
|
||||
"domicilio_calle": domicilio.get("calle"),
|
||||
"domicilio_numero_exterior": domicilio.get("num_ext"),
|
||||
"domicilio_numero_interior": domicilio.get("num_int"),
|
||||
"domicilio_numero_texto": domicilio.get("num_ext_texto"),
|
||||
"domicilio_colonia": domicilio.get("colonia"),
|
||||
"domicilio_codigo_postal": domicilio.get("cp"),
|
||||
"domicilio_ciudad": domicilio.get("ciudad"),
|
||||
"domicilio_estado": domicilio.get("estado"),
|
||||
"domicilio_completo": domicilio_completo,
|
||||
"emergencia_nombre": emergencia.get("nombre"),
|
||||
"emergencia_telefono": emergencia.get("telefono"),
|
||||
"emergencia_parentesco": emergencia.get("relacion"),
|
||||
"referencia_1_nombre": referencias[0].get("nombre"),
|
||||
"referencia_1_telefono": referencias[0].get("telefono"),
|
||||
"referencia_1_tipo": referencias[0].get("relacion"),
|
||||
"referencia_2_nombre": referencias[1].get("nombre"),
|
||||
"referencia_2_telefono": referencias[1].get("telefono"),
|
||||
"referencia_2_tipo": referencias[1].get("relacion"),
|
||||
"referencia_3_nombre": referencias[2].get("nombre"),
|
||||
"referencia_3_telefono": referencias[2].get("telefono"),
|
||||
"referencia_3_tipo": referencias[2].get("relacion"),
|
||||
"origen_registro": "telegram_bot",
|
||||
"telegram_usuario": telegram_username,
|
||||
"telegram_chat_id": telegram_chat_id,
|
||||
"bot_version": metadata.get("bot_version"),
|
||||
"fecha_registro": fecha_registro,
|
||||
"tiempo_registro_minutos": tiempo_registro_minutos,
|
||||
"fecha_procesamiento": fecha_procesamiento
|
||||
}
|
||||
|
||||
session_hr = SessionVanityHr()
|
||||
try:
|
||||
existing = session_hr.get(DataEmpleadas, numero_empleado)
|
||||
if existing:
|
||||
for field, value in empleada_payload.items():
|
||||
setattr(existing, field, value)
|
||||
else:
|
||||
session_hr.add(DataEmpleadas(**empleada_payload))
|
||||
session_hr.commit()
|
||||
logging.info(f"User {telegram_id} registered in vanity_hr.data_empleadas as {numero_empleado}.")
|
||||
return True
|
||||
except Exception as exc:
|
||||
session_hr.rollback()
|
||||
logging.error(f"Error persisting colaboradora in vanity_hr: {exc}")
|
||||
return False
|
||||
finally:
|
||||
session_hr.close()
|
||||
|
||||
@@ -406,16 +406,15 @@ async def finalizar(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||
|
||||
# --- REGISTRO EN BASE DE DATOS ---
|
||||
db_ok = register_user({
|
||||
**meta,
|
||||
**payload["metadata"],
|
||||
**payload["candidato"],
|
||||
**payload["contacto"]
|
||||
"meta": meta,
|
||||
**payload
|
||||
})
|
||||
|
||||
chat_id_log = payload.get("metadata", {}).get("chat_id", meta.get("telegram_id"))
|
||||
if db_ok:
|
||||
logging.info(f"Usuario {meta['chat_id']} registrado en la base de datos.")
|
||||
logging.info(f"Usuario {chat_id_log} registrado en la base de datos.")
|
||||
else:
|
||||
logging.error(f"Fallo al registrar usuario {meta['chat_id']} en la base de datos.")
|
||||
logging.error(f"Fallo al registrar usuario {chat_id_log} en la base de datos.")
|
||||
|
||||
if enviado:
|
||||
await update.message.reply_text(
|
||||
|
||||
Reference in New Issue
Block a user