204 lines
7.3 KiB
Python
204 lines
7.3 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""Fallback de autoenlace para el workflow Sucursal->Marca (x4DqZ5FtSc43tdzB).
|
|
|
|
Inserta entre 'Mapear Custom Fields Cuenta Origen - SUCURSAL' y
|
|
'DATOS CUENTA OBJETIVO' un IF + PUT condicional:
|
|
- Si el CF 'contact.id_contacto_sucursal' ya == contact.id -> bypass directo.
|
|
- Si NO -> PUT autoenlace en la sucursal antes de continuar.
|
|
|
|
Defensivo contra automatizaciones nativas de GHL que crean contactos sin
|
|
disparar nuestro fill. onError=continueRegularOutput para no romper el sync
|
|
si el PUT falla.
|
|
|
|
Modo dry-run por defecto (dumpea n8n/dryrun_*.json sin tocar la API).
|
|
Pasar --apply para PUT real + reactivar.
|
|
"""
|
|
import argparse
|
|
import sys
|
|
import os
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
from scripts.n8n_workflow_lib import load_credentials, N8NClient
|
|
import uuid
|
|
|
|
WID = "x4DqZ5FtSc43tdzB"
|
|
|
|
NAME_IF = "ya esta el CF id_contacto_sucursal"
|
|
NAME_PUT = "Autoenlace CF id_contacto_sucursal - SUCURSAL"
|
|
|
|
SRC = "Mapear Custom Fields Cuenta Origen - SUCURSAL"
|
|
DST = "DATOS CUENTA OBJETIVO"
|
|
|
|
|
|
def build_if_node():
|
|
return {
|
|
"parameters": {
|
|
"conditions": {
|
|
"options": {
|
|
"caseSensitive": True,
|
|
"leftValue": "",
|
|
"typeValidation": "strict",
|
|
"version": 2,
|
|
},
|
|
"conditions": [
|
|
{
|
|
"id": str(uuid.uuid4()),
|
|
"leftValue": "={{ $json['contact.id_contacto_sucursal'] }}",
|
|
"rightValue": "={{ $json.id }}",
|
|
"operator": {
|
|
"type": "string",
|
|
"operation": "equals",
|
|
"name": "filter.operator.equals",
|
|
},
|
|
}
|
|
],
|
|
"combinator": "and",
|
|
},
|
|
"options": {},
|
|
},
|
|
"type": "n8n-nodes-base.if",
|
|
"typeVersion": 2.2,
|
|
"position": [1760, -288],
|
|
"id": str(uuid.uuid4()),
|
|
"name": NAME_IF,
|
|
"notes": (
|
|
"Fallback defensivo. Si el CF 'contact.id_contacto_sucursal' ya == contact.id "
|
|
"(estado normal poblado por fill_contact_id_sucursal.py), pasa directo. "
|
|
"Si esta vacio o distinto (creacion por automatizacion nativa de GHL que se "
|
|
"salto nuestro fill), va a la rama PUT para autoenlazar antes de tocar Marca."
|
|
),
|
|
}
|
|
|
|
|
|
def build_put_node():
|
|
json_body = (
|
|
"={{ JSON.stringify({ customFields: [ { "
|
|
"id: $('Conseguir Custom Cuenta Origen- SUCURSAL').first().json.customFields"
|
|
".find(f => f.fieldKey === 'contact.id_contacto_sucursal')?.id, "
|
|
"key: 'contact.id_contacto_sucursal', "
|
|
"field_value: $('Mapear Custom Fields Cuenta Origen - SUCURSAL').item.json.id"
|
|
" } ] }) }}"
|
|
)
|
|
return {
|
|
"parameters": {
|
|
"method": "PUT",
|
|
"url": (
|
|
"=https://services.leadconnectorhq.com/contacts/"
|
|
"{{ $('Mapear Custom Fields Cuenta Origen - SUCURSAL').item.json.id }}"
|
|
),
|
|
"sendHeaders": True,
|
|
"headerParameters": {
|
|
"parameters": [
|
|
{"name": "Accept", "value": "application/json"},
|
|
{"name": "Version", "value": "2021-07-28"},
|
|
{
|
|
"name": "Authorization",
|
|
"value": (
|
|
"=Bearer "
|
|
"{{ $('Obtener Info de cuenta origen - SUCURSAL').item.json.API_token }}"
|
|
),
|
|
},
|
|
{"name": "Content-Type", "value": "application/json"},
|
|
]
|
|
},
|
|
"sendBody": True,
|
|
"specifyBody": "json",
|
|
"jsonBody": json_body,
|
|
"options": {},
|
|
},
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.2,
|
|
"position": [1936, -192],
|
|
"id": str(uuid.uuid4()),
|
|
"name": NAME_PUT,
|
|
"onError": "continueRegularOutput",
|
|
"notes": (
|
|
"Autoenlace de seguridad: setea CF 'contact.id_contacto_sucursal' = "
|
|
"contact.id propio en la sucursal. Solo se ejecuta si el IF previo "
|
|
"detecto que el CF estaba vacio o distinto. onError=continue para no "
|
|
"romper el sync si el PUT falla."
|
|
),
|
|
}
|
|
|
|
|
|
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)
|
|
print(f" versionId previo: {wf.get('versionId')}")
|
|
print(f" active : {wf.get('active')}")
|
|
print(f" total nodos : {len(wf.get('nodes') or [])}")
|
|
|
|
existing = {n["name"] for n in wf.get("nodes") or []}
|
|
if NAME_IF in existing or NAME_PUT in existing:
|
|
print(f"\nERROR: los nodos ya existen ({NAME_IF!r} o {NAME_PUT!r}). Abortando.")
|
|
sys.exit(2)
|
|
if SRC not in existing:
|
|
print(f"\nERROR: no encontre el nodo source {SRC!r}. Abortando.")
|
|
sys.exit(2)
|
|
if DST not in existing:
|
|
print(f"\nERROR: no encontre el nodo destino {DST!r}. Abortando.")
|
|
sys.exit(2)
|
|
|
|
print(f"\n[2/6] Backup fresco")
|
|
_, bpath = client.backup_workflow(WID, label="pre_fallback_autoenlace")
|
|
print(f" backup -> {bpath}")
|
|
|
|
print(f"\n[3/6] Inyectar nodos IF + PUT")
|
|
if_node = build_if_node()
|
|
put_node = build_put_node()
|
|
client.add_node(wf, if_node)
|
|
client.add_node(wf, put_node)
|
|
print(f" IF : {NAME_IF}")
|
|
print(f" PUT: {NAME_PUT}")
|
|
|
|
print(f"\n[4/6] Reconectar grafo")
|
|
# Reemplaza SRC -> DST por SRC -> IF
|
|
client.insert_between(wf, SRC, NAME_IF, DST)
|
|
# IF.output(0)=true (CF ya ok) -> DST; IF.output(1)=false (CF vacio/distinto) -> PUT -> DST
|
|
client.branch_if(wf, NAME_IF, true_target=DST, false_target=NAME_PUT)
|
|
# PUT -> DST (convergencia)
|
|
conns = wf["connections"]
|
|
conns[NAME_PUT] = {"main": [[{"node": DST, "type": "main", "index": 0}]]}
|
|
print(f" {SRC} -> {NAME_IF}")
|
|
print(f" {NAME_IF} [true] -> {DST}")
|
|
print(f" {NAME_IF} [false] -> {NAME_PUT}")
|
|
print(f" {NAME_PUT} -> {DST}")
|
|
|
|
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,
|
|
expected_node_names=[NAME_IF, NAME_PUT],
|
|
prev_version_id=wf.get("versionId"),
|
|
)
|
|
print(f" versionId nuevo: {new_wf.get('versionId')}")
|
|
if not new_wf.get("active"):
|
|
print(" workflow quedo inactivo tras PUT; reactivando...")
|
|
client.activate(WID)
|
|
new_wf = client.get_workflow(WID)
|
|
print(f" active final : {new_wf.get('active')}")
|
|
print(f" total nodos : {len(new_wf.get('nodes') or [])}")
|
|
print("\nOK.")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|