Commit inicial: Sales Bot - Sistema de Automatización de Ventas
- Stack completo con Mattermost, NocoDB y Sales Bot - Procesamiento OCR de tickets con Tesseract - Sistema de comisiones por tubos de tinte - Comandos slash /metas y /ranking - Documentación completa del proyecto Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
611
sales-bot/nocodb_client.py
Normal file
611
sales-bot/nocodb_client.py
Normal file
@@ -0,0 +1,611 @@
|
||||
import requests
|
||||
import logging
|
||||
import os
|
||||
from datetime import datetime, date, timezone, timedelta
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Zona horaria de México (UTC-6)
|
||||
TZ_MEXICO = timezone(timedelta(hours=-6))
|
||||
|
||||
class NocoDBClient:
|
||||
def __init__(self, url, token):
|
||||
self.url = url.rstrip('/')
|
||||
self.token = token
|
||||
self.headers = {
|
||||
'xc-token': self.token,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
|
||||
# IDs de tablas desde variables de entorno
|
||||
self.table_vendedores = os.getenv('NOCODB_TABLE_VENDEDORES')
|
||||
self.table_ventas = os.getenv('NOCODB_TABLE_VENTAS')
|
||||
self.table_metas = os.getenv('NOCODB_TABLE_METAS')
|
||||
self.table_detalle = os.getenv('NOCODB_TABLE_VENTAS_DETALLE')
|
||||
|
||||
# NUEVA CONFIGURACIÓN DE COMISIONES
|
||||
self.META_DIARIA_TUBOS = 3 # Meta: 3 tubos diarios
|
||||
self.COMISION_POR_TUBO = 10.0 # $10 por tubo después del 3ro
|
||||
|
||||
def test_connection(self):
|
||||
"""Prueba la conexión con NocoDB"""
|
||||
try:
|
||||
response = requests.get(
|
||||
f"{self.url}/api/v2/meta/bases",
|
||||
headers=self.headers,
|
||||
timeout=10
|
||||
)
|
||||
response.raise_for_status()
|
||||
bases = response.json()
|
||||
logger.info(f"Conexión exitosa con NocoDB. Bases: {len(bases.get('list', []))}")
|
||||
return {
|
||||
'status': 'success',
|
||||
'bases_count': len(bases.get('list', []))
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Error conectando con NocoDB: {str(e)}")
|
||||
return {'status': 'error', 'message': str(e)}
|
||||
|
||||
def get_vendedor(self, username):
|
||||
"""Obtiene información de un vendedor por username"""
|
||||
try:
|
||||
response = requests.get(
|
||||
f"{self.url}/api/v2/tables/{self.table_vendedores}/records",
|
||||
headers=self.headers,
|
||||
params={'limit': 100},
|
||||
timeout=10
|
||||
)
|
||||
response.raise_for_status()
|
||||
vendedores = response.json().get('list', [])
|
||||
|
||||
for vendedor in vendedores:
|
||||
if vendedor.get('username') == username:
|
||||
return vendedor
|
||||
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"Error obteniendo vendedor {username}: {str(e)}")
|
||||
return None
|
||||
|
||||
def crear_vendedor(self, username, nombre_completo, email, meta_diaria_tubos=3):
|
||||
"""Crea un nuevo vendedor con meta diaria de tubos"""
|
||||
try:
|
||||
payload = {
|
||||
'username': username,
|
||||
'nombre_completo': nombre_completo,
|
||||
'email': email,
|
||||
'meta_diaria_tubos': meta_diaria_tubos, # Nueva: meta en tubos por día
|
||||
'activo': True,
|
||||
'fecha_registro': datetime.now(TZ_MEXICO).isoformat()
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{self.url}/api/v2/tables/{self.table_vendedores}/records",
|
||||
headers=self.headers,
|
||||
json=payload,
|
||||
timeout=10
|
||||
)
|
||||
response.raise_for_status()
|
||||
logger.info(f"Vendedor {username} creado con meta diaria de {meta_diaria_tubos} tubos")
|
||||
return response.json()
|
||||
except Exception as e:
|
||||
logger.error(f"Error creando vendedor: {str(e)}")
|
||||
return None
|
||||
|
||||
def registrar_venta(self, vendedor_username, monto, cliente, producto=None,
|
||||
descripcion=None, mensaje_id=None, canal=None, imagen_url=None):
|
||||
"""Registra una nueva venta con URL de imagen opcional"""
|
||||
try:
|
||||
payload = {
|
||||
'vendedor_username': vendedor_username,
|
||||
'monto': float(monto),
|
||||
'cliente': cliente,
|
||||
'fecha_venta': datetime.now(TZ_MEXICO).isoformat(),
|
||||
'estado': 'confirmada',
|
||||
'mensaje_id': mensaje_id,
|
||||
'canal': canal
|
||||
}
|
||||
|
||||
if producto:
|
||||
payload['producto'] = producto
|
||||
if descripcion:
|
||||
payload['descripcion'] = descripcion
|
||||
|
||||
if imagen_url:
|
||||
filename = imagen_url.split('/')[-1].split('?')[0] if '/' in imagen_url else 'ticket.jpg'
|
||||
payload['imagen_ticket'] = [
|
||||
{
|
||||
"url": imagen_url,
|
||||
"title": filename,
|
||||
"mimetype": "image/jpeg"
|
||||
}
|
||||
]
|
||||
logger.info(f"Guardando imagen con URL: {imagen_url[:50]}...")
|
||||
|
||||
logger.info(f"Registrando venta - Monto: ${monto}, Cliente: {cliente}")
|
||||
|
||||
response = requests.post(
|
||||
f"{self.url}/api/v2/tables/{self.table_ventas}/records",
|
||||
headers=self.headers,
|
||||
json=payload,
|
||||
timeout=30
|
||||
)
|
||||
response.raise_for_status()
|
||||
venta = response.json()
|
||||
logger.info(f"Venta registrada: ${monto} - {cliente} - vendedor: {vendedor_username}")
|
||||
|
||||
# NUEVO: No actualizar meta mensual, ahora se calcula al consultar
|
||||
|
||||
return venta
|
||||
except Exception as e:
|
||||
logger.error(f"Error registrando venta: {str(e)}", exc_info=True)
|
||||
if 'response' in locals():
|
||||
logger.error(f"Response: {response.text}")
|
||||
return None
|
||||
|
||||
def get_ventas_dia(self, vendedor_username=None, fecha=None):
|
||||
"""Obtiene ventas de un día específico (convierte UTC a hora México)"""
|
||||
try:
|
||||
if fecha is None:
|
||||
fecha = datetime.now(TZ_MEXICO).strftime('%Y-%m-%d')
|
||||
elif isinstance(fecha, date):
|
||||
fecha = fecha.strftime('%Y-%m-%d')
|
||||
|
||||
# Obtener todas las ventas y filtrar
|
||||
response = requests.get(
|
||||
f"{self.url}/api/v2/tables/{self.table_ventas}/records",
|
||||
headers=self.headers,
|
||||
params={'limit': 1000},
|
||||
timeout=10
|
||||
)
|
||||
response.raise_for_status()
|
||||
todas_ventas = response.json().get('list', [])
|
||||
|
||||
# Filtrar por día (convertir UTC a México)
|
||||
ventas_filtradas = []
|
||||
for venta in todas_ventas:
|
||||
fecha_venta_str = venta.get('fecha_venta', '')
|
||||
vendedor = venta.get('vendedor_username', '')
|
||||
|
||||
# Convertir fecha UTC a México
|
||||
try:
|
||||
# Parsear fecha UTC de NocoDB
|
||||
if '+' in fecha_venta_str:
|
||||
fecha_venta_utc = datetime.fromisoformat(fecha_venta_str.replace('+00:00', '+0000'))
|
||||
else:
|
||||
fecha_venta_utc = datetime.fromisoformat(fecha_venta_str)
|
||||
|
||||
# Convertir a hora de México
|
||||
fecha_venta_mexico = fecha_venta_utc.astimezone(TZ_MEXICO)
|
||||
fecha_venta_local = fecha_venta_mexico.strftime('%Y-%m-%d')
|
||||
except:
|
||||
fecha_venta_local = fecha_venta_str[:10] if fecha_venta_str else ''
|
||||
|
||||
fecha_match = fecha_venta_local == fecha
|
||||
|
||||
if vendedor_username:
|
||||
vendedor_match = vendedor == vendedor_username
|
||||
else:
|
||||
vendedor_match = True
|
||||
|
||||
if fecha_match and vendedor_match:
|
||||
ventas_filtradas.append(venta)
|
||||
|
||||
logger.info(f"Ventas del día {fecha} para {vendedor_username or 'todos'}: {len(ventas_filtradas)}")
|
||||
return ventas_filtradas
|
||||
except Exception as e:
|
||||
logger.error(f"Error obteniendo ventas del día: {str(e)}")
|
||||
return []
|
||||
|
||||
def get_ventas_mes(self, vendedor_username=None, mes=None):
|
||||
"""Obtiene ventas del mes actual o especificado"""
|
||||
try:
|
||||
if mes is None:
|
||||
mes = datetime.now(TZ_MEXICO).strftime('%Y-%m')
|
||||
|
||||
response = requests.get(
|
||||
f"{self.url}/api/v2/tables/{self.table_ventas}/records",
|
||||
headers=self.headers,
|
||||
params={'limit': 1000},
|
||||
timeout=10
|
||||
)
|
||||
response.raise_for_status()
|
||||
todas_ventas = response.json().get('list', [])
|
||||
|
||||
ventas_filtradas = []
|
||||
for venta in todas_ventas:
|
||||
fecha_venta = venta.get('fecha_venta', '')
|
||||
vendedor = venta.get('vendedor_username', '')
|
||||
|
||||
fecha_match = fecha_venta.startswith(mes)
|
||||
|
||||
if vendedor_username:
|
||||
vendedor_match = vendedor == vendedor_username
|
||||
else:
|
||||
vendedor_match = True
|
||||
|
||||
if fecha_match and vendedor_match:
|
||||
ventas_filtradas.append(venta)
|
||||
|
||||
logger.info(f"Ventas del mes {mes}: {len(ventas_filtradas)}")
|
||||
return ventas_filtradas
|
||||
except Exception as e:
|
||||
logger.error(f"Error obteniendo ventas del mes: {str(e)}")
|
||||
return []
|
||||
|
||||
def contar_tubos_vendidos_dia(self, vendedor_username, fecha=None):
|
||||
"""
|
||||
Cuenta los tubos de tinte vendidos en un día específico
|
||||
Busca en la tabla de detalle de ventas usando el campo numérico venta_id_num
|
||||
"""
|
||||
try:
|
||||
if fecha is None:
|
||||
fecha = datetime.now(TZ_MEXICO).strftime('%Y-%m-%d')
|
||||
elif isinstance(fecha, date):
|
||||
fecha = fecha.strftime('%Y-%m-%d')
|
||||
|
||||
logger.info(f"Contando tubos para {vendedor_username} del día {fecha}")
|
||||
|
||||
# Obtener ventas del día
|
||||
ventas_dia = self.get_ventas_dia(vendedor_username, fecha)
|
||||
|
||||
if not ventas_dia:
|
||||
logger.info(f"No hay ventas para {vendedor_username} el {fecha}")
|
||||
return 0
|
||||
|
||||
# Obtener IDs de las ventas
|
||||
venta_ids = [v.get('Id') for v in ventas_dia if v.get('Id')]
|
||||
logger.info(f"IDs de ventas a buscar: {venta_ids}")
|
||||
|
||||
if not venta_ids:
|
||||
return 0
|
||||
|
||||
# Obtener todos los detalles de productos
|
||||
response = requests.get(
|
||||
f"{self.url}/api/v2/tables/{self.table_detalle}/records",
|
||||
headers=self.headers,
|
||||
params={'limit': 1000},
|
||||
timeout=10
|
||||
)
|
||||
response.raise_for_status()
|
||||
todos_detalles = response.json().get('list', [])
|
||||
|
||||
logger.info(f"Total detalles en tabla: {len(todos_detalles)}")
|
||||
|
||||
# Contar tubos de tinte (productos que sean tintes)
|
||||
tubos_vendidos = 0
|
||||
detalles_encontrados = 0
|
||||
|
||||
for detalle in todos_detalles:
|
||||
# Usar el campo numérico venta_id_num para verificar la relación
|
||||
venta_id_num = detalle.get('venta_id_num')
|
||||
|
||||
if venta_id_num is None:
|
||||
continue
|
||||
|
||||
# Verificar si este detalle pertenece a alguna de las ventas del día
|
||||
if int(venta_id_num) in venta_ids:
|
||||
detalles_encontrados += 1
|
||||
|
||||
# Detectar si es un tubo de tinte
|
||||
producto = str(detalle.get('producto', '')).lower()
|
||||
marca = str(detalle.get('marca', '')).lower()
|
||||
nombre_completo = f"{marca} {producto}".lower()
|
||||
|
||||
logger.info(f" Analizando: {marca} {producto} (venta_id_num={venta_id_num})")
|
||||
|
||||
# Si contiene "tinte" o "cromatique" o es registro manual, contar
|
||||
if 'tinte' in nombre_completo or 'cromatique' in nombre_completo or 'manual' in marca:
|
||||
cantidad = int(detalle.get('cantidad', 0))
|
||||
tubos_vendidos += cantidad
|
||||
logger.info(f" ✓ Tubo detectado: {cantidad}x {marca} {producto}")
|
||||
|
||||
logger.info(f"Detalles encontrados: {detalles_encontrados}")
|
||||
logger.info(f"Total tubos vendidos por {vendedor_username} el {fecha}: {tubos_vendidos}")
|
||||
return tubos_vendidos
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error contando tubos: {str(e)}", exc_info=True)
|
||||
return 0
|
||||
|
||||
def calcular_comision_dia(self, vendedor_username, fecha=None):
|
||||
"""
|
||||
Calcula la comisión del día para un vendedor
|
||||
Fórmula: $10 por cada tubo vendido después del 3ro
|
||||
"""
|
||||
try:
|
||||
tubos_vendidos = self.contar_tubos_vendidos_dia(vendedor_username, fecha)
|
||||
|
||||
if tubos_vendidos <= self.META_DIARIA_TUBOS:
|
||||
comision = 0.0
|
||||
tubos_comisionables = 0
|
||||
else:
|
||||
tubos_comisionables = tubos_vendidos - self.META_DIARIA_TUBOS
|
||||
comision = tubos_comisionables * self.COMISION_POR_TUBO
|
||||
|
||||
logger.info(
|
||||
f"Comisión {vendedor_username}: "
|
||||
f"{tubos_vendidos} tubos vendidos, "
|
||||
f"{tubos_comisionables} comisionables = ${comision:.2f}"
|
||||
)
|
||||
|
||||
return {
|
||||
'tubos_vendidos': tubos_vendidos,
|
||||
'tubos_comisionables': tubos_comisionables,
|
||||
'comision': comision,
|
||||
'meta_diaria': self.META_DIARIA_TUBOS,
|
||||
'comision_por_tubo': self.COMISION_POR_TUBO
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error calculando comisión: {str(e)}")
|
||||
return {
|
||||
'tubos_vendidos': 0,
|
||||
'tubos_comisionables': 0,
|
||||
'comision': 0.0,
|
||||
'meta_diaria': self.META_DIARIA_TUBOS,
|
||||
'comision_por_tubo': self.COMISION_POR_TUBO
|
||||
}
|
||||
|
||||
def get_estadisticas_vendedor_dia(self, vendedor_username, fecha=None):
|
||||
"""
|
||||
Obtiene estadísticas completas del vendedor para un día
|
||||
"""
|
||||
try:
|
||||
if fecha is None:
|
||||
fecha = datetime.now(TZ_MEXICO).strftime('%Y-%m-%d')
|
||||
|
||||
# Contar tubos y calcular comisión
|
||||
resultado = self.calcular_comision_dia(vendedor_username, fecha)
|
||||
|
||||
# Obtener monto total vendido
|
||||
ventas = self.get_ventas_dia(vendedor_username, fecha)
|
||||
monto_total = sum(float(v.get('monto', 0)) for v in ventas)
|
||||
|
||||
estadisticas = {
|
||||
**resultado,
|
||||
'monto_total_dia': monto_total,
|
||||
'cantidad_ventas': len(ventas),
|
||||
'fecha': fecha,
|
||||
'vendedor': vendedor_username
|
||||
}
|
||||
|
||||
return estadisticas
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error obteniendo estadísticas: {str(e)}")
|
||||
return None
|
||||
|
||||
def get_estadisticas_vendedor_mes(self, vendedor_username, mes=None):
|
||||
"""
|
||||
Obtiene estadísticas del mes completo
|
||||
Calcula comisiones acumuladas día por día
|
||||
"""
|
||||
try:
|
||||
if mes is None:
|
||||
mes = datetime.now(TZ_MEXICO).strftime('%Y-%m')
|
||||
|
||||
# Obtener ventas del mes
|
||||
ventas_mes = self.get_ventas_mes(vendedor_username, mes)
|
||||
|
||||
# Agrupar por día (convertir UTC a México)
|
||||
dias = {}
|
||||
for venta in ventas_mes:
|
||||
fecha_venta = venta.get('fecha_venta', '')
|
||||
if fecha_venta:
|
||||
# Convertir fecha UTC a México
|
||||
try:
|
||||
fecha_utc = datetime.fromisoformat(fecha_venta.replace('+00:00', '+00:00').replace('Z', '+00:00'))
|
||||
if fecha_utc.tzinfo is None:
|
||||
fecha_utc = fecha_utc.replace(tzinfo=timezone.utc)
|
||||
fecha_mexico = fecha_utc.astimezone(TZ_MEXICO)
|
||||
dia = fecha_mexico.strftime('%Y-%m-%d')
|
||||
except:
|
||||
dia = fecha_venta[:10] # Fallback
|
||||
|
||||
if dia not in dias:
|
||||
dias[dia] = []
|
||||
dias[dia].append(venta)
|
||||
|
||||
# Calcular comisiones día por día
|
||||
comision_total_mes = 0.0
|
||||
tubos_totales_mes = 0
|
||||
dias_meta_cumplida = 0
|
||||
|
||||
for dia in sorted(dias.keys()):
|
||||
stats_dia = self.get_estadisticas_vendedor_dia(vendedor_username, dia)
|
||||
if stats_dia:
|
||||
comision_total_mes += stats_dia['comision']
|
||||
tubos_totales_mes += stats_dia['tubos_vendidos']
|
||||
if stats_dia['tubos_vendidos'] >= self.META_DIARIA_TUBOS:
|
||||
dias_meta_cumplida += 1
|
||||
|
||||
monto_total_mes = sum(float(v.get('monto', 0)) for v in ventas_mes)
|
||||
|
||||
return {
|
||||
'mes': mes,
|
||||
'vendedor': vendedor_username,
|
||||
'tubos_totales': tubos_totales_mes,
|
||||
'comision_total': comision_total_mes,
|
||||
'monto_total': monto_total_mes,
|
||||
'cantidad_ventas': len(ventas_mes),
|
||||
'dias_activos': len(dias),
|
||||
'dias_meta_cumplida': dias_meta_cumplida,
|
||||
'promedio_tubos_dia': tubos_totales_mes / len(dias) if dias else 0,
|
||||
'meta_diaria': self.META_DIARIA_TUBOS
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error obteniendo estadísticas del mes: {str(e)}", exc_info=True)
|
||||
return None
|
||||
|
||||
def get_ranking_vendedores(self, mes=None):
|
||||
"""Obtiene ranking de vendedores por tubos vendidos en el mes"""
|
||||
try:
|
||||
if mes is None:
|
||||
mes = datetime.now(TZ_MEXICO).strftime('%Y-%m')
|
||||
|
||||
# Obtener todos los vendedores
|
||||
response = requests.get(
|
||||
f"{self.url}/api/v2/tables/{self.table_vendedores}/records",
|
||||
headers=self.headers,
|
||||
params={'limit': 100},
|
||||
timeout=10
|
||||
)
|
||||
response.raise_for_status()
|
||||
vendedores = response.json().get('list', [])
|
||||
|
||||
# Calcular estadísticas para cada vendedor
|
||||
ranking = []
|
||||
for vendedor in vendedores:
|
||||
if not vendedor.get('activo', True):
|
||||
continue
|
||||
|
||||
username = vendedor.get('username')
|
||||
if not username:
|
||||
continue
|
||||
|
||||
stats = self.get_estadisticas_vendedor_mes(username, mes)
|
||||
if stats:
|
||||
ranking.append({
|
||||
'vendedor_username': username,
|
||||
'nombre_completo': vendedor.get('nombre_completo', username),
|
||||
**stats
|
||||
})
|
||||
|
||||
# Ordenar por tubos vendidos (descendente)
|
||||
ranking_ordenado = sorted(
|
||||
ranking,
|
||||
key=lambda x: x.get('tubos_totales', 0),
|
||||
reverse=True
|
||||
)
|
||||
|
||||
return ranking_ordenado
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error obteniendo ranking: {str(e)}")
|
||||
return []
|
||||
|
||||
def guardar_productos_venta(self, venta_id, productos):
|
||||
"""Guarda el detalle de productos de una venta usando campo numérico venta_id_num"""
|
||||
try:
|
||||
if not self.table_detalle:
|
||||
logger.warning("No se configuró NOCODB_TABLE_VENTAS_DETALLE")
|
||||
return None
|
||||
|
||||
productos_guardados = []
|
||||
|
||||
for producto in productos:
|
||||
# Crear registro del producto con venta_id_num (campo numérico simple)
|
||||
payload = {
|
||||
'producto': producto.get('producto', ''),
|
||||
'marca': producto.get('marca', 'Sin marca'),
|
||||
'cantidad': producto.get('cantidad', 1),
|
||||
'precio_unitario': float(producto.get('precio_unitario', 0)),
|
||||
'importe': float(producto.get('importe', 0)),
|
||||
'detectado_ocr': True,
|
||||
'venta_id_num': int(venta_id) # Campo numérico simple en lugar de link
|
||||
}
|
||||
|
||||
logger.info(f"Guardando producto con venta_id_num={venta_id}: {producto.get('marca')} {producto.get('producto')}")
|
||||
|
||||
response = requests.post(
|
||||
f"{self.url}/api/v2/tables/{self.table_detalle}/records",
|
||||
headers=self.headers,
|
||||
json=payload,
|
||||
timeout=10
|
||||
)
|
||||
response.raise_for_status()
|
||||
detalle = response.json()
|
||||
detalle_id = detalle.get('Id')
|
||||
|
||||
logger.info(f"Producto guardado ID={detalle_id}: {producto.get('marca')} {producto.get('producto')} -> venta_id_num={venta_id}")
|
||||
productos_guardados.append(detalle)
|
||||
|
||||
logger.info(f"Total productos guardados: {len(productos_guardados)} para venta {venta_id}")
|
||||
return productos_guardados
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error guardando productos: {str(e)}", exc_info=True)
|
||||
return None
|
||||
|
||||
def actualizar_meta_vendedor(self, vendedor_username):
|
||||
"""Actualiza o crea el registro de metas del vendedor para el mes actual"""
|
||||
try:
|
||||
if not self.table_metas:
|
||||
logger.warning("No se configuró NOCODB_TABLE_METAS")
|
||||
return None
|
||||
|
||||
# Formato para búsqueda: 2026-01 (año-mes)
|
||||
mes_busqueda = datetime.now(TZ_MEXICO).strftime('%Y-%m')
|
||||
# Formato para guardar en BD: 2026-01-01 (campo Date requiere YYYY-MM-DD)
|
||||
mes_fecha = datetime.now(TZ_MEXICO).strftime('%Y-%m-01')
|
||||
|
||||
# Obtener estadísticas del mes (ya convierte fechas UTC a México)
|
||||
stats = self.get_estadisticas_vendedor_mes(vendedor_username, mes_busqueda)
|
||||
if not stats:
|
||||
logger.warning(f"No se pudieron obtener estadísticas para {vendedor_username}")
|
||||
return None
|
||||
|
||||
tubos_vendidos = stats.get('tubos_totales', 0)
|
||||
comision = stats.get('comision_total', 0)
|
||||
dias_activos = stats.get('dias_activos', 0)
|
||||
dias_cumplida = stats.get('dias_meta_cumplida', 0)
|
||||
|
||||
logger.info(f"Stats para metas: tubos={tubos_vendidos}, comision={comision}, dias={dias_activos}")
|
||||
|
||||
# Buscar registro existente del mes
|
||||
response = requests.get(
|
||||
f"{self.url}/api/v2/tables/{self.table_metas}/records",
|
||||
headers=self.headers,
|
||||
params={'limit': 1000},
|
||||
timeout=10
|
||||
)
|
||||
response.raise_for_status()
|
||||
registros = response.json().get('list', [])
|
||||
|
||||
registro_existente = None
|
||||
for reg in registros:
|
||||
if reg.get('vendedor_username') == vendedor_username:
|
||||
fecha_reg = str(reg.get('mes', ''))[:7]
|
||||
if fecha_reg == mes_busqueda:
|
||||
registro_existente = reg
|
||||
break
|
||||
|
||||
payload = {
|
||||
'vendedor_username': vendedor_username,
|
||||
'mes': mes_fecha,
|
||||
'tubos_vendidos': tubos_vendidos,
|
||||
'comision_ganada': comision,
|
||||
'dias_activos': dias_activos,
|
||||
'dias_meta_cumplida': dias_cumplida
|
||||
}
|
||||
|
||||
if registro_existente:
|
||||
# Actualizar registro existente
|
||||
response = requests.patch(
|
||||
f"{self.url}/api/v2/tables/{self.table_metas}/records",
|
||||
headers=self.headers,
|
||||
json=[{"Id": registro_existente['Id'], **payload}],
|
||||
timeout=10
|
||||
)
|
||||
else:
|
||||
# Crear nuevo registro
|
||||
response = requests.post(
|
||||
f"{self.url}/api/v2/tables/{self.table_metas}/records",
|
||||
headers=self.headers,
|
||||
json=payload,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
response.raise_for_status()
|
||||
logger.info(f"Meta actualizada para {vendedor_username}: {tubos_vendidos} tubos, ${comision} comisión")
|
||||
return response.json()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error actualizando meta: {str(e)}", exc_info=True)
|
||||
return None
|
||||
|
||||
def get_meta_vendedor(self, vendedor_username, mes=None):
|
||||
"""Obtiene las estadísticas del vendedor para el mes"""
|
||||
return self.get_estadisticas_vendedor_mes(vendedor_username, mes)
|
||||
Reference in New Issue
Block a user