Primer commit

This commit is contained in:
2026-05-30 14:31:19 -06:00
commit a35d26fac0
277 changed files with 265240 additions and 0 deletions
+123
View File
@@ -0,0 +1,123 @@
# -*- 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()