Primer commit
This commit is contained in:
@@ -0,0 +1,217 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Fase 2 — MARCA → SUCURSAL V2: match directo por id_contacto_sucursal.
|
||||
|
||||
Insertar entre `Datos API Cuenta objetivo - SUCURSAL` y
|
||||
`Buscar Contacto Objetivo - SUCURSAL(mail)`:
|
||||
1) Code `Extraer id_contacto_sucursal de Marca` — saca CF del contacto Marca.
|
||||
2) IF `¿cfValue presente?` — si vacío salta directo a cascada email.
|
||||
3) HTTP GET `Match directo por id_contacto_sucursal` — GET /contacts/{cfValue}
|
||||
con token sucursal. onError: continueRegularOutput.
|
||||
4) IF `¿GET de contacto sucursal OK?` — true→Conseguir Custom...SUCURSAL (UPDATE),
|
||||
false→cascada email.
|
||||
|
||||
Uso:
|
||||
python n8n/_apply_phase2.py # dry-run
|
||||
python n8n/_apply_phase2.py --apply [--activate]
|
||||
"""
|
||||
import argparse
|
||||
import copy
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import uuid
|
||||
|
||||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
sys.path.insert(0, ROOT)
|
||||
sys.path.insert(0, os.path.join(ROOT, "scripts"))
|
||||
from n8n_workflow_lib import load_credentials, N8NClient # noqa: E402
|
||||
|
||||
WID = "4UMRwxJdHFfOGHBp"
|
||||
CF_ID_BRAND = "E6lI9ykWhqpj7Pmi7Qd3"
|
||||
|
||||
CODE_EXTRACT_NAME = "Extraer id_contacto_sucursal de Marca"
|
||||
IF_CF_PRESENT_NAME = "¿cfValue presente?"
|
||||
HTTP_GET_NAME = "Match directo por id_contacto_sucursal"
|
||||
IF_GET_OK_NAME = "¿GET de contacto sucursal OK?"
|
||||
|
||||
|
||||
def code_extract_node():
|
||||
return {
|
||||
"parameters": {
|
||||
"jsCode": (
|
||||
"var c = $('Obtener Contacto Cuenta Origen - SUCURSAL').first().json.contact || {};\n"
|
||||
"var cfs = c.customFields || [];\n"
|
||||
"var v = null;\n"
|
||||
"for (var i = 0; i < cfs.length; i++) {\n"
|
||||
" if (cfs[i].id === '" + CF_ID_BRAND + "') { v = cfs[i].value; break; }\n"
|
||||
"}\n"
|
||||
"return [{ json: { cfValue: v || null } }];"
|
||||
)
|
||||
},
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [1376, 416],
|
||||
"id": str(uuid.uuid4()),
|
||||
"name": CODE_EXTRACT_NAME,
|
||||
"notes": "FASE 2. Extrae el valor del CF id_contacto_sucursal del contacto Marca origen.",
|
||||
}
|
||||
|
||||
|
||||
def if_cf_present_node():
|
||||
return {
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": True,
|
||||
"leftValue": "",
|
||||
"typeValidation": "loose",
|
||||
"version": 3,
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"id": str(uuid.uuid4()),
|
||||
"leftValue": "={{ $json.cfValue }}",
|
||||
"rightValue": "",
|
||||
"operator": {"type": "string", "operation": "notEmpty", "singleValue": True},
|
||||
}
|
||||
],
|
||||
"combinator": "and",
|
||||
},
|
||||
"options": {},
|
||||
},
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2,
|
||||
"position": [1568, 416],
|
||||
"id": str(uuid.uuid4()),
|
||||
"name": IF_CF_PRESENT_NAME,
|
||||
"notes": "true→HTTP GET, false→cascada email",
|
||||
}
|
||||
|
||||
|
||||
def http_get_node():
|
||||
return {
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "=https://services.leadconnectorhq.com/contacts/{{ $('" + CODE_EXTRACT_NAME + "').item.json.cfValue }}",
|
||||
"sendHeaders": True,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{"name": "Version", "value": "2021-07-28"},
|
||||
{"name": "Authorization",
|
||||
"value": "=Bearer {{ $('Datos API Cuenta objetivo - SUCURSAL').item.json['Token/API'] }}"},
|
||||
]
|
||||
},
|
||||
"options": {"redirect": {"redirect": {}}},
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [1760, 416],
|
||||
"id": str(uuid.uuid4()),
|
||||
"name": HTTP_GET_NAME,
|
||||
"onError": "continueRegularOutput",
|
||||
"alwaysOutputData": True,
|
||||
"notes": "GET directo a sucursal por id (el valor del CF). Si 404, IF siguiente cae a cascada.",
|
||||
}
|
||||
|
||||
|
||||
def if_get_ok_node():
|
||||
return {
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": True,
|
||||
"leftValue": "",
|
||||
"typeValidation": "loose",
|
||||
"version": 3,
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"id": str(uuid.uuid4()),
|
||||
"leftValue": "={{ $json.contact && $json.contact.id ? 'ok' : '' }}",
|
||||
"rightValue": "",
|
||||
"operator": {"type": "string", "operation": "notEmpty", "singleValue": True},
|
||||
}
|
||||
],
|
||||
"combinator": "and",
|
||||
},
|
||||
"options": {},
|
||||
},
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2,
|
||||
"position": [1952, 416],
|
||||
"id": str(uuid.uuid4()),
|
||||
"name": IF_GET_OK_NAME,
|
||||
"notes": "true→UPDATE (Conseguir Custom Cuenta objetivo - SUCURSAL), false→cascada email",
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--apply", action="store_true")
|
||||
parser.add_argument("--activate", action="store_true")
|
||||
args = parser.parse_args()
|
||||
if hasattr(sys.stdout, "reconfigure"):
|
||||
sys.stdout.reconfigure(encoding="utf-8")
|
||||
|
||||
client = N8NClient(*load_credentials())
|
||||
wf, backup = client.backup_workflow(WID, label="fase2_pre")
|
||||
print(f"backup: {backup}")
|
||||
prev = wf.get("versionId")
|
||||
print(f"versionId pre: {prev} active: {wf.get('active')} nodes: {len(wf['nodes'])}")
|
||||
|
||||
wfm = copy.deepcopy(wf)
|
||||
for name in (CODE_EXTRACT_NAME, IF_CF_PRESENT_NAME, HTTP_GET_NAME, IF_GET_OK_NAME):
|
||||
try:
|
||||
client.assert_idempotent(wfm, name)
|
||||
except Exception as e:
|
||||
raise SystemExit(f"Aborto: {e}")
|
||||
|
||||
# Insertar nodos
|
||||
code_node = code_extract_node()
|
||||
if_cf = if_cf_present_node()
|
||||
http_get = http_get_node()
|
||||
if_ok = if_get_ok_node()
|
||||
for n in (code_node, if_cf, http_get, if_ok):
|
||||
client.add_node(wfm, n)
|
||||
|
||||
# Conexiones:
|
||||
# Datos API Cuenta objetivo - SUCURSAL → Code Extract
|
||||
# Code Extract → IF cfValue presente
|
||||
# true → HTTP GET
|
||||
# → IF GET OK
|
||||
# true → Conseguir Custom Cuenta objetivo - SUCURSAL
|
||||
# false → Buscar Contacto Objetivo - SUCURSAL(mail)
|
||||
# false → Buscar Contacto Objetivo - SUCURSAL(mail)
|
||||
client.set_connection(wfm, "Datos API Cuenta objetivo - SUCURSAL", CODE_EXTRACT_NAME)
|
||||
client.set_connection(wfm, CODE_EXTRACT_NAME, IF_CF_PRESENT_NAME)
|
||||
client.branch_if(wfm, IF_CF_PRESENT_NAME,
|
||||
true_target=HTTP_GET_NAME,
|
||||
false_target="Buscar Contacto Objetivo - SUCURSAL(mail)")
|
||||
client.set_connection(wfm, HTTP_GET_NAME, IF_GET_OK_NAME)
|
||||
client.branch_if(wfm, IF_GET_OK_NAME,
|
||||
true_target="Conseguir Custom Cuenta objetivo - SUCURSAL",
|
||||
false_target="Buscar Contacto Objetivo - SUCURSAL(mail)")
|
||||
|
||||
print(f"nodes post: {len(wfm['nodes'])} (+{len(wfm['nodes'])-len(wf['nodes'])})")
|
||||
|
||||
if not args.apply:
|
||||
res = client.put_workflow(WID, wfm, dry_run=True)
|
||||
print(f"DRY-RUN. {res['path']}")
|
||||
return
|
||||
|
||||
client.put_workflow(WID, wfm, dry_run=False)
|
||||
wf2 = client.verify_post(WID,
|
||||
expected_node_names=[CODE_EXTRACT_NAME, IF_CF_PRESENT_NAME,
|
||||
HTTP_GET_NAME, IF_GET_OK_NAME],
|
||||
prev_version_id=prev)
|
||||
print(f"versionId nuevo: {wf2.get('versionId')} active: {wf2.get('active')}")
|
||||
if args.activate or not wf2.get("active"):
|
||||
client.activate(WID)
|
||||
print("activado")
|
||||
|
||||
print("Fase 2 aplicada.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user