@wondai/n8n-nodes-nucleo 0.2.2 → 0.2.4

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 CHANGED
@@ -20,9 +20,11 @@ parede. Isso mantém o segredo fora do workflow, a lógica num lugar só e o tok
20
20
  |---|---|---|---|
21
21
  | Cliente | Buscar | `GET /api/v1/agent/cliente` | `cliente:ler` |
22
22
  | Catálogo | Resolver Produtos | `POST /api/v1/agent/catalogo/resolver` | `catalogo:ler` |
23
+ | Pedido | Detalhar | `GET /api/v1/agent/pedido/:ref` | `pedido:ler` |
23
24
  | Pedido | Criar | `POST /api/v1/agent/pedido` | `pedido:escrever` |
24
25
  | Pedido | Alterar | `PATCH /api/v1/agent/pedido/:id` | `pedido:escrever` |
25
26
  | Pedido | Cancelar | `POST /api/v1/agent/pedido/:id/cancelar` | `pedido:escrever` |
27
+ | Conversa | Registrar | `POST /api/v1/agent/conversa/fechar` | `conversa:escrever` |
26
28
 
27
29
  **Resolver Produtos** é a operação inteligente: manda várias consultas numa chamada (máx 10),
28
30
  tolera erro de digitação (`banofe`→Banoffee), falta de acento (`pao frances`→Pão Francês) e
@@ -31,6 +33,10 @@ apelidos; devolve no máx 3 candidatos por consulta com `status` (`achou`/`ambig
31
33
  **Criar** é idempotente: deixe *Idempotency Key* vazio (gera uma) ou repita a mesma chave num retry
32
34
  — o Núcleo nunca duplica o pedido.
33
35
 
36
+ No **Pedido Criar**, `Nome do Cliente` e `Endereço de Entrega (JSON)` são opcionais e existem para
37
+ carregar o que a IA já coletou no atendimento. O node continua burro: só envia `nome_cliente` e
38
+ `endereco_entrega`; quem decide tenant, valida e grava snapshot em `crm.entregas` é o Núcleo.
39
+
34
40
  ## Instalação (n8n self-hosted)
35
41
 
36
42
  1. **Settings → Community Nodes → Install** → `@wondai/n8n-nodes-nucleo`.
@@ -85,6 +85,24 @@ function asArray(value) {
85
85
  }
86
86
  return [];
87
87
  }
88
+ /** Lê um parâmetro JSON (string ou objeto) como objeto simples. Vazio → null. */
89
+ function asObject(value) {
90
+ if (value && typeof value === "object" && !Array.isArray(value))
91
+ return value;
92
+ if (typeof value === "string") {
93
+ const t = value.trim();
94
+ if (!t)
95
+ return null;
96
+ try {
97
+ const j = JSON.parse(t);
98
+ return j && typeof j === "object" && !Array.isArray(j) ? j : null;
99
+ }
100
+ catch {
101
+ return null;
102
+ }
103
+ }
104
+ return null;
105
+ }
88
106
  /** IDs separados por vírgula ou JSON array → string[]. */
89
107
  function parseIds(value) {
90
108
  if (Array.isArray(value))
@@ -196,7 +214,7 @@ class Nucleo {
196
214
  name: "Alterar",
197
215
  value: "alterar",
198
216
  action: "Alterar pedido",
199
- description: "Altera data/observações/itens (adicionar, remover, mudar quantidade)",
217
+ description: "Altera data/observações/itens. Itens por PRODUTO (adicionar soma, ajustar +/-, definir, remover produto) ou por id de item (compat).",
200
218
  },
201
219
  {
202
220
  name: "Cancelar",
@@ -268,6 +286,14 @@ class Nucleo {
268
286
  description: "Telefone do cliente (E.164). Resolve o cliente existente ou cria um novo.",
269
287
  displayOptions: { show: { resource: ["pedido"], operation: ["criar"] } },
270
288
  },
289
+ {
290
+ displayName: "Nome do Cliente",
291
+ name: "nomeCliente",
292
+ type: "string",
293
+ default: "",
294
+ description: "Opcional. Preenche cliente novo ou cliente existente ainda sem nome. Não sobrescreve nome já cadastrado.",
295
+ displayOptions: { show: { resource: ["pedido"], operation: ["criar"] } },
296
+ },
271
297
  {
272
298
  displayName: "Itens (JSON)",
273
299
  name: "itens",
@@ -304,6 +330,14 @@ class Nucleo {
304
330
  description: "Data/hora ISO 8601 para encomenda. Vazio = imediato.",
305
331
  displayOptions: { show: { resource: ["pedido"], operation: ["criar"] } },
306
332
  },
333
+ {
334
+ displayName: "Endereço de Entrega (JSON)",
335
+ name: "enderecoEntrega",
336
+ type: "json",
337
+ default: "{}",
338
+ description: 'Opcional. Para entrega, snapshot do endereço escolhido: {"logradouro","numero","complemento","bairro","cidade","estado","cep","referencia"}. Logradouro e cidade são obrigatórios quando enviado.',
339
+ displayOptions: { show: { resource: ["pedido"], operation: ["criar"] } },
340
+ },
307
341
  {
308
342
  displayName: "Loja (unit_id)",
309
343
  name: "unitId",
@@ -370,6 +404,30 @@ class Nucleo {
370
404
  description: 'Muda a quantidade de itens já existentes: [{"item_id":"...","quantidade":4}]. Os IDs vêm do Detalhar. Trocar item = remover + adicionar. Vazio = não altera.',
371
405
  displayOptions: { show: { resource: ["pedido"], operation: ["alterar"] } },
372
406
  },
407
+ {
408
+ displayName: "Ajustar Itens por Produto (JSON)",
409
+ name: "ajustarItens",
410
+ type: "json",
411
+ default: "[]",
412
+ description: 'Ajuste relativo POR PRODUTO (o cliente não sabe id de item): [{"produto_id":"...","quantidade_delta":7}] soma; -5 subtrai; resultado ≤ 0 remove a linha. produto_id vem do Catálogo Resolver. Vazio = não ajusta.',
413
+ displayOptions: { show: { resource: ["pedido"], operation: ["alterar"] } },
414
+ },
415
+ {
416
+ displayName: "Definir Itens por Produto (JSON)",
417
+ name: "definirItens",
418
+ type: "json",
419
+ default: "[]",
420
+ description: 'Quantidade absoluta POR PRODUTO: [{"produto_id":"...","quantidade":5}] deixa a linha com 5; 0 remove. produto_id vem do Catálogo Resolver. Vazio = não define.',
421
+ displayOptions: { show: { resource: ["pedido"], operation: ["alterar"] } },
422
+ },
423
+ {
424
+ displayName: "Remover Produtos (produto_ids)",
425
+ name: "removerProdutoIds",
426
+ type: "string",
427
+ default: "",
428
+ description: "produto_ids a remover do pedido (todas as linhas do produto), separados por vírgula ou JSON array. Use para 'tirar os X' e para a troca. produto_id vem do Catálogo Resolver.",
429
+ displayOptions: { show: { resource: ["pedido"], operation: ["alterar"] } },
430
+ },
373
431
  {
374
432
  displayName: "Motivo",
375
433
  name: "motivo",
@@ -455,20 +513,27 @@ class Nucleo {
455
513
  }
456
514
  else if (resource === "pedido" && operation === "criar") {
457
515
  const telefone = this.getNodeParameter("telefone", i).trim();
516
+ const nomeCliente = this.getNodeParameter("nomeCliente", i, "").trim();
458
517
  const itens = asArray(this.getNodeParameter("itens", i));
459
518
  const tipoEntrega = this.getNodeParameter("tipoEntrega", i);
460
519
  const observacoes = this.getNodeParameter("observacoes", i, "").trim();
461
520
  const agendadoPara = this.getNodeParameter("agendadoPara", i, "").trim();
521
+ const enderecoEntrega = asObject(this.getNodeParameter("enderecoEntrega", i, "{}"));
462
522
  const unitId = this.getNodeParameter("unitId", i, "").trim();
463
523
  idempotencyKey =
464
524
  this.getNodeParameter("idempotencyKey", i, "").trim() || (0, node_crypto_1.randomUUID)();
465
525
  const b = { telefone, itens };
526
+ if (nomeCliente)
527
+ b.nome_cliente = nomeCliente;
466
528
  if (tipoEntrega)
467
529
  b.tipo_entrega = tipoEntrega;
468
530
  if (observacoes)
469
531
  b.observacoes = observacoes;
470
532
  if (agendadoPara)
471
533
  b.agendado_para = agendadoPara;
534
+ if (enderecoEntrega && Object.keys(enderecoEntrega).length) {
535
+ b.endereco_entrega = enderecoEntrega;
536
+ }
472
537
  if (unitId)
473
538
  b.unit_id = unitId;
474
539
  method = "POST";
@@ -482,6 +547,9 @@ class Nucleo {
482
547
  const adicionar = asArray(this.getNodeParameter("adicionarItens", i, "[]"));
483
548
  const remover = parseIds(this.getNodeParameter("removerItemIds", i, ""));
484
549
  const alterar = asArray(this.getNodeParameter("alterarItens", i, "[]"));
550
+ const ajustar = asArray(this.getNodeParameter("ajustarItens", i, "[]"));
551
+ const definir = asArray(this.getNodeParameter("definirItens", i, "[]"));
552
+ const removerProdutos = parseIds(this.getNodeParameter("removerProdutoIds", i, ""));
485
553
  const b = {};
486
554
  if (observacoes.trim())
487
555
  b.observacoes = observacoes.trim();
@@ -493,6 +561,12 @@ class Nucleo {
493
561
  b.remover_item_ids = remover;
494
562
  if (alterar.length)
495
563
  b.alterar_itens = alterar;
564
+ if (ajustar.length)
565
+ b.ajustar_itens = ajustar;
566
+ if (definir.length)
567
+ b.definir_itens = definir;
568
+ if (removerProdutos.length)
569
+ b.remover_produto_ids = removerProdutos;
496
570
  method = "PATCH";
497
571
  path = `/api/v1/agent/pedido/${encodeURIComponent(pedidoId)}`;
498
572
  bodyObj = b;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wondai/n8n-nodes-nucleo",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Node n8n para o Núcleo Wondai — atendimento de IA (cliente, catálogo fuzzy, pedidos) com assinatura HMAC v1. Tenant vem do token (a parede, ADR-013).",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",