Primer commit
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
import os
|
||||
import sys
|
||||
import sqlite3
|
||||
import json
|
||||
|
||||
# Ruta a la base de datos (un nivel arriba del script)
|
||||
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
if ROOT_DIR not in sys.path:
|
||||
sys.path.insert(0, ROOT_DIR)
|
||||
|
||||
import sync_engine # noqa: E402
|
||||
|
||||
from paths import DB_PATH
|
||||
_schema_cache = {}
|
||||
_tokens_map = None
|
||||
|
||||
|
||||
def get_location_token(location_id):
|
||||
global _tokens_map
|
||||
if _tokens_map is None:
|
||||
_tokens_map = sync_engine.get_tokens_map()
|
||||
return _tokens_map.get(location_id)
|
||||
|
||||
|
||||
def get_schema_field_id(location_id, object_key, field_name):
|
||||
cache_key = (location_id, object_key)
|
||||
if cache_key not in _schema_cache:
|
||||
token = get_location_token(location_id)
|
||||
if not token:
|
||||
_schema_cache[cache_key] = {}
|
||||
else:
|
||||
_schema_cache[cache_key] = sync_engine.ghl_client.get_object_schema(token, location_id, object_key)
|
||||
return _schema_cache[cache_key].get(field_name)
|
||||
|
||||
|
||||
def extract_custom_field_value(cf_json, field_id):
|
||||
if not field_id or not cf_json:
|
||||
return None
|
||||
try:
|
||||
cfs = json.loads(cf_json)
|
||||
if isinstance(cfs, list):
|
||||
for cf in cfs:
|
||||
if cf.get("id") == field_id or cf.get("fieldId") == field_id:
|
||||
return cf.get("value")
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
def analyze_discrepancies():
|
||||
if not os.path.exists(DB_PATH):
|
||||
print(f"Error: La base de datos local no existe en {DB_PATH}.")
|
||||
print("Por favor, ejecuta la sincronización global primero desde el dashboard.")
|
||||
sys.exit(1)
|
||||
|
||||
conn = sqlite3.connect(DB_PATH)
|
||||
conn.row_factory = sqlite3.Row
|
||||
|
||||
try:
|
||||
# 1. Obtener todas las cuentas y mapear sus IDs y nombres
|
||||
accounts_rows = conn.execute("SELECT location_id, nombre FROM accounts").fetchall()
|
||||
branches = {}
|
||||
for r in accounts_rows:
|
||||
name = r['nombre']
|
||||
# Normalizar nombre de sucursal para comparación (ej. de "85932 - MP - La Viga" a "la viga")
|
||||
normalized_name = name.lower()
|
||||
for token in ["mp", "-", "859", "0001", "qro", "demo", "plaza"]:
|
||||
normalized_name = normalized_name.replace(token, "")
|
||||
normalized_name = " ".join(normalized_name.split())
|
||||
branches[r['location_id']] = {
|
||||
"full_name": name,
|
||||
"clean_name": normalized_name
|
||||
}
|
||||
|
||||
# 2. Consultar todas las oportunidades y sus contactos asociados
|
||||
sql = """
|
||||
SELECT o.id as opp_id, o.name as opp_name, o.location_id as opp_loc_id, o.status as opp_status,
|
||||
c.id as contact_id, c.first_name, c.last_name, c.custom_fields_json, c.location_id as contact_loc_id
|
||||
FROM opportunities o
|
||||
JOIN contacts c ON o.contact_id = c.id AND o.location_id = c.location_id
|
||||
"""
|
||||
opps = conn.execute(sql).fetchall()
|
||||
|
||||
print("=== ANÃLISIS DE DISCREPANCIAS DE SUCURSAL ===")
|
||||
print(f"Total de oportunidades con contactos analizadas: {len(opps)}")
|
||||
print("Buscando inconsistencias entre la sucursal del contacto (campo custom) y la ubicación real de la oportunidad...\n")
|
||||
print("-" * 110)
|
||||
|
||||
discrepancies_count = 0
|
||||
missing_schema_locations = set()
|
||||
|
||||
for row in opps:
|
||||
opp_loc_id = row['opp_loc_id']
|
||||
contact_cf_json = row['custom_fields_json']
|
||||
contact_loc_id = row['contact_loc_id']
|
||||
|
||||
# Obtener nombre de la sucursal donde vive la oportunidad
|
||||
opp_branch_info = branches.get(opp_loc_id, {"full_name": "Desconocida", "clean_name": ""})
|
||||
opp_branch_clean = opp_branch_info["clean_name"]
|
||||
|
||||
# Leer solo el campo personalizado Sucursal resuelto dinamicamente por schema.
|
||||
contact_sucursal_val = None
|
||||
sucursal_field_id = get_schema_field_id(contact_loc_id, "contact", "Sucursal")
|
||||
if not sucursal_field_id:
|
||||
missing_schema_locations.add(contact_loc_id)
|
||||
else:
|
||||
val = str(extract_custom_field_value(contact_cf_json, sucursal_field_id) or "").strip()
|
||||
val_lower = val.lower()
|
||||
for loc_id, b_info in branches.items():
|
||||
b_clean = b_info["clean_name"]
|
||||
if b_clean and b_clean in val_lower:
|
||||
contact_sucursal_val = b_info["full_name"]
|
||||
break
|
||||
|
||||
# Si el contacto tiene un valor de sucursal definido, pero no se parece al de la oportunidad
|
||||
if contact_sucursal_val:
|
||||
# Normalizar la sucursal del contacto para comparar
|
||||
c_suc_clean = contact_sucursal_val.lower()
|
||||
for token in ["mp", "-", "859", "0001", "qro", "demo", "plaza"]:
|
||||
c_suc_clean = c_suc_clean.replace(token, "")
|
||||
c_suc_clean = " ".join(c_suc_clean.split())
|
||||
|
||||
# Si los nombres limpios difieren, hay discrepancia
|
||||
if c_suc_clean != opp_branch_clean:
|
||||
discrepancies_count += 1
|
||||
contact_name = f"{row['first_name'] or ''} {row['last_name'] or ''}".strip()
|
||||
print(f"{discrepancies_count:02d}. Oportunidad: {row['opp_name']}")
|
||||
print(f" Cliente: {contact_name} (ID: {row['contact_id']})")
|
||||
print(f" Ubicación Opp: {opp_branch_info['full_name']} ({opp_loc_id})")
|
||||
print(f" Campo Contact: {contact_sucursal_val}")
|
||||
print(f" Estado Opp: {row['opp_status'].upper()}")
|
||||
print("-" * 110)
|
||||
|
||||
print(f"\nAnálisis finalizado.")
|
||||
if missing_schema_locations:
|
||||
print(f"Locations sin campo Sucursal resuelto por schema: {len(missing_schema_locations)}")
|
||||
if discrepancies_count == 0:
|
||||
print("¡Felicidades! No se detectaron discrepancias de sucursal en la base de datos.")
|
||||
else:
|
||||
print(f"Se encontraron {discrepancies_count} discrepancias de sucursal.")
|
||||
print("Se recomienda correr el script 'fix_sucursal_discrepancies.py' para corregir automáticamente.")
|
||||
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
analyze_discrepancies()
|
||||
Reference in New Issue
Block a user