# -*- coding: utf-8 -*- """Tercer fix del path cascada del workflow Marca->Sucursal V2 (4UMRwxJdHFfOGHBp). Tras arreglar el token y el locationId del 'Buscar Contacto Objetivo - SUCURSAL(mail)', el endpoint responde correctamente con {contacts:[], total:0} cuando no encuentra por email. Pero el IF siguiente `Si hay mas de un resultado` no maneja el caso 0 resultados: condiciones actuales: contacts[1] exists AND total > 0 (AND) true -> Retry telefono false -> Obtener Contacto Cuenta objetivo - SUCURSAL (BUG: con id undefined) Cuando total=0, ambas condiciones son false, va a [false] -> `Obtener Contacto` con `$json.contacts[0].id` = undefined, lo que dispara 403 y termina en CREATE fallido (si hay duplicate phone) o CREATE exitoso (si es contacto nuevo). Fix: alinear con la logica del IF `Si hay mas de un resultado1` (el siguiente en la cadena), que ya tiene la condicion correcta: condiciones correctas: contacts[1] exists OR total equals 0 (OR) true (2+ resultados o 0 resultados) -> Retry telefono false (exactamente 1 resultado) -> Obtener Contacto Cambio neto: 2 expressions (operator y combinator) en un solo IF. Total nodos sin cambios. """ import argparse import os import sys sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from scripts.n8n_workflow_lib import load_credentials, N8NClient WID = "4UMRwxJdHFfOGHBp" TARGET_NODE = "Si hay más de un resultado" REFERENCE_NODE = "Si hay más de un resultado1" # ya tiene la logica correcta def parse_args(): ap = argparse.ArgumentParser() ap.add_argument("--apply", action="store_true", help="Aplicar PUT real (default: dry-run a n8n/dryrun_*.json)") return ap.parse_args() def main(): args = parse_args() api_key, base_url = load_credentials() client = N8NClient(api_key, base_url) print(f"[1/6] GET workflow {WID}") wf = client.get_workflow(WID) prev_version = wf.get("versionId") print(f" versionId previo: {prev_version}") print(f" total nodos : {len(wf.get('nodes') or [])}") target = next((n for n in wf["nodes"] if n.get("name") == TARGET_NODE), None) ref = next((n for n in wf["nodes"] if n.get("name") == REFERENCE_NODE), None) if not target or not ref: print(f"\nERROR: nodos requeridos no existen. Abortando.") sys.exit(2) target_conds = target["parameters"]["conditions"] ref_conds = ref["parameters"]["conditions"] print(f"\n[2/6] Diagnostico") print(f" Target combinator: {target_conds.get('combinator')}") print(f" Ref combinator: {ref_conds.get('combinator')}") for i, (tc, rc) in enumerate(zip(target_conds["conditions"], ref_conds["conditions"])): t_op = tc.get("operator", {}).get("operation") r_op = rc.get("operator", {}).get("operation") print(f" cond[{i}]: target.op={t_op} ref.op={r_op}") # Aplicar misma estructura del ref al target (preservando los ids existentes) if target_conds.get("combinator") == "or": is_total_eq_zero = any( c.get("operator", {}).get("operation") == "equals" and c.get("leftValue", "").endswith("total }}") for c in target_conds["conditions"] ) if is_total_eq_zero: print(f"\n [SKIP] Target ya esta arreglado.") return print(f"\n[3/6] Backup fresco") _, bpath = client.backup_workflow(WID, label="pre_fix_cascada_if_zero_results") print(f" backup -> {bpath}") print(f"\n[4/6] Aplicar cambio en memoria") # Forzar combinator OR target_conds["combinator"] = "or" # Cambiar la segunda condicion (total > 0) a (total equals 0) second = target_conds["conditions"][1] second["operator"]["operation"] = "equals" # rightValue ya era 0 (number), se queda igual print(f" nuevo combinator: {target_conds['combinator']}") print(f" nuevo cond[1].op: {second['operator']['operation']}") print(f"\n[5/6] {'APPLY' if args.apply else 'DRY-RUN'} put_workflow") result = client.put_workflow(WID, wf, dry_run=not args.apply) if not args.apply: print(f" dry-run dump -> {result}") print("\nLISTO. Revisa el JSON. Si todo bien, corre con --apply.") return print(f"\n[6/6] Verificar y reactivar") new_wf = client.verify_post(WID, prev_version_id=prev_version) target_post = next((n for n in new_wf["nodes"] if n.get("name") == TARGET_NODE), None) conds_post = target_post["parameters"]["conditions"] print(f" versionId nuevo : {new_wf.get('versionId')}") print(f" total nodos : {len(new_wf.get('nodes') or [])}") print(f" combinator post : {conds_post.get('combinator')}") print(f" cond[1].op post : {conds_post['conditions'][1]['operator']['operation']}") if not new_wf.get("active"): print(" workflow inactivo; reactivando...") client.activate(WID) new_wf = client.get_workflow(WID) print(f" active final : {new_wf.get('active')}") print("\nOK.") if __name__ == "__main__": main()