- 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>
612 lines
24 KiB
Python
612 lines
24 KiB
Python
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)
|