n8n-nodes-niiko 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -0
- package/dist/credentials/NiikoApi.credentials.js +34 -0
- package/dist/nodes/Niiko/Niiko.node.js +230 -0
- package/dist/nodes/Niiko/niiko.svg +1 -0
- package/index.js +1 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# n8n-nodes-niiko
|
|
2
|
+
|
|
3
|
+
El nodo de [niiko](https://niiko.org) para n8n. **Generado** del mismo plan que los SDK, la CLI y el servidor
|
|
4
|
+
MCP (plan `9a8b80d285ff`): cada acción abierta al mundo es una operación del nodo, y una acción nueva aparece
|
|
5
|
+
al regenerar. Este repositorio no se edita a mano.
|
|
6
|
+
|
|
7
|
+
## Instalar
|
|
8
|
+
|
|
9
|
+
En n8n: *Settings → Community Nodes → Install* → `n8n-nodes-niiko`. Credenciales: una llave `nk_…` creada en
|
|
10
|
+
*Administración → Seguridad → Claves de API*, con los alcances de las acciones que el flujo vaya a ejercer.
|
|
11
|
+
|
|
12
|
+
## Cómo lee la salida
|
|
13
|
+
|
|
14
|
+
Cada ítem produce un objeto con `status`, que es **uno de tres**: `done` (con `output`), `refused` (con
|
|
15
|
+
`reason` y, si lo hay, `detail.message` explicando qué hacer) y `pending_approval` (queda esperando una firma
|
|
16
|
+
humana; el resultado llega por webhook). **Una negativa no es un error del nodo**: es una respuesta con nombre,
|
|
17
|
+
y el flujo decide con un IF. Solo el transporte roto lanza.
|
|
18
|
+
|
|
19
|
+
Cada ítem manda su propia `Idempotency-Key` (viaja en la salida): reintentar el flujo no repite el efecto.
|
|
20
|
+
|
|
21
|
+
## Las operaciones de hoy
|
|
22
|
+
|
|
23
|
+
- **Lead create (miira)** (`miira.lead_create`) — Crea un lead nuevo en el CRM del workspace a partir de sus datos de contacto. Si ya existe uno que encaja, no lo duplica: contesta `ambiguous` con los candidatos.
|
|
24
|
+
- **Call logged (crm)** (`crm.call_logged`) — Anota en la ficha de un cliente, dicho por su nombre, lo que se habló en una llamada; opcionalmente deja creado el seguimiento con su fecha. No lee nada ni llama a nadie.
|
|
25
|
+
- **Owner assigned (crm)** (`crm.owner_assigned`) — Cambia de quién es un cliente, diciendo el nombre del cliente y el nombre (o correo) del miembro del equipo. Si alguno de los dos es ambiguo, se niega con la lista.
|
|
26
|
+
- **Stage moved (crm)** (`crm.stage_moved`) — Mueve el negocio abierto de un cliente a otra etapa del pipeline, diciendo el nombre del cliente y el de la etapa. No crea negocios: sin uno abierto se niega, y con varios se niega con la lista.
|
|
27
|
+
- **Invoice proposed (kiipu)** (`kiipu.invoice_proposed`) — Deja preparada una factura como BORRADOR para un cliente dicho por su nombre, con sus líneas e impuestos. NO la emite, NO la numera y NO cuenta como deuda: una persona la revisa y la emite en Kiipu. No crea el cliente si no existe.
|
|
28
|
+
- **Task created (crm)** (`crm.task_created`) — Crea un recordatorio (tarea con fecha y hora) sobre un cliente dicho por su nombre. No anota una llamada: para eso está crm.call_logged. No crea el cliente si no existe.
|
|
29
|
+
- **Deal created (crm)** (`crm.deal_created`) — Abre un negocio nuevo en el pipeline para un cliente dicho por su nombre, con título, valor opcional en USD, etapa opcional (por nombre; sin ella, la primera) y responsable opcional. No comprueba si ya tiene otros abiertos: devuelve cuántos quedan para que se vea un duplicado. No lo gana ni lo pierde: eso es crm.stage_moved.
|
|
30
|
+
- **Note added (crm)** (`crm.note_added`) — Guarda una nota en la ficha de un cliente dicho por su nombre: algo que hay que saber la próxima vez, sin llamada ni fecha. Para una llamada está crm.call_logged; para un recordatorio con fecha, crm.task_created.
|
|
31
|
+
- **Contact added (crm)** (`crm.contact_added`) — Añade una persona (nombre, y opcionalmente correo, teléfono y cargo) a la ficha de un cliente dicho por su nombre. No la hace contacto principal ni crea el cliente. Si ya había alguien con ese correo o teléfono, lo dice en la respuesta pero no lo impide.
|
|
32
|
+
- **Broadcast quoted (miira)** (`miira.broadcast_quoted`) — Presupuesta mandar el MISMO mensaje de WhatsApp a varios clientes dichos por su nombre (hasta 50). NO envía nada: dice, por cliente, si le llega el texto tal cual (ventana de 24 h abierta, gratis), si hace falta una plantilla aprobada y cuánto cuesta, o por qué no se le puede escribir. Devuelve un presupuesto firmado que vale 15 minutos; para enviar, llama a miira.broadcast_sent con él. Enséñale el presupuesto a la persona antes.
|
|
33
|
+
- **Broadcast sent (miira)** (`miira.broadcast_sent`) — Envía la difusión de WhatsApp presupuestada por miira.broadcast_quoted, exactamente a quienes y como dijo el presupuesto. Si algo cambió (ventana, consentimiento, tarifa) se niega con el presupuesto nuevo para confirmarlo otra vez. Cuesta dinero cuando hay plantillas: no lo llames sin que la persona haya visto el coste.
|
|
34
|
+
- **Draft voided (kiipu)** (`kiipu.draft_voided`) — Anula un BORRADOR de factura (uno creado con kiipu.invoice_proposed y todavía no emitido), por su id o por el nombre del cliente si es su único borrador. No anula facturas emitidas: eso es de una persona en Kiipu.
|
|
35
|
+
- **Payment reported (kiipu)** (`kiipu.payment_reported`) — Deja en la cola de aprobación de Kiipu el aviso de que un cliente (por su nombre) pagó cierto monto de una factura abierta. NO aplica el pago ni toca saldos: una persona lo revisa contra el banco y lo aplica. Si el cliente tiene varias facturas abiertas hay que decir el número.
|
|
36
|
+
|
|
37
|
+
Referencia completa de cada campo: https://developers.niiko.org
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NiikoApi = void 0;
|
|
4
|
+
class NiikoApi {
|
|
5
|
+
name = "niikoApi";
|
|
6
|
+
displayName = "niiko API";
|
|
7
|
+
documentationUrl = "https://developers.niiko.org";
|
|
8
|
+
properties = [
|
|
9
|
+
{
|
|
10
|
+
displayName: "Llave de API",
|
|
11
|
+
name: "apiKey",
|
|
12
|
+
type: "string",
|
|
13
|
+
typeOptions: { password: true },
|
|
14
|
+
default: "",
|
|
15
|
+
description: "Una llave nk_… creada en Administración → Seguridad → Claves de API, con los alcances de las acciones que este flujo va a ejercer.",
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
displayName: "URL",
|
|
19
|
+
name: "baseUrl",
|
|
20
|
+
type: "string",
|
|
21
|
+
default: "https://niiko.org",
|
|
22
|
+
description: "Solo cambia esto si niiko corre en otra dirección.",
|
|
23
|
+
},
|
|
24
|
+
];
|
|
25
|
+
authenticate = {
|
|
26
|
+
type: "generic",
|
|
27
|
+
properties: { headers: { Authorization: "=Bearer {{$credentials.apiKey}}" } },
|
|
28
|
+
};
|
|
29
|
+
/** Solo comprueba que la dirección contesta: la API no tiene lecturas, así que una llave se prueba ejerciendo. */
|
|
30
|
+
test = {
|
|
31
|
+
request: { baseURL: "={{$credentials.baseUrl}}", url: "/api/mcp", method: "GET" },
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
exports.NiikoApi = NiikoApi;
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Niiko = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
const node_crypto_1 = require("node:crypto");
|
|
6
|
+
/** Por operación: la acción que se ejerce, sus campos, y cuáles viajan como JSON. */
|
|
7
|
+
const OPERACIONES = {
|
|
8
|
+
"miiraLeadCreate": { accion: "miira.lead_create", campos: ["submissionId", "source", "name", "email", "phone", "fields", "consent"], json: ["fields", "consent"] },
|
|
9
|
+
"crmCallLogged": { accion: "crm.call_logged", campos: ["client", "summary", "followUp"], json: ["followUp"] },
|
|
10
|
+
"crmOwnerAssigned": { accion: "crm.owner_assigned", campos: ["client", "owner"], json: [] },
|
|
11
|
+
"crmStageMoved": { accion: "crm.stage_moved", campos: ["client", "stage", "lostReason"], json: [] },
|
|
12
|
+
"kiipuInvoiceProposed": { accion: "kiipu.invoice_proposed", campos: ["client", "lines", "dueAt", "series"], json: ["lines"] },
|
|
13
|
+
"crmTaskCreated": { accion: "crm.task_created", campos: ["client", "what", "dueAt"], json: [] },
|
|
14
|
+
"crmDealCreated": { accion: "crm.deal_created", campos: ["client", "title", "valueUsd", "stage", "owner"], json: [] },
|
|
15
|
+
"crmNoteAdded": { accion: "crm.note_added", campos: ["client", "note"], json: [] },
|
|
16
|
+
"crmContactAdded": { accion: "crm.contact_added", campos: ["client", "name", "email", "phone", "role"], json: [] },
|
|
17
|
+
"miiraBroadcastQuoted": { accion: "miira.broadcast_quoted", campos: ["clients", "message", "template", "templateValues"], json: ["clients", "templateValues"] },
|
|
18
|
+
"miiraBroadcastSent": { accion: "miira.broadcast_sent", campos: ["quote"], json: [] },
|
|
19
|
+
"kiipuDraftVoided": { accion: "kiipu.draft_voided", campos: ["invoiceId", "client"], json: [] },
|
|
20
|
+
"kiipuPaymentReported": { accion: "kiipu.payment_reported", campos: ["client", "amountUsd", "invoice"], json: [] },
|
|
21
|
+
};
|
|
22
|
+
class Niiko {
|
|
23
|
+
description = {
|
|
24
|
+
displayName: "niiko",
|
|
25
|
+
name: "niiko",
|
|
26
|
+
icon: "file:niiko.svg",
|
|
27
|
+
group: ["transform"],
|
|
28
|
+
version: 1,
|
|
29
|
+
subtitle: '={{ $parameter["operation"] }}',
|
|
30
|
+
description: "Ejerce una acción de niiko: cada operación es una acción abierta al mundo. Plan 9a8b80d285ff.",
|
|
31
|
+
defaults: { name: "niiko" },
|
|
32
|
+
inputs: ["main"],
|
|
33
|
+
outputs: ["main"],
|
|
34
|
+
credentials: [{ name: "niikoApi", required: true }],
|
|
35
|
+
properties: [
|
|
36
|
+
{
|
|
37
|
+
displayName: "Operation",
|
|
38
|
+
name: "operation",
|
|
39
|
+
type: "options",
|
|
40
|
+
noDataExpression: true,
|
|
41
|
+
options: [
|
|
42
|
+
{
|
|
43
|
+
name: "Lead create (miira)",
|
|
44
|
+
value: "miiraLeadCreate",
|
|
45
|
+
action: "Lead create (miira)",
|
|
46
|
+
description: "Crea un lead nuevo en el CRM del workspace a partir de sus datos de contacto. Si ya existe uno que encaja, no lo duplica: contesta `ambiguous` con los candidatos.",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "Call logged (crm)",
|
|
50
|
+
value: "crmCallLogged",
|
|
51
|
+
action: "Call logged (crm)",
|
|
52
|
+
description: "Anota en la ficha de un cliente, dicho por su nombre, lo que se habló en una llamada; opcionalmente deja creado el seguimiento con su fecha. No lee nada ni llama a nadie.",
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: "Owner assigned (crm)",
|
|
56
|
+
value: "crmOwnerAssigned",
|
|
57
|
+
action: "Owner assigned (crm)",
|
|
58
|
+
description: "Cambia de quién es un cliente, diciendo el nombre del cliente y el nombre (o correo) del miembro del equipo. Si alguno de los dos es ambiguo, se niega con la lista.",
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: "Stage moved (crm)",
|
|
62
|
+
value: "crmStageMoved",
|
|
63
|
+
action: "Stage moved (crm)",
|
|
64
|
+
description: "Mueve el negocio abierto de un cliente a otra etapa del pipeline, diciendo el nombre del cliente y el de la etapa. No crea negocios: sin uno abierto se niega, y con varios se niega con la lista.",
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: "Invoice proposed (kiipu)",
|
|
68
|
+
value: "kiipuInvoiceProposed",
|
|
69
|
+
action: "Invoice proposed (kiipu)",
|
|
70
|
+
description: "Deja preparada una factura como BORRADOR para un cliente dicho por su nombre, con sus líneas e impuestos. NO la emite, NO la numera y NO cuenta como deuda: una persona la revisa y la emite en Kiipu. No crea el cliente si no existe.",
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: "Task created (crm)",
|
|
74
|
+
value: "crmTaskCreated",
|
|
75
|
+
action: "Task created (crm)",
|
|
76
|
+
description: "Crea un recordatorio (tarea con fecha y hora) sobre un cliente dicho por su nombre. No anota una llamada: para eso está crm.call_logged. No crea el cliente si no existe.",
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: "Deal created (crm)",
|
|
80
|
+
value: "crmDealCreated",
|
|
81
|
+
action: "Deal created (crm)",
|
|
82
|
+
description: "Abre un negocio nuevo en el pipeline para un cliente dicho por su nombre, con título, valor opcional en USD, etapa opcional (por nombre; sin ella, la primera) y responsable opcional. No comprueba si ya tiene otros abiertos: devuelve cuántos quedan para que se vea un duplicado. No lo gana ni lo pierde: eso es crm.stage_moved.",
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: "Note added (crm)",
|
|
86
|
+
value: "crmNoteAdded",
|
|
87
|
+
action: "Note added (crm)",
|
|
88
|
+
description: "Guarda una nota en la ficha de un cliente dicho por su nombre: algo que hay que saber la próxima vez, sin llamada ni fecha. Para una llamada está crm.call_logged; para un recordatorio con fecha, crm.task_created.",
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: "Contact added (crm)",
|
|
92
|
+
value: "crmContactAdded",
|
|
93
|
+
action: "Contact added (crm)",
|
|
94
|
+
description: "Añade una persona (nombre, y opcionalmente correo, teléfono y cargo) a la ficha de un cliente dicho por su nombre. No la hace contacto principal ni crea el cliente. Si ya había alguien con ese correo o teléfono, lo dice en la respuesta pero no lo impide.",
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: "Broadcast quoted (miira)",
|
|
98
|
+
value: "miiraBroadcastQuoted",
|
|
99
|
+
action: "Broadcast quoted (miira)",
|
|
100
|
+
description: "Presupuesta mandar el MISMO mensaje de WhatsApp a varios clientes dichos por su nombre (hasta 50). NO envía nada: dice, por cliente, si le llega el texto tal cual (ventana de 24 h abierta, gratis), si hace falta una plantilla aprobada y cuánto cuesta, o por qué no se le puede escribir. Devuelve un presupuesto firmado que vale 15 minutos; para enviar, llama a miira.broadcast_sent con él. Enséñale el presupuesto a la persona antes.",
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: "Broadcast sent (miira)",
|
|
104
|
+
value: "miiraBroadcastSent",
|
|
105
|
+
action: "Broadcast sent (miira)",
|
|
106
|
+
description: "Envía la difusión de WhatsApp presupuestada por miira.broadcast_quoted, exactamente a quienes y como dijo el presupuesto. Si algo cambió (ventana, consentimiento, tarifa) se niega con el presupuesto nuevo para confirmarlo otra vez. Cuesta dinero cuando hay plantillas: no lo llames sin que la persona haya visto el coste.",
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: "Draft voided (kiipu)",
|
|
110
|
+
value: "kiipuDraftVoided",
|
|
111
|
+
action: "Draft voided (kiipu)",
|
|
112
|
+
description: "Anula un BORRADOR de factura (uno creado con kiipu.invoice_proposed y todavía no emitido), por su id o por el nombre del cliente si es su único borrador. No anula facturas emitidas: eso es de una persona en Kiipu.",
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: "Payment reported (kiipu)",
|
|
116
|
+
value: "kiipuPaymentReported",
|
|
117
|
+
action: "Payment reported (kiipu)",
|
|
118
|
+
description: "Deja en la cola de aprobación de Kiipu el aviso de que un cliente (por su nombre) pagó cierto monto de una factura abierta. NO aplica el pago ni toca saldos: una persona lo revisa contra el banco y lo aplica. Si el cliente tiene varias facturas abiertas hay que decir el número.",
|
|
119
|
+
}
|
|
120
|
+
],
|
|
121
|
+
default: "miiraLeadCreate",
|
|
122
|
+
},
|
|
123
|
+
{ displayName: "submissionId", name: "submissionId", required: true, displayOptions: { show: { operation: ["miiraLeadCreate"] } }, type: "string", default: "" },
|
|
124
|
+
{ displayName: "source", name: "source", required: true, displayOptions: { show: { operation: ["miiraLeadCreate"] } }, type: "options", options: [{ name: "web_form", value: "web_form" }, { name: "referral", value: "referral" }, { name: "api", value: "api" }, { name: "import", value: "import" }, { name: "event", value: "event" }, { name: "other", value: "other" }], default: "web_form" },
|
|
125
|
+
{ displayName: "name", name: "name", required: false, displayOptions: { show: { operation: ["miiraLeadCreate"] } }, type: "string", default: "" },
|
|
126
|
+
{ displayName: "email", name: "email", required: false, displayOptions: { show: { operation: ["miiraLeadCreate"] } }, type: "string", default: "" },
|
|
127
|
+
{ displayName: "phone", name: "phone", required: false, displayOptions: { show: { operation: ["miiraLeadCreate"] } }, type: "string", default: "" },
|
|
128
|
+
{ displayName: "fields", name: "fields", required: false, displayOptions: { show: { operation: ["miiraLeadCreate"] } }, type: "json", default: "{}", description: "JSON. Un objeto con la forma que dice la referencia (developers.niiko.org)." },
|
|
129
|
+
{ displayName: "consent", name: "consent", required: false, displayOptions: { show: { operation: ["miiraLeadCreate"] } }, type: "json", default: "{}", description: "JSON. Un objeto con la forma que dice la referencia (developers.niiko.org)." },
|
|
130
|
+
{ displayName: "client", name: "client", required: true, displayOptions: { show: { operation: ["crmCallLogged"] } }, type: "string", default: "" },
|
|
131
|
+
{ displayName: "summary", name: "summary", required: true, displayOptions: { show: { operation: ["crmCallLogged"] } }, type: "string", default: "" },
|
|
132
|
+
{ displayName: "followUp", name: "followUp", required: false, displayOptions: { show: { operation: ["crmCallLogged"] } }, type: "json", default: "{}", description: "JSON. Un objeto con la forma que dice la referencia (developers.niiko.org)." },
|
|
133
|
+
{ displayName: "client", name: "client", required: true, displayOptions: { show: { operation: ["crmOwnerAssigned"] } }, type: "string", default: "" },
|
|
134
|
+
{ displayName: "owner", name: "owner", required: true, displayOptions: { show: { operation: ["crmOwnerAssigned"] } }, type: "string", default: "" },
|
|
135
|
+
{ displayName: "client", name: "client", required: true, displayOptions: { show: { operation: ["crmStageMoved"] } }, type: "string", default: "" },
|
|
136
|
+
{ displayName: "stage", name: "stage", required: true, displayOptions: { show: { operation: ["crmStageMoved"] } }, type: "string", default: "" },
|
|
137
|
+
{ displayName: "lostReason", name: "lostReason", required: false, displayOptions: { show: { operation: ["crmStageMoved"] } }, type: "string", default: "" },
|
|
138
|
+
{ displayName: "client", name: "client", required: true, displayOptions: { show: { operation: ["kiipuInvoiceProposed"] } }, type: "string", default: "" },
|
|
139
|
+
{ displayName: "lines", name: "lines", required: true, displayOptions: { show: { operation: ["kiipuInvoiceProposed"] } }, type: "json", default: "[]", description: "JSON. Una lista con la forma que dice la referencia (developers.niiko.org)." },
|
|
140
|
+
{ displayName: "dueAt", name: "dueAt", required: false, displayOptions: { show: { operation: ["kiipuInvoiceProposed"] } }, type: "string", default: "" },
|
|
141
|
+
{ displayName: "series", name: "series", required: false, displayOptions: { show: { operation: ["kiipuInvoiceProposed"] } }, type: "string", default: "" },
|
|
142
|
+
{ displayName: "client", name: "client", required: true, displayOptions: { show: { operation: ["crmTaskCreated"] } }, type: "string", default: "" },
|
|
143
|
+
{ displayName: "what", name: "what", required: true, displayOptions: { show: { operation: ["crmTaskCreated"] } }, type: "string", default: "" },
|
|
144
|
+
{ displayName: "dueAt", name: "dueAt", required: true, displayOptions: { show: { operation: ["crmTaskCreated"] } }, type: "string", default: "" },
|
|
145
|
+
{ displayName: "client", name: "client", required: true, displayOptions: { show: { operation: ["crmDealCreated"] } }, type: "string", default: "" },
|
|
146
|
+
{ displayName: "title", name: "title", required: true, displayOptions: { show: { operation: ["crmDealCreated"] } }, type: "string", default: "" },
|
|
147
|
+
{ displayName: "valueUsd", name: "valueUsd", required: false, displayOptions: { show: { operation: ["crmDealCreated"] } }, type: "string", default: "" },
|
|
148
|
+
{ displayName: "stage", name: "stage", required: false, displayOptions: { show: { operation: ["crmDealCreated"] } }, type: "string", default: "" },
|
|
149
|
+
{ displayName: "owner", name: "owner", required: false, displayOptions: { show: { operation: ["crmDealCreated"] } }, type: "string", default: "" },
|
|
150
|
+
{ displayName: "client", name: "client", required: true, displayOptions: { show: { operation: ["crmNoteAdded"] } }, type: "string", default: "" },
|
|
151
|
+
{ displayName: "note", name: "note", required: true, displayOptions: { show: { operation: ["crmNoteAdded"] } }, type: "string", default: "" },
|
|
152
|
+
{ displayName: "client", name: "client", required: true, displayOptions: { show: { operation: ["crmContactAdded"] } }, type: "string", default: "" },
|
|
153
|
+
{ displayName: "name", name: "name", required: true, displayOptions: { show: { operation: ["crmContactAdded"] } }, type: "string", default: "" },
|
|
154
|
+
{ displayName: "email", name: "email", required: false, displayOptions: { show: { operation: ["crmContactAdded"] } }, type: "string", default: "" },
|
|
155
|
+
{ displayName: "phone", name: "phone", required: false, displayOptions: { show: { operation: ["crmContactAdded"] } }, type: "string", default: "" },
|
|
156
|
+
{ displayName: "role", name: "role", required: false, displayOptions: { show: { operation: ["crmContactAdded"] } }, type: "string", default: "" },
|
|
157
|
+
{ displayName: "clients", name: "clients", required: true, displayOptions: { show: { operation: ["miiraBroadcastQuoted"] } }, type: "json", default: "[]", description: "JSON. Una lista con la forma que dice la referencia (developers.niiko.org)." },
|
|
158
|
+
{ displayName: "message", name: "message", required: true, displayOptions: { show: { operation: ["miiraBroadcastQuoted"] } }, type: "string", default: "" },
|
|
159
|
+
{ displayName: "template", name: "template", required: false, displayOptions: { show: { operation: ["miiraBroadcastQuoted"] } }, type: "string", default: "" },
|
|
160
|
+
{ displayName: "templateValues", name: "templateValues", required: false, displayOptions: { show: { operation: ["miiraBroadcastQuoted"] } }, type: "json", default: "[]", description: "JSON. Una lista con la forma que dice la referencia (developers.niiko.org)." },
|
|
161
|
+
{ displayName: "quote", name: "quote", required: true, displayOptions: { show: { operation: ["miiraBroadcastSent"] } }, type: "string", default: "" },
|
|
162
|
+
{ displayName: "invoiceId", name: "invoiceId", required: false, displayOptions: { show: { operation: ["kiipuDraftVoided"] } }, type: "string", default: "" },
|
|
163
|
+
{ displayName: "client", name: "client", required: false, displayOptions: { show: { operation: ["kiipuDraftVoided"] } }, type: "string", default: "" },
|
|
164
|
+
{ displayName: "client", name: "client", required: true, displayOptions: { show: { operation: ["kiipuPaymentReported"] } }, type: "string", default: "" },
|
|
165
|
+
{ displayName: "amountUsd", name: "amountUsd", required: true, displayOptions: { show: { operation: ["kiipuPaymentReported"] } }, type: "string", default: "" },
|
|
166
|
+
{ displayName: "invoice", name: "invoice", required: false, displayOptions: { show: { operation: ["kiipuPaymentReported"] } }, type: "string", default: "" },
|
|
167
|
+
],
|
|
168
|
+
};
|
|
169
|
+
async execute() {
|
|
170
|
+
const items = this.getInputData();
|
|
171
|
+
const salida = [];
|
|
172
|
+
const cred = (await this.getCredentials("niikoApi"));
|
|
173
|
+
const base = (cred.baseUrl || "https://niiko.org").replace(/\/+$/, "");
|
|
174
|
+
for (let i = 0; i < items.length; i++) {
|
|
175
|
+
const operacion = this.getNodeParameter("operation", i);
|
|
176
|
+
const def = OPERACIONES[operacion];
|
|
177
|
+
if (!def)
|
|
178
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `operación desconocida: ${operacion}`, { itemIndex: i });
|
|
179
|
+
const entrada = {};
|
|
180
|
+
for (const campo of def.campos) {
|
|
181
|
+
const v = this.getNodeParameter(campo, i, undefined);
|
|
182
|
+
if (v === undefined || v === "" || v === null)
|
|
183
|
+
continue; // lo opcional que no se rellenó no viaja
|
|
184
|
+
if (def.json.includes(campo)) {
|
|
185
|
+
try {
|
|
186
|
+
entrada[campo] = typeof v === "string" ? JSON.parse(v) : v;
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `el campo ${campo} no es JSON válido`, { itemIndex: i });
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
else
|
|
193
|
+
entrada[campo] = v;
|
|
194
|
+
}
|
|
195
|
+
// Una clave por ítem: reintentar el flujo no repite el efecto. El servidor la exige.
|
|
196
|
+
const idempotencyKey = (0, node_crypto_1.randomUUID)();
|
|
197
|
+
let respuesta;
|
|
198
|
+
try {
|
|
199
|
+
respuesta = (await this.helpers.httpRequest({
|
|
200
|
+
method: "POST",
|
|
201
|
+
url: `${base}/api/v1/actions/${def.accion}`,
|
|
202
|
+
headers: {
|
|
203
|
+
Authorization: `Bearer ${cred.apiKey}`,
|
|
204
|
+
"Content-Type": "application/json",
|
|
205
|
+
"Idempotency-Key": idempotencyKey,
|
|
206
|
+
"User-Agent": "n8n-nodes-niiko/9a8b80d285ff",
|
|
207
|
+
},
|
|
208
|
+
body: entrada,
|
|
209
|
+
json: true,
|
|
210
|
+
// Los desenlaces con nombre vienen con 4xx (negada) o 202 (esperando firma) y NO son errores del
|
|
211
|
+
// nodo: se leen como respuesta. Solo el transporte roto o un 5xx lo son.
|
|
212
|
+
ignoreHttpStatusErrors: true,
|
|
213
|
+
}));
|
|
214
|
+
}
|
|
215
|
+
catch (e) {
|
|
216
|
+
if (this.continueOnFail()) {
|
|
217
|
+
salida.push({ json: { error: String(e), idempotencyKey }, pairedItem: { item: i } });
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `niiko no contestó: ${String(e)}`, { itemIndex: i });
|
|
221
|
+
}
|
|
222
|
+
if (!respuesta || typeof respuesta !== "object" || !respuesta.status) {
|
|
223
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), "respuesta inesperada de niiko", { itemIndex: i });
|
|
224
|
+
}
|
|
225
|
+
salida.push({ json: { ...respuesta, action: def.accion, idempotencyKey: respuesta.idempotencyKey ?? idempotencyKey }, pairedItem: { item: i } });
|
|
226
|
+
}
|
|
227
|
+
return [salida];
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
exports.Niiko = Niiko;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2l2.6 7.4L22 12l-7.4 2.6L12 22l-2.6-7.4L2 12l7.4-2.6z"/></svg>
|
package/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = {};
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "n8n-nodes-niiko",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "El nodo de niiko para n8n: cada acción abierta al mundo, como operación. Generado del mismo plan que los SDK.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"n8n-community-node-package",
|
|
7
|
+
"niiko",
|
|
8
|
+
"crm",
|
|
9
|
+
"whatsapp"
|
|
10
|
+
],
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"homepage": "https://developers.niiko.org",
|
|
13
|
+
"author": {
|
|
14
|
+
"name": "Vorluno",
|
|
15
|
+
"email": "contacto@vorluno.dev"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/vorluno/n8n-nodes-niiko.git"
|
|
20
|
+
},
|
|
21
|
+
"main": "index.js",
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsc && node -e \"require('fs').copyFileSync('nodes/Niiko/niiko.svg','dist/nodes/Niiko/niiko.svg')\"",
|
|
24
|
+
"prepublishOnly": "npm run build"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist"
|
|
28
|
+
],
|
|
29
|
+
"n8n": {
|
|
30
|
+
"n8nNodesApiVersion": 1,
|
|
31
|
+
"credentials": [
|
|
32
|
+
"dist/credentials/NiikoApi.credentials.js"
|
|
33
|
+
],
|
|
34
|
+
"nodes": [
|
|
35
|
+
"dist/nodes/Niiko/Niiko.node.js"
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"n8n-workflow": "*",
|
|
40
|
+
"typescript": "^5.6.0",
|
|
41
|
+
"@types/node": "^22.10.0"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"n8n-workflow": "*"
|
|
45
|
+
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=20"
|
|
48
|
+
}
|
|
49
|
+
}
|