@wondai/n8n-nodes-nucleo 0.5.5 → 0.6.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 +5 -0
- package/dist/nodes/Nucleo/Nucleo.node.js +400 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -56,6 +56,11 @@ Exemplo de item configurável:
|
|
|
56
56
|
O node não interpreta essas regras: ele encaminha o JSON sem alteração. A validação e o snapshot
|
|
57
57
|
imutável do contrato aplicado pertencem à API do Núcleo (ADR-035).
|
|
58
58
|
|
|
59
|
+
A partir da v0.5.6, `Pedido Criar` falha antes da requisição quando `itens` está vazio ou quando
|
|
60
|
+
alguma linha não traz `produto_id`/`alias` e `quantidade` positiva. É uma proteção de integração:
|
|
61
|
+
o Núcleo continua sendo a autoridade final das regras, mas o workflow não envia pedido vazio por
|
|
62
|
+
erro de tool schema.
|
|
63
|
+
|
|
59
64
|
**Disponibilidade na Rede** ("tem na outra loja?") é exposta como **tool** do AI Agent, mas com regra
|
|
60
65
|
estrita no prompt: **usar SÓ quando o cliente pedir explicitamente outra unidade**. Passe o `produto_id`
|
|
61
66
|
já resolvido (ou uma `consulta` curta). A loja que pergunta vem do **vínculo do token** (a IA não
|
|
@@ -86,6 +86,29 @@ function asArray(value) {
|
|
|
86
86
|
}
|
|
87
87
|
return [];
|
|
88
88
|
}
|
|
89
|
+
function hasPedidoItemIdentifier(item) {
|
|
90
|
+
const produtoId = String(item.produto_id ?? "").trim();
|
|
91
|
+
const alias = String(item.alias ?? "").trim();
|
|
92
|
+
return Boolean(produtoId || alias);
|
|
93
|
+
}
|
|
94
|
+
function hasPositiveQuantidade(item) {
|
|
95
|
+
const quantidade = Number(item.quantidade);
|
|
96
|
+
return Number.isFinite(quantidade) && quantidade > 0;
|
|
97
|
+
}
|
|
98
|
+
function assertPedidoCriarItens(itens) {
|
|
99
|
+
if (!Array.isArray(itens) || itens.length === 0) {
|
|
100
|
+
throw new Error("Pedido Criar bloqueado: o campo itens precisa ser um array JSON não vazio com produto_id ou alias e quantidade. Resolva o catálogo e envie os itens estruturados antes de criar o pedido.");
|
|
101
|
+
}
|
|
102
|
+
const invalidIndex = itens.findIndex((item) => {
|
|
103
|
+
if (!item || typeof item !== "object" || Array.isArray(item))
|
|
104
|
+
return true;
|
|
105
|
+
const itemObject = item;
|
|
106
|
+
return !hasPedidoItemIdentifier(itemObject) || !hasPositiveQuantidade(itemObject);
|
|
107
|
+
});
|
|
108
|
+
if (invalidIndex >= 0) {
|
|
109
|
+
throw new Error(`Pedido Criar bloqueado: itens[${invalidIndex}] precisa ter produto_id ou alias e quantidade positiva.`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
89
112
|
/** Lê um parâmetro JSON (string ou objeto) como objeto simples. Vazio → null. */
|
|
90
113
|
function asObject(value) {
|
|
91
114
|
if (value && typeof value === "object" && !Array.isArray(value))
|
|
@@ -158,6 +181,11 @@ class Nucleo {
|
|
|
158
181
|
{ name: "Conversa", value: "conversa" },
|
|
159
182
|
{ name: "Contexto", value: "contexto" },
|
|
160
183
|
{ name: "Telemetria", value: "telemetria" },
|
|
184
|
+
// --- odonto (ADR-036/038): resources próprios do vertical; server roteia pelo token ---
|
|
185
|
+
{ name: "Paciente", value: "paciente" },
|
|
186
|
+
{ name: "Procedimento", value: "procedimento" },
|
|
187
|
+
{ name: "Agenda", value: "agenda" },
|
|
188
|
+
{ name: "Agendamento", value: "agendamento" },
|
|
161
189
|
],
|
|
162
190
|
default: "catalogo",
|
|
163
191
|
},
|
|
@@ -299,6 +327,82 @@ class Nucleo {
|
|
|
299
327
|
],
|
|
300
328
|
default: "enviar",
|
|
301
329
|
},
|
|
330
|
+
{
|
|
331
|
+
displayName: "Operação",
|
|
332
|
+
name: "operation",
|
|
333
|
+
type: "options",
|
|
334
|
+
noDataExpression: true,
|
|
335
|
+
displayOptions: { show: { resource: ["paciente"] } },
|
|
336
|
+
options: [
|
|
337
|
+
{
|
|
338
|
+
name: "Buscar",
|
|
339
|
+
value: "buscar",
|
|
340
|
+
action: "Buscar paciente por telefone",
|
|
341
|
+
description: "Lookup do paciente (cliente + dados clínicos) + agendamentos recentes",
|
|
342
|
+
},
|
|
343
|
+
],
|
|
344
|
+
default: "buscar",
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
displayName: "Operação",
|
|
348
|
+
name: "operation",
|
|
349
|
+
type: "options",
|
|
350
|
+
noDataExpression: true,
|
|
351
|
+
displayOptions: { show: { resource: ["procedimento"] } },
|
|
352
|
+
options: [
|
|
353
|
+
{
|
|
354
|
+
name: "Resolver",
|
|
355
|
+
value: "resolver",
|
|
356
|
+
action: "Resolver procedimentos por nome",
|
|
357
|
+
description: "Busca vários procedimentos numa chamada, tolera erro de digitação e apelidos e devolve id, duração e preço (status achou/ambiguo/nao_encontrado)",
|
|
358
|
+
},
|
|
359
|
+
],
|
|
360
|
+
default: "resolver",
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
displayName: "Operação",
|
|
364
|
+
name: "operation",
|
|
365
|
+
type: "options",
|
|
366
|
+
noDataExpression: true,
|
|
367
|
+
displayOptions: { show: { resource: ["agenda"] } },
|
|
368
|
+
options: [
|
|
369
|
+
{
|
|
370
|
+
name: "Consultar",
|
|
371
|
+
value: "consultar",
|
|
372
|
+
action: "Consultar horários livres numa data",
|
|
373
|
+
description: "Horários livres por profissional numa data (horário publicado − ocupado). Sem config do dia ⇒ unknown honesto",
|
|
374
|
+
},
|
|
375
|
+
],
|
|
376
|
+
default: "consultar",
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
displayName: "Operação",
|
|
380
|
+
name: "operation",
|
|
381
|
+
type: "options",
|
|
382
|
+
noDataExpression: true,
|
|
383
|
+
displayOptions: { show: { resource: ["agendamento"] } },
|
|
384
|
+
options: [
|
|
385
|
+
{
|
|
386
|
+
name: "Criar",
|
|
387
|
+
value: "criar",
|
|
388
|
+
action: "Criar agendamento",
|
|
389
|
+
description: "Marca um horário (idempotente). Overlap do profissional ⇒ 409",
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
name: "Alterar",
|
|
393
|
+
value: "alterar",
|
|
394
|
+
action: "Alterar agendamento",
|
|
395
|
+
description: "Remarca / troca procedimento ou profissional / observação / status (idempotente). Transição inválida ⇒ 409",
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
name: "Cancelar",
|
|
399
|
+
value: "cancelar",
|
|
400
|
+
action: "Cancelar agendamento",
|
|
401
|
+
description: "Cancela o agendamento (idempotente). Libera o horário",
|
|
402
|
+
},
|
|
403
|
+
],
|
|
404
|
+
default: "criar",
|
|
405
|
+
},
|
|
302
406
|
// ----------------------------------------------------------------- cliente:buscar
|
|
303
407
|
{
|
|
304
408
|
displayName: "Telefone",
|
|
@@ -831,6 +935,211 @@ class Nucleo {
|
|
|
831
935
|
description: "Opcional. UUID do pedido vinculado (validado contra o tenant).",
|
|
832
936
|
displayOptions: { show: { resource: ["telemetria"], operation: ["enviar"] } },
|
|
833
937
|
},
|
|
938
|
+
// ===================================================================== ODONTO (ADR-036/038)
|
|
939
|
+
// ----------------------------------------------------------------- paciente:buscar
|
|
940
|
+
{
|
|
941
|
+
displayName: "Telefone",
|
|
942
|
+
name: "pacTelefone",
|
|
943
|
+
type: "string",
|
|
944
|
+
default: "",
|
|
945
|
+
required: true,
|
|
946
|
+
placeholder: "+5511999999999",
|
|
947
|
+
description: "Telefone do paciente em E.164 (só dígitos + DDI). Devolve o paciente (cliente + dados clínicos) e os agendamentos recentes.",
|
|
948
|
+
displayOptions: { show: { resource: ["paciente"], operation: ["buscar"] } },
|
|
949
|
+
},
|
|
950
|
+
// ----------------------------------------------------------------- procedimento:resolver
|
|
951
|
+
{
|
|
952
|
+
displayName: "Consultas",
|
|
953
|
+
name: "procConsultas",
|
|
954
|
+
type: "string",
|
|
955
|
+
typeOptions: { rows: 3 },
|
|
956
|
+
default: "",
|
|
957
|
+
required: true,
|
|
958
|
+
placeholder: "limpeza, clareamento, canal",
|
|
959
|
+
description: "Procedimentos a buscar: um por linha ou separados por vírgula (máx 10). Tolera erro de digitação, falta de acento e apelidos. Devolve status (achou/ambiguo/nao_encontrado) + candidatos com id, duração e preço.",
|
|
960
|
+
displayOptions: { show: { resource: ["procedimento"], operation: ["resolver"] } },
|
|
961
|
+
},
|
|
962
|
+
// ----------------------------------------------------------------- agenda:consultar
|
|
963
|
+
{
|
|
964
|
+
displayName: "Data",
|
|
965
|
+
name: "agdData",
|
|
966
|
+
type: "string",
|
|
967
|
+
default: "",
|
|
968
|
+
required: true,
|
|
969
|
+
placeholder: "2026-07-15",
|
|
970
|
+
description: "UMA data YYYY-MM-DD. Devolve horários livres por profissional (horário publicado − ocupado). Sem config do dia ⇒ status unknown (não invente horário).",
|
|
971
|
+
displayOptions: { show: { resource: ["agenda"], operation: ["consultar"] } },
|
|
972
|
+
},
|
|
973
|
+
{
|
|
974
|
+
displayName: "Procedimento (ID, opcional)",
|
|
975
|
+
name: "agdProcedimentoId",
|
|
976
|
+
type: "string",
|
|
977
|
+
default: "",
|
|
978
|
+
description: "UUID do procedimento (do Procedimento Resolver). Define a duração dos slots; vazio = 30 min.",
|
|
979
|
+
displayOptions: { show: { resource: ["agenda"], operation: ["consultar"] } },
|
|
980
|
+
},
|
|
981
|
+
{
|
|
982
|
+
displayName: "Profissional (ID, opcional)",
|
|
983
|
+
name: "agdProfissionalId",
|
|
984
|
+
type: "string",
|
|
985
|
+
default: "",
|
|
986
|
+
description: "UUID do profissional. Vazio = todos os profissionais ativos.",
|
|
987
|
+
displayOptions: { show: { resource: ["agenda"], operation: ["consultar"] } },
|
|
988
|
+
},
|
|
989
|
+
{
|
|
990
|
+
displayName: "Passo (min, opcional)",
|
|
991
|
+
name: "agdPassoMin",
|
|
992
|
+
type: "number",
|
|
993
|
+
default: 30,
|
|
994
|
+
description: "Granularidade dos horários oferecidos, em minutos (1..240). Default 30.",
|
|
995
|
+
displayOptions: { show: { resource: ["agenda"], operation: ["consultar"] } },
|
|
996
|
+
},
|
|
997
|
+
// ----------------------------------------------------------------- agendamento:criar
|
|
998
|
+
{
|
|
999
|
+
displayName: "Telefone",
|
|
1000
|
+
name: "agcTelefone",
|
|
1001
|
+
type: "string",
|
|
1002
|
+
default: "",
|
|
1003
|
+
required: true,
|
|
1004
|
+
placeholder: "+5511999999999",
|
|
1005
|
+
description: "Telefone do paciente (E.164). Resolve o paciente existente ou cria um novo.",
|
|
1006
|
+
displayOptions: { show: { resource: ["agendamento"], operation: ["criar"] } },
|
|
1007
|
+
},
|
|
1008
|
+
{
|
|
1009
|
+
displayName: "Nome do Paciente",
|
|
1010
|
+
name: "agcNomePaciente",
|
|
1011
|
+
type: "string",
|
|
1012
|
+
default: "",
|
|
1013
|
+
description: "Opcional. Preenche paciente novo ou existente ainda sem nome. Não sobrescreve nome já cadastrado.",
|
|
1014
|
+
displayOptions: { show: { resource: ["agendamento"], operation: ["criar"] } },
|
|
1015
|
+
},
|
|
1016
|
+
{
|
|
1017
|
+
displayName: "Profissional (ID)",
|
|
1018
|
+
name: "agcProfissionalId",
|
|
1019
|
+
type: "string",
|
|
1020
|
+
default: "",
|
|
1021
|
+
required: true,
|
|
1022
|
+
description: "UUID do profissional (obrigatório). Horário ocupado desse profissional ⇒ 409 (indisponível).",
|
|
1023
|
+
displayOptions: { show: { resource: ["agendamento"], operation: ["criar"] } },
|
|
1024
|
+
},
|
|
1025
|
+
{
|
|
1026
|
+
displayName: "Procedimento (ID, opcional)",
|
|
1027
|
+
name: "agcProcedimentoId",
|
|
1028
|
+
type: "string",
|
|
1029
|
+
default: "",
|
|
1030
|
+
description: "UUID do procedimento (do Procedimento Resolver). Define a duração quando 'Fim' não é enviado.",
|
|
1031
|
+
displayOptions: { show: { resource: ["agendamento"], operation: ["criar"] } },
|
|
1032
|
+
},
|
|
1033
|
+
{
|
|
1034
|
+
displayName: "Início",
|
|
1035
|
+
name: "agcInicio",
|
|
1036
|
+
type: "string",
|
|
1037
|
+
default: "",
|
|
1038
|
+
required: true,
|
|
1039
|
+
placeholder: "2026-07-15T14:00:00-03:00",
|
|
1040
|
+
description: "Instante ISO 8601 do início do agendamento.",
|
|
1041
|
+
displayOptions: { show: { resource: ["agendamento"], operation: ["criar"] } },
|
|
1042
|
+
},
|
|
1043
|
+
{
|
|
1044
|
+
displayName: "Fim (opcional)",
|
|
1045
|
+
name: "agcFim",
|
|
1046
|
+
type: "string",
|
|
1047
|
+
default: "",
|
|
1048
|
+
placeholder: "2026-07-15T14:30:00-03:00",
|
|
1049
|
+
description: "Instante ISO 8601 do fim. Vazio = início + duração do procedimento (ou 30 min).",
|
|
1050
|
+
displayOptions: { show: { resource: ["agendamento"], operation: ["criar"] } },
|
|
1051
|
+
},
|
|
1052
|
+
{
|
|
1053
|
+
displayName: "Observações",
|
|
1054
|
+
name: "agcObservacoes",
|
|
1055
|
+
type: "string",
|
|
1056
|
+
default: "",
|
|
1057
|
+
displayOptions: { show: { resource: ["agendamento"], operation: ["criar"] } },
|
|
1058
|
+
},
|
|
1059
|
+
// ----------------------------------------------------------------- agendamento:alterar / cancelar
|
|
1060
|
+
{
|
|
1061
|
+
displayName: "Agendamento ID",
|
|
1062
|
+
name: "agAgendamentoId",
|
|
1063
|
+
type: "string",
|
|
1064
|
+
default: "",
|
|
1065
|
+
required: true,
|
|
1066
|
+
description: "UUID do agendamento (do tenant). Agendamento de outro tenant → 404.",
|
|
1067
|
+
displayOptions: { show: { resource: ["agendamento"], operation: ["alterar", "cancelar"] } },
|
|
1068
|
+
},
|
|
1069
|
+
{
|
|
1070
|
+
displayName: "Início (remarcar)",
|
|
1071
|
+
name: "agaInicio",
|
|
1072
|
+
type: "string",
|
|
1073
|
+
default: "",
|
|
1074
|
+
placeholder: "2026-07-16T09:00:00-03:00",
|
|
1075
|
+
description: "ISO 8601. Vazio = não remarca.",
|
|
1076
|
+
displayOptions: { show: { resource: ["agendamento"], operation: ["alterar"] } },
|
|
1077
|
+
},
|
|
1078
|
+
{
|
|
1079
|
+
displayName: "Fim (remarcar)",
|
|
1080
|
+
name: "agaFim",
|
|
1081
|
+
type: "string",
|
|
1082
|
+
default: "",
|
|
1083
|
+
description: "ISO 8601. Vazio = mantém/recalcula pelo início.",
|
|
1084
|
+
displayOptions: { show: { resource: ["agendamento"], operation: ["alterar"] } },
|
|
1085
|
+
},
|
|
1086
|
+
{
|
|
1087
|
+
displayName: "Procedimento (ID, alterar)",
|
|
1088
|
+
name: "agaProcedimentoId",
|
|
1089
|
+
type: "string",
|
|
1090
|
+
default: "",
|
|
1091
|
+
description: "UUID do novo procedimento. Vazio = não altera. Use 'null' para desvincular.",
|
|
1092
|
+
displayOptions: { show: { resource: ["agendamento"], operation: ["alterar"] } },
|
|
1093
|
+
},
|
|
1094
|
+
{
|
|
1095
|
+
displayName: "Profissional (ID, alterar)",
|
|
1096
|
+
name: "agaProfissionalId",
|
|
1097
|
+
type: "string",
|
|
1098
|
+
default: "",
|
|
1099
|
+
description: "UUID do novo profissional. Vazio = não altera. Overlap ⇒ 409.",
|
|
1100
|
+
displayOptions: { show: { resource: ["agendamento"], operation: ["alterar"] } },
|
|
1101
|
+
},
|
|
1102
|
+
{
|
|
1103
|
+
displayName: "Observações (alterar)",
|
|
1104
|
+
name: "agaObservacoes",
|
|
1105
|
+
type: "string",
|
|
1106
|
+
default: "",
|
|
1107
|
+
description: "Vazio = não altera.",
|
|
1108
|
+
displayOptions: { show: { resource: ["agendamento"], operation: ["alterar"] } },
|
|
1109
|
+
},
|
|
1110
|
+
{
|
|
1111
|
+
displayName: "Status",
|
|
1112
|
+
name: "agaStatus",
|
|
1113
|
+
type: "options",
|
|
1114
|
+
options: [
|
|
1115
|
+
{ name: "(não alterar)", value: "" },
|
|
1116
|
+
{ name: "Confirmado", value: "confirmado" },
|
|
1117
|
+
{ name: "Realizado", value: "realizado" },
|
|
1118
|
+
{ name: "Faltou", value: "faltou" },
|
|
1119
|
+
{ name: "Cancelado", value: "cancelado" },
|
|
1120
|
+
],
|
|
1121
|
+
default: "",
|
|
1122
|
+
description: "Transição de status (a API recusa transição inválida com 409). Válidas: agendado→confirmado/cancelado; confirmado→realizado/faltou/cancelado.",
|
|
1123
|
+
displayOptions: { show: { resource: ["agendamento"], operation: ["alterar"] } },
|
|
1124
|
+
},
|
|
1125
|
+
{
|
|
1126
|
+
displayName: "Motivo",
|
|
1127
|
+
name: "agxMotivo",
|
|
1128
|
+
type: "string",
|
|
1129
|
+
default: "",
|
|
1130
|
+
description: "Motivo do cancelamento (opcional).",
|
|
1131
|
+
displayOptions: { show: { resource: ["agendamento"], operation: ["cancelar"] } },
|
|
1132
|
+
},
|
|
1133
|
+
{
|
|
1134
|
+
displayName: "Idempotency Key",
|
|
1135
|
+
name: "agIdempotencyKey",
|
|
1136
|
+
type: "string",
|
|
1137
|
+
default: "",
|
|
1138
|
+
description: "Criar: vazio = gerada automaticamente. Alterar/cancelar: use a MESMA chave (ex.: messageID do WhatsApp) num retry para não reaplicar a operação.",
|
|
1139
|
+
displayOptions: {
|
|
1140
|
+
show: { resource: ["agendamento"], operation: ["criar", "alterar", "cancelar"] },
|
|
1141
|
+
},
|
|
1142
|
+
},
|
|
834
1143
|
],
|
|
835
1144
|
};
|
|
836
1145
|
}
|
|
@@ -884,6 +1193,7 @@ class Nucleo {
|
|
|
884
1193
|
const telefone = this.getNodeParameter("telefone", i).trim();
|
|
885
1194
|
const nomeCliente = this.getNodeParameter("nomeCliente", i, "").trim();
|
|
886
1195
|
const itens = asArray(this.getNodeParameter("itens", i));
|
|
1196
|
+
assertPedidoCriarItens(itens);
|
|
887
1197
|
const tipoEntrega = this.getNodeParameter("tipoEntrega", i);
|
|
888
1198
|
const observacoes = this.getNodeParameter("observacoes", i, "").trim();
|
|
889
1199
|
const agendadoPara = this.getNodeParameter("agendadoPara", i, "").trim();
|
|
@@ -1081,6 +1391,96 @@ class Nucleo {
|
|
|
1081
1391
|
path = "/api/v1/agent/telemetria";
|
|
1082
1392
|
bodyObj = b;
|
|
1083
1393
|
}
|
|
1394
|
+
else if (resource === "paciente" && operation === "buscar") {
|
|
1395
|
+
const telefone = this.getNodeParameter("pacTelefone", i).trim();
|
|
1396
|
+
method = "POST";
|
|
1397
|
+
path = "/api/v1/agent/paciente";
|
|
1398
|
+
bodyObj = { telefone };
|
|
1399
|
+
}
|
|
1400
|
+
else if (resource === "procedimento" && operation === "resolver") {
|
|
1401
|
+
const consultas = parseConsultas(this.getNodeParameter("procConsultas", i));
|
|
1402
|
+
method = "POST";
|
|
1403
|
+
path = "/api/v1/agent/procedimento/resolver";
|
|
1404
|
+
bodyObj = { consultas };
|
|
1405
|
+
}
|
|
1406
|
+
else if (resource === "agenda" && operation === "consultar") {
|
|
1407
|
+
const data = this.getNodeParameter("agdData", i).trim();
|
|
1408
|
+
const procedimentoId = this.getNodeParameter("agdProcedimentoId", i, "").trim();
|
|
1409
|
+
const profissionalId = this.getNodeParameter("agdProfissionalId", i, "").trim();
|
|
1410
|
+
const passoMin = Number(this.getNodeParameter("agdPassoMin", i, 30));
|
|
1411
|
+
const b = { data };
|
|
1412
|
+
if (procedimentoId)
|
|
1413
|
+
b.procedimento_id = procedimentoId;
|
|
1414
|
+
if (profissionalId)
|
|
1415
|
+
b.profissional_id = profissionalId;
|
|
1416
|
+
if (Number.isFinite(passoMin) && passoMin > 0)
|
|
1417
|
+
b.passo_min = passoMin;
|
|
1418
|
+
method = "POST";
|
|
1419
|
+
path = "/api/v1/agent/agenda/consultar";
|
|
1420
|
+
bodyObj = b;
|
|
1421
|
+
}
|
|
1422
|
+
else if (resource === "agendamento" && operation === "criar") {
|
|
1423
|
+
const telefone = this.getNodeParameter("agcTelefone", i).trim();
|
|
1424
|
+
const nomePaciente = this.getNodeParameter("agcNomePaciente", i, "").trim();
|
|
1425
|
+
const profissionalId = this.getNodeParameter("agcProfissionalId", i).trim();
|
|
1426
|
+
const procedimentoId = this.getNodeParameter("agcProcedimentoId", i, "").trim();
|
|
1427
|
+
const inicio = this.getNodeParameter("agcInicio", i).trim();
|
|
1428
|
+
const fim = this.getNodeParameter("agcFim", i, "").trim();
|
|
1429
|
+
const observacoes = this.getNodeParameter("agcObservacoes", i, "").trim();
|
|
1430
|
+
// Escrita de agenda EXIGE Idempotency-Key no servidor (400 se vazia) — gera se não vier.
|
|
1431
|
+
idempotencyKey =
|
|
1432
|
+
this.getNodeParameter("agIdempotencyKey", i, "").trim() || (0, node_crypto_1.randomUUID)();
|
|
1433
|
+
const b = { telefone, profissional_id: profissionalId, inicio };
|
|
1434
|
+
if (nomePaciente)
|
|
1435
|
+
b.nome_paciente = nomePaciente;
|
|
1436
|
+
if (procedimentoId)
|
|
1437
|
+
b.procedimento_id = procedimentoId;
|
|
1438
|
+
if (fim)
|
|
1439
|
+
b.fim = fim;
|
|
1440
|
+
if (observacoes)
|
|
1441
|
+
b.observacoes = observacoes;
|
|
1442
|
+
method = "POST";
|
|
1443
|
+
path = "/api/v1/agent/agendamento";
|
|
1444
|
+
bodyObj = b;
|
|
1445
|
+
}
|
|
1446
|
+
else if (resource === "agendamento" && operation === "alterar") {
|
|
1447
|
+
const agendamentoId = this.getNodeParameter("agAgendamentoId", i).trim();
|
|
1448
|
+
const inicio = this.getNodeParameter("agaInicio", i, "").trim();
|
|
1449
|
+
const fim = this.getNodeParameter("agaFim", i, "").trim();
|
|
1450
|
+
const procedimentoId = this.getNodeParameter("agaProcedimentoId", i, "").trim();
|
|
1451
|
+
const profissionalId = this.getNodeParameter("agaProfissionalId", i, "").trim();
|
|
1452
|
+
const observacoes = this.getNodeParameter("agaObservacoes", i, "").trim();
|
|
1453
|
+
const status = this.getNodeParameter("agaStatus", i, "").trim();
|
|
1454
|
+
const b = {};
|
|
1455
|
+
if (inicio)
|
|
1456
|
+
b.inicio = inicio;
|
|
1457
|
+
if (fim)
|
|
1458
|
+
b.fim = fim;
|
|
1459
|
+
// "null" (string) desvincula o procedimento; vazio = não mexe no campo.
|
|
1460
|
+
if (procedimentoId) {
|
|
1461
|
+
b.procedimento_id = procedimentoId.toLowerCase() === "null" ? null : procedimentoId;
|
|
1462
|
+
}
|
|
1463
|
+
if (profissionalId)
|
|
1464
|
+
b.profissional_id = profissionalId;
|
|
1465
|
+
if (observacoes)
|
|
1466
|
+
b.observacoes = observacoes;
|
|
1467
|
+
if (status)
|
|
1468
|
+
b.status = status;
|
|
1469
|
+
idempotencyKey =
|
|
1470
|
+
this.getNodeParameter("agIdempotencyKey", i, "").trim() || (0, node_crypto_1.randomUUID)();
|
|
1471
|
+
method = "PATCH";
|
|
1472
|
+
path = `/api/v1/agent/agendamento/${encodeURIComponent(agendamentoId)}`;
|
|
1473
|
+
bodyObj = b;
|
|
1474
|
+
}
|
|
1475
|
+
else if (resource === "agendamento" && operation === "cancelar") {
|
|
1476
|
+
const agendamentoId = this.getNodeParameter("agAgendamentoId", i).trim();
|
|
1477
|
+
const motivo = this.getNodeParameter("agxMotivo", i, "").trim();
|
|
1478
|
+
idempotencyKey =
|
|
1479
|
+
this.getNodeParameter("agIdempotencyKey", i, "").trim() || (0, node_crypto_1.randomUUID)();
|
|
1480
|
+
method = "POST";
|
|
1481
|
+
path = `/api/v1/agent/agendamento/${encodeURIComponent(agendamentoId)}/cancelar`;
|
|
1482
|
+
bodyObj = motivo ? { motivo } : {};
|
|
1483
|
+
}
|
|
1084
1484
|
else {
|
|
1085
1485
|
throw new n8n_workflow_1.NodeApiError(this.getNode(), {
|
|
1086
1486
|
message: `Operação não suportada: ${resource}.${operation}`,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wondai/n8n-nodes-nucleo",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Node n8n para o Núcleo Wondai — atendimento de IA (gate liga/desliga, cliente, catálogo fuzzy,
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "Node n8n para o Núcleo Wondai — atendimento de IA multi-vertical (gate liga/desliga, cliente/paciente, catálogo fuzzy, pedidos [padaria], procedimento/agenda/agendamento [odonto], contexto operacional, telemetria) com assinatura HMAC v1. Tenant e vertical vêm do token (a parede, ADR-013/038).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"n8n-community-node-package",
|
|
7
7
|
"n8n",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"nucleo",
|
|
10
10
|
"crm",
|
|
11
11
|
"padaria",
|
|
12
|
+
"odonto",
|
|
12
13
|
"ai-tool"
|
|
13
14
|
],
|
|
14
15
|
"license": "MIT",
|