nexusflex-mcp 3.6.0 → 3.67.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/docs.mjs +526 -322
- package/package.json +1 -1
- package/server.mjs +592 -54
package/docs.mjs
CHANGED
|
@@ -1,322 +1,526 @@
|
|
|
1
|
-
// ============================================================================
|
|
2
|
-
// Documentación integrada del MCP de Nexus Flex — fuente de verdad de la ayuda
|
|
3
|
-
// por herramienta (tool `ayuda`). Cada tool trae: qué hace, cómo usarlo (params),
|
|
4
|
-
// un ejemplo resuelto y qué NO hace (para no inferir comportamiento inexistente).
|
|
5
|
-
//
|
|
6
|
-
// ⚠️ ESPEJO: este archivo está DUPLICADO en `backend/src/services/mcp-docs.ts`
|
|
7
|
-
// (el conector remoto se deploya sin la carpeta mcp/). Si editás uno, editá el
|
|
8
|
-
// otro — igual que MCP_VERSION/NOVEDADES. Mantené el contenido idéntico.
|
|
9
|
-
// ============================================================================
|
|
10
|
-
|
|
11
|
-
// Tópicos transversales (no son tools) — se consultan con `ayuda tema:<clave>`.
|
|
12
|
-
export const TOPICS = {
|
|
13
|
-
facturacion_marketplace: [
|
|
14
|
-
"FACTURACIÓN DEL MARKETPLACE DE RUTAS PÚBLICAS",
|
|
15
|
-
"",
|
|
16
|
-
"Adjudicar una ruta (ruta_adjudicar) es OPERATIVO: cierra la subasta, marca el",
|
|
17
|
-
"nodo ganador (nodoToma) y deja registrada la OBLIGACIÓN al precio de la oferta",
|
|
18
|
-
"(precioAcordado). NO crea ningún asiento automático de facturación semanal, ni",
|
|
19
|
-
"en rendiciones, ni en comisiones de afiliado, ni en el clearing entre nodos.",
|
|
20
|
-
"El marketplace NO mueve dinero.",
|
|
21
|
-
"",
|
|
22
|
-
"Es decir: adjudicar ≠ colecta. Una colecta entre nodos sí genera un ítem de",
|
|
23
|
-
"clearing; una ruta adjudicada, NO. El pago entre el nodo que publica y el que",
|
|
24
|
-
"toma se acuerda y se salda MANUALMENTE (efectivo con el cadete, o descontándolo",
|
|
25
|
-
"de lo que un nodo ya le debe al otro por otros conceptos).",
|
|
26
|
-
"",
|
|
27
|
-
"Cómo verificarlo desde el MCP:",
|
|
28
|
-
" • mis_rutas → estado 'adjudicada', nodoToma y precioAcordado de cada ruta tuya.",
|
|
29
|
-
" • rendiciones / comisiones_afiliado_ver → NO listarán la ruta adjudicada.",
|
|
30
|
-
"",
|
|
31
|
-
"Qué falta (roadmap, todavía NO implementado): un settlement automático que,",
|
|
32
|
-
"al adjudicar, cargue el precioAcordado en la cuenta corriente entre los dos",
|
|
33
|
-
"nodos. Mientras no exista, tratá el pago como manual y avisáselo al usuario.",
|
|
34
|
-
].join("\n"),
|
|
35
|
-
|
|
36
|
-
dinero: [
|
|
37
|
-
"DINERO — QUÉ NO HACE NUNCA EL MCP",
|
|
38
|
-
"",
|
|
39
|
-
"El MCP nunca mueve plata: no registra pagos de vendedores, no factura, no",
|
|
40
|
-
"concilia caja, no imputa cobros/haberes en cuentas corrientes. Hay una denylist",
|
|
41
|
-
"de dinero (mcpMoneyGuard) heredada del backend. Las únicas escrituras 'con",
|
|
42
|
-
"olor a plata' son configuraciones de TARIFA (colecta_configurar, precio_",
|
|
43
|
-
"actualizar, afiliacion_crear) o el REGISTRO de una obligación (comision_liquidar",
|
|
44
|
-
"marca comisiones ya devengadas como pagadas; ruta_adjudicar deja el precio",
|
|
45
|
-
"acordado). Ver tema facturacion_marketplace.",
|
|
46
|
-
].join("\n"),
|
|
47
|
-
|
|
48
|
-
aislamiento: [
|
|
49
|
-
"AISLAMIENTO ENTRE NODOS",
|
|
50
|
-
"",
|
|
51
|
-
"Cada token de MCP pertenece a un usuario. Todos los tools llaman a la API REST",
|
|
52
|
-
"del backend con ese token, así que heredan el MISMO gateo que la web:",
|
|
53
|
-
"requireAuth + requirePermiso + scope por nodo/idCliente. Un vendedor solo ve",
|
|
54
|
-
"SUS envíos/stock; un operador solo su nodo; el admin global ve la red. Ningún",
|
|
55
|
-
"tool puede leer ni escribir datos de otro nodo (salvo lo que es público por",
|
|
56
|
-
"diseño, como el marketplace de rutas abiertas). Si un tool devuelve 403 es el",
|
|
57
|
-
"aislamiento del backend, no un bug.",
|
|
58
|
-
].join("\n"),
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
// Guías de arranque por rol (onboarding). Se muestran al conectar (server
|
|
62
|
-
// `instructions`) y con el tool `guia`. Metodología para empezar a usar el sistema
|
|
63
|
-
// rápido y bien, con las herramientas concretas de cada paso.
|
|
64
|
-
export const GUIAS = {
|
|
65
|
-
mensajero: [
|
|
66
|
-
"GUÍA DEL MENSAJERO — registrar lo que colectás, en el momento",
|
|
67
|
-
"",
|
|
68
|
-
"La idea: cuando pasás a colectar a un cliente, en vez de anotar a mano, me",
|
|
69
|
-
"mandás las FOTOS y yo cargo los envíos por vos. Paso a paso:",
|
|
70
|
-
"",
|
|
71
|
-
"1) Sacá una FOTO de cada paquete que agarrás: la etiqueta (Flex/Mercado Libre",
|
|
72
|
-
" o la que sea) y, si tiene QR, que se vea. Si el paquete NO tiene etiqueta,",
|
|
73
|
-
" sacá foto igual (datos del paquete: a quién va, dirección, teléfono).",
|
|
74
|
-
"2) Mandámelas todas juntas (un grupo de fotos). Yo leo cada una y registro el",
|
|
75
|
-
" envío del cliente que estás colectando:",
|
|
76
|
-
" • Con etiqueta ML / QR → lo registro con `envio_desde_etiqueta_ml`",
|
|
77
|
-
" (guardo el QR real, la etiqueta queda reimprimible).",
|
|
78
|
-
" • Sin etiqueta, con datos → lo registro con `envio_cargar` como pedido",
|
|
79
|
-
" manual y se le puede generar una etiqueta NUEVA para pegar.",
|
|
80
|
-
"3) IMPORTANTE (aislamiento): SOLO podés registrar envíos de clientes de TU",
|
|
81
|
-
" nodo, o de un cliente que tengas COLECTADO (incluye colectas tomadas del",
|
|
82
|
-
" marketplace de colectas). De otros clientes, no.",
|
|
83
|
-
"4) Cada envío queda 'A retirar', atribuido a vos, con un LINK de etiqueta",
|
|
84
|
-
" imprimible. El vendedor lo ve al toque en su cuenta.",
|
|
85
|
-
"5) Al llegar al nodo, el centro procesa los envíos; a los que cargaste sin",
|
|
86
|
-
" etiqueta se les pega una y salen normal. Después se escanean, aparecen en",
|
|
87
|
-
" el mapa y se te suma la tarifa como cualquier reparto.",
|
|
88
|
-
"",
|
|
89
|
-
"Tips: si una etiqueta se rompió o no se lee, cargá el envío con `envio_cargar`",
|
|
90
|
-
"y generás una nueva. Consultá el estado de un envío con `envio_consultar`, tu",
|
|
91
|
-
"ruta con `mi_ruta` y tus colectas con `mis_colectas`.",
|
|
92
|
-
"(Próximamente: aviso en la app para imprimir de una todas las etiquetas que",
|
|
93
|
-
"cargaste por acá.)",
|
|
94
|
-
].join("\n"),
|
|
95
|
-
|
|
96
|
-
cliente: [
|
|
97
|
-
"GUÍA DEL VENDEDOR — cargá tus ventas y seguí tu operación desde acá",
|
|
98
|
-
"",
|
|
99
|
-
"1) Cargar una venta/envío: `envio_cargar` con destinatario, teléfono,",
|
|
100
|
-
" dirección y localidad (montoCobro si cobrás contra entrega). Te devuelvo",
|
|
101
|
-
" el tracking + un link de etiqueta para imprimir + un link para subir una",
|
|
102
|
-
" foto del paquete.",
|
|
103
|
-
"2) ¿Vendés por Mercado Libre? Mandame la foto de la etiqueta y la registro",
|
|
104
|
-
" con `envio_desde_etiqueta_ml` (queda con el QR real).",
|
|
105
|
-
"3) Colecta (que te retiren): `mi_colecta` para ver cómo estás, `colecta_auto`",
|
|
106
|
-
" para prender/apagar la automática, `colecta_solicitar` para una por única vez.",
|
|
107
|
-
"4) Tu negocio: `mi_stock` / `mi_disponible` (para no sobrevender), ",
|
|
108
|
-
" `mi_rentabilidad`, `mis_top_productos`, `mis_kpis`. Sucursales de retiro con",
|
|
109
|
-
" `mis_sucursales` / `sucursal_guardar`.",
|
|
110
|
-
"",
|
|
111
|
-
"Todo lo tuyo es solo tuyo (aislamiento). Nunca toco dinero por acá.",
|
|
112
|
-
].join("\n"),
|
|
113
|
-
|
|
114
|
-
nodo: [
|
|
115
|
-
"GUÍA DEL NODO (operador/admin) — implementar y operar Nexus Flex rápido",
|
|
116
|
-
"",
|
|
117
|
-
"Puesta a punto:",
|
|
118
|
-
" • Vendedores: `cliente_crear` / `cliente_editar` (asigná su lista de precio).",
|
|
119
|
-
" • Choferes: `chofer_crear` (te devuelvo una clave temporal para pasarle).",
|
|
120
|
-
" • Colecta: `colecta_configurar` (pago del nodo/mensajero, cobro por vendedor).",
|
|
121
|
-
" • Zonas: `asignar_mensajero_zona` (mensajero↔metazona), `asignar_nodo_zona`",
|
|
122
|
-
" (un nodo de tu grupo cubre una localidad).",
|
|
123
|
-
"",
|
|
124
|
-
"El día a día:",
|
|
125
|
-
" • `colecta_pendientes` (hoy + mañana) y `colecta_asignar` (que Fulano levante",
|
|
126
|
-
" a tal cliente).",
|
|
127
|
-
" • Envíos: los cadetes pueden cargar lo que colectan por MCP (foto→envío); vos",
|
|
128
|
-
" los ves 'A retirar'. Consultá con `envio_consultar`, corregí con",
|
|
129
|
-
" `cobro_corregir` / `envio_reasignar_cliente`.",
|
|
130
|
-
" • Rendiciones: `rendiciones` (qué falta recuperar/rendir).",
|
|
131
|
-
"",
|
|
132
|
-
"Clearing entre nodos (eficiente):",
|
|
133
|
-
" • Metazonas + grupos logísticos rutean los envíos al nodo que cubre la zona.",
|
|
134
|
-
" Recibís lo que te rutean con `envios_por_zona` → `procesar_zona` (aceptar) o",
|
|
135
|
-
" `rechazar_zona`. El clearing sale de la config de tarifas por grupo.",
|
|
136
|
-
" • Marketplace de rutas (`rutas_publicas`, `ruta_publicar`/`ofertar`/`adjudicar`):",
|
|
137
|
-
" subasta abierta entre nodos. OJO: adjudicar NO factura automático — el pago",
|
|
138
|
-
" entre nodos se salda a mano (ver `ayuda tema:facturacion_marketplace`).",
|
|
139
|
-
" • Números: `kpi_nodo` (P&L, top clientes), `metricas_por_tipo`.",
|
|
140
|
-
"",
|
|
141
|
-
"Aislamiento: todo lo que ves/hacés es de TU nodo (o tu grupo cuando corresponde).",
|
|
142
|
-
].join("\n"),
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
//
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
}
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Documentación integrada del MCP de Nexus Flex — fuente de verdad de la ayuda
|
|
3
|
+
// por herramienta (tool `ayuda`). Cada tool trae: qué hace, cómo usarlo (params),
|
|
4
|
+
// un ejemplo resuelto y qué NO hace (para no inferir comportamiento inexistente).
|
|
5
|
+
//
|
|
6
|
+
// ⚠️ ESPEJO: este archivo está DUPLICADO en `backend/src/services/mcp-docs.ts`
|
|
7
|
+
// (el conector remoto se deploya sin la carpeta mcp/). Si editás uno, editá el
|
|
8
|
+
// otro — igual que MCP_VERSION/NOVEDADES. Mantené el contenido idéntico.
|
|
9
|
+
// ============================================================================
|
|
10
|
+
|
|
11
|
+
// Tópicos transversales (no son tools) — se consultan con `ayuda tema:<clave>`.
|
|
12
|
+
export const TOPICS = {
|
|
13
|
+
facturacion_marketplace: [
|
|
14
|
+
"FACTURACIÓN DEL MARKETPLACE DE RUTAS PÚBLICAS",
|
|
15
|
+
"",
|
|
16
|
+
"Adjudicar una ruta (ruta_adjudicar) es OPERATIVO: cierra la subasta, marca el",
|
|
17
|
+
"nodo ganador (nodoToma) y deja registrada la OBLIGACIÓN al precio de la oferta",
|
|
18
|
+
"(precioAcordado). NO crea ningún asiento automático de facturación semanal, ni",
|
|
19
|
+
"en rendiciones, ni en comisiones de afiliado, ni en el clearing entre nodos.",
|
|
20
|
+
"El marketplace NO mueve dinero.",
|
|
21
|
+
"",
|
|
22
|
+
"Es decir: adjudicar ≠ colecta. Una colecta entre nodos sí genera un ítem de",
|
|
23
|
+
"clearing; una ruta adjudicada, NO. El pago entre el nodo que publica y el que",
|
|
24
|
+
"toma se acuerda y se salda MANUALMENTE (efectivo con el cadete, o descontándolo",
|
|
25
|
+
"de lo que un nodo ya le debe al otro por otros conceptos).",
|
|
26
|
+
"",
|
|
27
|
+
"Cómo verificarlo desde el MCP:",
|
|
28
|
+
" • mis_rutas → estado 'adjudicada', nodoToma y precioAcordado de cada ruta tuya.",
|
|
29
|
+
" • rendiciones / comisiones_afiliado_ver → NO listarán la ruta adjudicada.",
|
|
30
|
+
"",
|
|
31
|
+
"Qué falta (roadmap, todavía NO implementado): un settlement automático que,",
|
|
32
|
+
"al adjudicar, cargue el precioAcordado en la cuenta corriente entre los dos",
|
|
33
|
+
"nodos. Mientras no exista, tratá el pago como manual y avisáselo al usuario.",
|
|
34
|
+
].join("\n"),
|
|
35
|
+
|
|
36
|
+
dinero: [
|
|
37
|
+
"DINERO — QUÉ NO HACE NUNCA EL MCP",
|
|
38
|
+
"",
|
|
39
|
+
"El MCP nunca mueve plata: no registra pagos de vendedores, no factura, no",
|
|
40
|
+
"concilia caja, no imputa cobros/haberes en cuentas corrientes. Hay una denylist",
|
|
41
|
+
"de dinero (mcpMoneyGuard) heredada del backend. Las únicas escrituras 'con",
|
|
42
|
+
"olor a plata' son configuraciones de TARIFA (colecta_configurar, precio_",
|
|
43
|
+
"actualizar, afiliacion_crear) o el REGISTRO de una obligación (comision_liquidar",
|
|
44
|
+
"marca comisiones ya devengadas como pagadas; ruta_adjudicar deja el precio",
|
|
45
|
+
"acordado). Ver tema facturacion_marketplace.",
|
|
46
|
+
].join("\n"),
|
|
47
|
+
|
|
48
|
+
aislamiento: [
|
|
49
|
+
"AISLAMIENTO ENTRE NODOS",
|
|
50
|
+
"",
|
|
51
|
+
"Cada token de MCP pertenece a un usuario. Todos los tools llaman a la API REST",
|
|
52
|
+
"del backend con ese token, así que heredan el MISMO gateo que la web:",
|
|
53
|
+
"requireAuth + requirePermiso + scope por nodo/idCliente. Un vendedor solo ve",
|
|
54
|
+
"SUS envíos/stock; un operador solo su nodo; el admin global ve la red. Ningún",
|
|
55
|
+
"tool puede leer ni escribir datos de otro nodo (salvo lo que es público por",
|
|
56
|
+
"diseño, como el marketplace de rutas abiertas). Si un tool devuelve 403 es el",
|
|
57
|
+
"aislamiento del backend, no un bug.",
|
|
58
|
+
].join("\n"),
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// Guías de arranque por rol (onboarding). Se muestran al conectar (server
|
|
62
|
+
// `instructions`) y con el tool `guia`. Metodología para empezar a usar el sistema
|
|
63
|
+
// rápido y bien, con las herramientas concretas de cada paso.
|
|
64
|
+
export const GUIAS = {
|
|
65
|
+
mensajero: [
|
|
66
|
+
"GUÍA DEL MENSAJERO — registrar lo que colectás, en el momento",
|
|
67
|
+
"",
|
|
68
|
+
"La idea: cuando pasás a colectar a un cliente, en vez de anotar a mano, me",
|
|
69
|
+
"mandás las FOTOS y yo cargo los envíos por vos. Paso a paso:",
|
|
70
|
+
"",
|
|
71
|
+
"1) Sacá una FOTO de cada paquete que agarrás: la etiqueta (Flex/Mercado Libre",
|
|
72
|
+
" o la que sea) y, si tiene QR, que se vea. Si el paquete NO tiene etiqueta,",
|
|
73
|
+
" sacá foto igual (datos del paquete: a quién va, dirección, teléfono).",
|
|
74
|
+
"2) Mandámelas todas juntas (un grupo de fotos). Yo leo cada una y registro el",
|
|
75
|
+
" envío del cliente que estás colectando:",
|
|
76
|
+
" • Con etiqueta ML / QR → lo registro con `envio_desde_etiqueta_ml`",
|
|
77
|
+
" (guardo el QR real, la etiqueta queda reimprimible).",
|
|
78
|
+
" • Sin etiqueta, con datos → lo registro con `envio_cargar` como pedido",
|
|
79
|
+
" manual y se le puede generar una etiqueta NUEVA para pegar.",
|
|
80
|
+
"3) IMPORTANTE (aislamiento): SOLO podés registrar envíos de clientes de TU",
|
|
81
|
+
" nodo, o de un cliente que tengas COLECTADO (incluye colectas tomadas del",
|
|
82
|
+
" marketplace de colectas). De otros clientes, no.",
|
|
83
|
+
"4) Cada envío queda 'A retirar', atribuido a vos, con un LINK de etiqueta",
|
|
84
|
+
" imprimible. El vendedor lo ve al toque en su cuenta.",
|
|
85
|
+
"5) Al llegar al nodo, el centro procesa los envíos; a los que cargaste sin",
|
|
86
|
+
" etiqueta se les pega una y salen normal. Después se escanean, aparecen en",
|
|
87
|
+
" el mapa y se te suma la tarifa como cualquier reparto.",
|
|
88
|
+
"",
|
|
89
|
+
"Tips: si una etiqueta se rompió o no se lee, cargá el envío con `envio_cargar`",
|
|
90
|
+
"y generás una nueva. Consultá el estado de un envío con `envio_consultar`, tu",
|
|
91
|
+
"ruta con `mi_ruta` y tus colectas con `mis_colectas`.",
|
|
92
|
+
"(Próximamente: aviso en la app para imprimir de una todas las etiquetas que",
|
|
93
|
+
"cargaste por acá.)",
|
|
94
|
+
].join("\n"),
|
|
95
|
+
|
|
96
|
+
cliente: [
|
|
97
|
+
"GUÍA DEL VENDEDOR — cargá tus ventas y seguí tu operación desde acá",
|
|
98
|
+
"",
|
|
99
|
+
"1) Cargar una venta/envío: `envio_cargar` con destinatario, teléfono,",
|
|
100
|
+
" dirección y localidad (montoCobro si cobrás contra entrega). Te devuelvo",
|
|
101
|
+
" el tracking + un link de etiqueta para imprimir + un link para subir una",
|
|
102
|
+
" foto del paquete.",
|
|
103
|
+
"2) ¿Vendés por Mercado Libre? Mandame la foto de la etiqueta y la registro",
|
|
104
|
+
" con `envio_desde_etiqueta_ml` (queda con el QR real).",
|
|
105
|
+
"3) Colecta (que te retiren): `mi_colecta` para ver cómo estás, `colecta_auto`",
|
|
106
|
+
" para prender/apagar la automática, `colecta_solicitar` para una por única vez.",
|
|
107
|
+
"4) Tu negocio: `mi_stock` / `mi_disponible` (para no sobrevender), ",
|
|
108
|
+
" `mi_rentabilidad`, `mis_top_productos`, `mis_kpis`. Sucursales de retiro con",
|
|
109
|
+
" `mis_sucursales` / `sucursal_guardar`.",
|
|
110
|
+
"",
|
|
111
|
+
"Todo lo tuyo es solo tuyo (aislamiento). Nunca toco dinero por acá.",
|
|
112
|
+
].join("\n"),
|
|
113
|
+
|
|
114
|
+
nodo: [
|
|
115
|
+
"GUÍA DEL NODO (operador/admin) — implementar y operar Nexus Flex rápido",
|
|
116
|
+
"",
|
|
117
|
+
"Puesta a punto:",
|
|
118
|
+
" • Vendedores: `cliente_crear` / `cliente_editar` (asigná su lista de precio).",
|
|
119
|
+
" • Choferes: `chofer_crear` (te devuelvo una clave temporal para pasarle).",
|
|
120
|
+
" • Colecta: `colecta_configurar` (pago del nodo/mensajero, cobro por vendedor).",
|
|
121
|
+
" • Zonas: `asignar_mensajero_zona` (mensajero↔metazona), `asignar_nodo_zona`",
|
|
122
|
+
" (un nodo de tu grupo cubre una localidad).",
|
|
123
|
+
"",
|
|
124
|
+
"El día a día:",
|
|
125
|
+
" • `colecta_pendientes` (hoy + mañana) y `colecta_asignar` (que Fulano levante",
|
|
126
|
+
" a tal cliente).",
|
|
127
|
+
" • Envíos: los cadetes pueden cargar lo que colectan por MCP (foto→envío); vos",
|
|
128
|
+
" los ves 'A retirar'. Consultá con `envio_consultar`, corregí con",
|
|
129
|
+
" `cobro_corregir` / `envio_reasignar_cliente`.",
|
|
130
|
+
" • Rendiciones: `rendiciones` (qué falta recuperar/rendir).",
|
|
131
|
+
"",
|
|
132
|
+
"Clearing entre nodos (eficiente):",
|
|
133
|
+
" • Metazonas + grupos logísticos rutean los envíos al nodo que cubre la zona.",
|
|
134
|
+
" Recibís lo que te rutean con `envios_por_zona` → `procesar_zona` (aceptar) o",
|
|
135
|
+
" `rechazar_zona`. El clearing sale de la config de tarifas por grupo.",
|
|
136
|
+
" • Marketplace de rutas (`rutas_publicas`, `ruta_publicar`/`ofertar`/`adjudicar`):",
|
|
137
|
+
" subasta abierta entre nodos. OJO: adjudicar NO factura automático — el pago",
|
|
138
|
+
" entre nodos se salda a mano (ver `ayuda tema:facturacion_marketplace`).",
|
|
139
|
+
" • Números: `kpi_nodo` (P&L, top clientes), `metricas_por_tipo`.",
|
|
140
|
+
"",
|
|
141
|
+
"Aislamiento: todo lo que ves/hacés es de TU nodo (o tu grupo cuando corresponde).",
|
|
142
|
+
].join("\n"),
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// Intro motivador (engancha antes de la guía del rol): que den ganas de hacerlo.
|
|
146
|
+
const INTRO = [
|
|
147
|
+
"🚀 Bienvenido a Nexus Flex — tu operación entera desde el chat.",
|
|
148
|
+
"",
|
|
149
|
+
"Esto no es un menú más: le hablás y se hace. En 5 minutos podés dar de alta",
|
|
150
|
+
"clientes y choferes, cargar envíos sacando una FOTO (yo leo la etiqueta), ver tu",
|
|
151
|
+
"plata al día, armar zonas, y hasta tomar rutas de otros nodos de la red. Sin",
|
|
152
|
+
"planillas, sin clicks eternos. Cada cosa que hacés acá queda LIVE al instante y",
|
|
153
|
+
"aislada a lo tuyo (nadie ve lo de otro nodo).",
|
|
154
|
+
"",
|
|
155
|
+
"Hacé la prueba: seguí los pasos de abajo, son 3 minutos y te va a quedar clarísimo",
|
|
156
|
+
"todo lo que podés automatizar. ¿Listo? Arrancamos 👇",
|
|
157
|
+
"",
|
|
158
|
+
"════════════════════════════════════════",
|
|
159
|
+
"",
|
|
160
|
+
].join("\n");
|
|
161
|
+
|
|
162
|
+
// Devuelve la guía de arranque para un rol (mensajero/cliente/operador/admin).
|
|
163
|
+
export function guiaOnboarding(rol) {
|
|
164
|
+
const cuerpo =
|
|
165
|
+
rol === "mensajero" ? GUIAS.mensajero :
|
|
166
|
+
rol === "cliente" ? GUIAS.cliente :
|
|
167
|
+
rol === "operador" || rol === "admin" ? GUIAS.nodo :
|
|
168
|
+
[GUIAS.cliente, "", "— — —", "", GUIAS.nodo].join("\n");
|
|
169
|
+
return INTRO + cuerpo;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Orden y agrupación para el índice (solo se muestran los que el usuario tiene).
|
|
173
|
+
export const GROUPS = [
|
|
174
|
+
{ titulo: "General", tools: ["mis_datos", "guia", "flujo", "flujo_registrar", "ayuda", "mcp_version", "sugerencia_crear", "mis_sugerencias"] },
|
|
175
|
+
{ titulo: "Vendedor — mi operación", tools: ["mis_envios", "envio_consultar", "mis_sucursales", "sucursal_guardar", "mi_colecta", "colecta_solicitar", "colecta_auto"] },
|
|
176
|
+
{ titulo: "Vendedor — mi stock y números", tools: ["mi_stock", "mi_disponible", "mi_rentabilidad", "mis_productos", "mis_top_productos", "mis_kpis"] },
|
|
177
|
+
{ titulo: "Mensajero", tools: ["mi_ruta", "mis_colectas"] },
|
|
178
|
+
{ titulo: "Envíos (staff)", tools: ["envio_cargar", "envio_desde_etiqueta_ml", "envio_procesar", "envio_entregar", "envio_estado", "envios_trabados", "planilla_reporte", "envio_asignar_grupo", "nodo_provisorio_crear", "provisorio_conciliar", "envio_reasignar_cliente", "envio_mensajero_externo", "envio_editar_zona", "envio_editar_fecha", "cobro_corregir"] },
|
|
179
|
+
{ titulo: "Colecta y zonas (staff)", tools: ["colecta_ver", "colecta_pendientes", "colecta_historial", "colecta_configurar", "colecta_fija", "colecta_asignar", "retiros_cargar", "colecta_desasignar", "zonas_reparto", "zona_barrios", "zonas_simetria", "asignar_mensajero_zona", "asignar_nodo_zona", "envios_por_zona", "procesar_zona", "rechazar_zona"] },
|
|
180
|
+
{ titulo: "Rendiciones y reclamos", tools: ["rendiciones", "rendicion_revertir", "reclamos_listar"] },
|
|
181
|
+
{ titulo: "Marketplace de rutas", tools: ["rutas_publicas", "mis_rutas", "ruta_ofertas", "ruta_publicar", "ruta_ofertar", "ruta_adjudicar"] },
|
|
182
|
+
{ titulo: "Afiliados", tools: ["afiliacion_listar", "comisiones_afiliado_ver", "afiliado_crear", "afiliacion_crear", "afiliacion_editar", "comision_liquidar"] },
|
|
183
|
+
{ titulo: "Métricas / KPIs", tools: ["kpi_nodo", "kpi_red", "metricas_por_tipo", "top_productos"] },
|
|
184
|
+
{ titulo: "WMS del nodo", tools: ["stock_nodo", "productos_nodo", "producto_crear"] },
|
|
185
|
+
{ titulo: "Gestión del nodo", tools: ["clientes_del_nodo", "cliente_crear", "cliente_editar", "sucursales_cliente", "sucursal_perfil", "precios_ver", "precio_actualizar", "cuentas_vinculadas", "generar_enlace_vinculacion"] },
|
|
186
|
+
{ titulo: "Marketing / atribución", tools: ["marketing_config_ver", "marketing_config_guardar"] },
|
|
187
|
+
{ titulo: "Liquidaciones (consulta)", tools: ["liquidacion_buscar", "liquidacion_detalle", "liquidaciones_sin_recibir"] },
|
|
188
|
+
{ titulo: "Usuarios / choferes", tools: ["chofer_listar", "chofer_crear", "chofer_editar", "usuario_editar", "operador_crear", "cliente_generar_usuario", "cliente_resetear_clave", "usuario_habilitar_reparto"] },
|
|
189
|
+
{ titulo: "Administración (red)", tools: ["nodos_listar", "nodo_crear", "usuario_habilitar_rol", "grupo_crear", "logo_subir", "sugerencias_listar"] },
|
|
190
|
+
];
|
|
191
|
+
|
|
192
|
+
// Doc por herramienta: que=qué hace, uso=cómo/params, ej=ejemplo resuelto, no=qué NO hace.
|
|
193
|
+
export const TOOL_DOCS = {
|
|
194
|
+
facturacion_estado: { que: "Diagnóstico de facturación ARCA: emisores del nodo y si están completos, cuentas de dinero sin emisor propio, y por cada cliente qué dato le falta para poder facturarle.", uso: "Sin argumentos = los clientes YA habilitados. cliente:<nombre|id> = ese cliente, aunque todavía no esté habilitado.", ej: "facturacion_estado cliente:Segucentro → te dice si le falta CUIT, razón social, condición IVA, emisor o la habilitación.", no: "No devuelve importes ni factura nada. Es estado de configuración." },
|
|
195
|
+
facturacion_preparar_cliente: { que: "Deja un cliente listo para facturar: CUIT, razón social (el titular del CUIT), condición IVA, cuenta donde cobra (define QUIÉN factura), frecuencia y corte.", uso: "cliente, cuit, razonSocial y condicionIVA son obligatorios; cuentaCobro, frecuencia (Semanal|Mensual) y desde (YYYY-MM-DD) son opcionales. El corte por defecto es HOY.", ej: "facturacion_preparar_cliente cliente:'Digital Store' cuit:30714508160 razonSocial:'DIGITAL STORE' condicionIVA:RI cuentaCobro:'UALA ElMaxzito' frecuencia:Semanal", no: "NO emite comprobantes (el MCP no factura) ni hace el trámite en ARCA: eso necesita la Clave Fiscal de esa persona y se hace en la web de ARCA." },
|
|
196
|
+
ayuda: { que: "Devuelve esta documentación: índice de herramientas o la ficha completa de una (qué hace, params, ejemplo, qué NO hace) y tópicos transversales.", uso: "Sin argumentos = índice. `tool:<nombre>` = ficha de esa herramienta. `tema:<clave>` = tópico (facturacion_marketplace, dinero, aislamiento).", ej: "ayuda tool:ruta_adjudicar → te explica cómo adjudicar y aclara que no mueve dinero.", no: "No ejecuta nada ni consulta datos; es solo lectura de documentación estática." },
|
|
197
|
+
guia: { que: "Guía de ARRANQUE para tu rol: la metodología para empezar a usar Nexus Flex rápido, con las herramientas de cada paso (mensajero: foto→envío al colectar; vendedor: cargar ventas; nodo: implementar + operar + clearing).", uso: "Sin argumentos = tu guía según tu rol. `rol:<mensajero|cliente|nodo>` para ver otra.", ej: "Recién conectás como mensajero → guia → te explico cómo registrar lo que colectás sacando fotos.", no: "No ejecuta acciones; es la metodología. Para el detalle de una herramienta usá `ayuda tool:<x>`." },
|
|
198
|
+
flujo: { que: "Paso a paso de un OBJETIVO para completarlo sin dejar nada incompleto (ej. alta de cliente con su lista de precios). Sirve para guiar a alguien que no sabe qué datos faltan.", uso: "Sin argumento = lista los flujos. `objetivo:<alta_cliente|alta_operador|alta_chofer>` = el paso a paso.", ej: "flujo alta_cliente → te digo que después de crearlo hay que preguntar qué lista de precios le cobrás.", no: "No ejecuta las acciones; te da la secuencia. Cada paso usa su propia herramienta." },
|
|
199
|
+
mis_datos: { que: "Tu usuario, rol y nodo/cliente: define tu ALCANCE (qué podés ver/hacer).", uso: "Sin parámetros.", ej: "Antes de operar, mis_datos → confirmás que sos operador del nodo 6 y qué permisos tenés.", no: "No lista otros usuarios ni cambia nada." },
|
|
200
|
+
mcp_version: { que: "Versión del MCP corriendo, por qué vía (remoto/npx) y las novedades recientes.", uso: "Sin parámetros.", ej: "El usuario pregunta 'tengo lo último' → mcp_version.", no: "No actualiza el MCP." },
|
|
201
|
+
sugerencia_crear: { que: "Registra una sugerencia/mejora/bug del usuario (queda para el equipo).", uso: "mensaje (obligatorio); categoria opcional (funcionalidad|mejora|bug|otro). Se adjunta tu usuario/rol/nodo solo.", ej: "sugerencia_crear mensaje:'Sumar recordatorio de colecta' categoria:mejora.", no: "No abre tickets externos ni notifica por mail." },
|
|
202
|
+
mis_sugerencias: { que: "Tus PROPIAS sugerencias/pedidos de mejora y en qué estado están (Recibida / En evaluación / ✅ Implementada / Descartada). Cada usuario ve solo las suyas.", uso: "Sin argumentos.", ej: "mis_sugerencias → ves cuáles de tus pedidos ya se aplicaron.", no: "No muestra las de otros usuarios (eso es `sugerencias_listar`, solo staff); no cambia su estado." },
|
|
203
|
+
flujo_registrar: { que: "Registra un 'flujo aprendido': cuando el asistente tuvo que hacer VARIAS preguntas para descubrir qué querías, guarda el objetivo + los pasos que lo aclararon, para armar un flujo directo.", uso: "objetivo + pasos. Lo llama el asistente solo cuando corresponde.", ej: "flujo_registrar objetivo:'cargar 5 paquetes ML de un nodo nuevo' pasos:'preguntó de quién eran → nodo no estaba → creó provisorio → cargó reusando QR'.", no: "No crea el flujo automáticamente (lo revisa el equipo); no mueve dinero." },
|
|
204
|
+
|
|
205
|
+
mis_envios: { que: "Tus envíos/paquetes (solo los tuyos).", uso: "estado opcional (filtra por estado).", ej: "mis_envios estado:'En camino' → los que están en reparto.", no: "No muestra envíos de otros vendedores; no los edita." },
|
|
206
|
+
envio_consultar: { que: "Busca UN envío por tracking/código y devuelve estado, historial, destino y datos de cobro/cambio.", uso: "codigo (tracking o código). Vendedor: entre SUS envíos; staff: dentro de su nodo. Si hay varios matches, pasá el tracking completo.", ej: "El vendedor pregunta '¿dónde está 44000...?' → envio_consultar codigo:'44000123'.", no: "No cambia el estado del envío; no busca fuera de tu alcance." },
|
|
207
|
+
envio_estado: { que: "Narrativa de estado + ETA de un envío para responder '¿cuándo llega?': mensajero asignado, cuántos envíos lleva en la ruta, en qué posición va este y un estimado (posición × min/parada). Marca problemas (nunca despachado, sin mensajero, mensajero detenido).", uso: "tracking. Staff y mensajero, scopeado a tu nodo. Es una estimación explicable, no exacta.", ej: "El vendedor pregunta '¿cuándo entregan NFABC?' → envio_estado tracking:'NFABC' → 'En camino con Maxi, 8 envíos en ruta, este va 3º, ~24 min'.", no: "No cambia nada; la ETA es orientativa." },
|
|
208
|
+
envios_trabados: { que: "Lista los envíos TRABADOS de tu nodo con el motivo: estancado (mucho tiempo sin avanzar), sin_mensajero (en el centro sin asignar), mensajero_detenido (en camino, sin reportar ubicación) o en_camino_sin_cerrar (en camino +24h sin cerrarse → marcalo entregado).", uso: "Sin parámetros. Staff. Ordenados por severidad. Misma detección que el aviso proactivo a operativos y admins del nodo.", ej: "El jefe pregunta '¿qué quedó trabado / por cerrar hoy?' → envios_trabados.", no: "Solo lo detecta/lista; no lo destraba ni lo marca entregado (eso lo hacés en la app / escáner)." },
|
|
209
|
+
planilla_reporte: { que: "Resumen de los envíos cargados por el método de planilla/control por foto (ml_manual): total + desglose por cliente, zona y estado + suma de valor declarado y de monto a cobrar.", uso: "Opcional desde/hasta (YYYY-MM-DD). Staff, scopeado a tu nodo.", ej: "planilla_reporte desde:'2026-09-01' → cuántos cargaste hoy, por cliente/zona/estado.", no: "No es el clearing plata-por-nodo (para eso el reporte de clearing); solo cuenta/valores de la planilla." },
|
|
210
|
+
mis_sucursales: { que: "Tus sucursales / puntos de retiro (la principal = tu dirección de retiro), con horario de corte y ventanas.", uso: "Sin parámetros.", ej: "mis_sucursales → confirmás desde dónde te colectan.", no: "No las edita (para eso, sucursal_guardar)." },
|
|
211
|
+
sucursal_guardar: { que: "Crea o edita una sucursal tuya (punto de retiro). Geocodifica la dirección sola.", uso: "id vacío = nueva; nombre obligatorio; principal:true la vuelve tu dirección de retiro; horarioCorte HH:MM y ventanas opcionales.", ej: "Cambiar tu retiro: sucursal_guardar id:<principal> direccion:'Av. Rivadavia 5000, CABA' principal:true.", no: "No mueve dinero; el detalle de piso/depto/timbre va aparte, no al geocoder." },
|
|
212
|
+
mi_colecta: { que: "Estado de tu colecta (retiro): si la automática está prendida, si pediste una por única vez y el aviso de costo.", uso: "Sin parámetros.", ej: "mi_colecta → ver si mañana te pasan a buscar.", no: "No la prende/apaga (colecta_auto) ni la solicita (colecta_solicitar)." },
|
|
213
|
+
colecta_solicitar: { que: "Pedís que te retiren los envíos POR ÚNICA VEZ, aunque tengas la automática apagada.", uso: "Sin parámetros. Devuelve el aviso de costo si aplica.", ej: "colecta_solicitar → aparecés en el panel de colecta del nodo.", no: "No prende la colecta automática permanente; no mueve dinero." },
|
|
214
|
+
colecta_auto: { que: "Prende (true) o apaga (false) tu colecta AUTOMÁTICA.", uso: "activa (bool).", ej: "colecta_auto activa:false → dejás de que te retiren; llevás vos al depósito.", no: "No agenda una colecta puntual (para eso colecta_solicitar)." },
|
|
215
|
+
|
|
216
|
+
mi_stock: { que: "Tu stock físico (solo tus productos).", uso: "Sin parámetros.", ej: "mi_stock → ver unidades por SKU.", no: "No muestra disponible-para-vender (usá mi_disponible)." },
|
|
217
|
+
mi_disponible: { que: "Disponible por SKU = físico − comprometido; marca ⚠️ si está bajo el mínimo.", uso: "Sin parámetros.", ej: "mi_disponible → cuántas unidades podés seguir vendiendo sin sobrevender.", no: "No repone stock." },
|
|
218
|
+
mi_rentabilidad: { que: "Margen por SKU (precio − flete real − COGS).", uso: "desde/hasta YYYY-MM-DD (default: mes en curso).", ej: "mi_rentabilidad desde:2026-08-01 → margen del mes.", no: "No incluye gastos fijos del negocio; es por SKU." },
|
|
219
|
+
mis_productos: { que: "Tu catálogo de productos.", uso: "Sin parámetros.", ej: "mis_productos → ver SKUs cargados.", no: "No da de alta (producto_crear, si el nodo tiene WMS)." },
|
|
220
|
+
mis_top_productos: { que: "Ranking de tus productos más despachados.", uso: "desde/hasta YYYY-MM-DD (default: mes).", ej: "mis_top_productos → qué se vende más.", no: "No muestra rentabilidad (usá mi_rentabilidad)." },
|
|
221
|
+
mis_kpis: { que: "Tus métricas + benchmark anónimo de tu nodo.", uso: "desde/hasta opcionales.", ej: "mis_kpis → tu tasa de entrega vs. el promedio del nodo.", no: "No revela datos de otros vendedores (el benchmark es anónimo)." },
|
|
222
|
+
|
|
223
|
+
mi_ruta: { que: "Tus entregas asignadas: las paradas de tu ruta del día.", uso: "Sin parámetros.", ej: "mi_ruta → orden de reparto de hoy.", no: "No marca entregas (eso se hace escaneando en la app)." },
|
|
224
|
+
mis_colectas: { que: "Las colectas/retiros que tenés asignados.", uso: "Sin parámetros.", ej: "mis_colectas → a qué vendedores tenés que ir a buscar.", no: "No las completa/confirma desde acá." },
|
|
225
|
+
|
|
226
|
+
envio_cargar: { que: "Registra un envío nuevo (queda 'A retirar') y devuelve tracking + link de etiqueta + link para subir foto.", uso: "Obligatorios: destinatario, telefono, direccion, localidad. Staff pasa `cliente` (nombre del vendedor de su nodo); el vendedor no. montoCobro = cobro contra entrega (opcional). `fecha` (solo staff, dd/mm/aaaa) si el envío NO es de hoy: es la que define en qué semana se liquida; no se admite futura.", ej: "Staff: envio_cargar cliente:'Distri Sur' destinatario:'Ana' telefono:'11...' direccion:'Belgrano 100' localidad:'Lanús' montoCobro:15000.", no: "No mueve dinero (montoCobro es el cobro a destino, no un asiento); no imprime, devuelve el link. Un vendedor/cadete NO puede pasar `fecha` (movería sus paquetes de período de liquidación)." },
|
|
227
|
+
envio_procesar: { que: "Marca un envío como PROCESADO / recibido en el centro de distribución (queda 'En centro de distribución') por su tracking, sin escanear.", uso: "tracking del envío. Scopeado a tu nodo/grupo (solo procesás lo que te corresponde). Es la misma acción 'procesar' del escáner.", ej: "envio_procesar tracking:'NFABC123' → lo marca recibido en el centro.", no: "No mueve dinero (registra el tramo de clearing del grupo, como procesar_zona); no lo entrega ni lo asigna a un cadete." },
|
|
228
|
+
envio_entregar: { que: "Marca como ENTREGADO uno o varios envíos que YA están cargados, por tracking — y con `fecha`, el día REAL en que se entregó.", uso: "trackings: ['TN-123', 'NFABC123'] + fecha (opcional, dd/mm/aaaa). Es la contraparte de avanzarA:'entregado' de envio_desde_etiqueta_ml, que SOLO corre en altas nuevas: si el paquete ya entró por otro lado (lo cargó el vendedor o una integración ML/TiendaNube) ese parámetro se ignora y el envío queda 'A retirar'. Sin `fecha` el historial dice que se entregó ahora, que es mentira cuando la tanda se controla por foto días después; sobre un envío que YA figura entregado, `fecha` CORRIGE el cierre ya registrado (vuelve en `fechaCorregida`). Entrega solo con los datos (sin foto ni firma). Scopeado a tu nodo.", ej: "envio_entregar trackings:['TN-2057600163'] fecha:'02/09/2026' → lo cierra con el día en que salió y sella la logística de entrega para el clearing.", no: "No mueve dinero; `fecha` toca el evento 'Entregado' del historial, NO la semana en que se liquida (eso es envio_editar_fecha); no pide foto ni firma (para eso está la entrega del cadete en la PWA); no revierte una entrega." },
|
|
229
|
+
envio_desde_etiqueta_ml: { que: "Registra un envío a partir de lo que VOS (Claude) leíste de la foto de una etiqueta de Mercado Libre.", uso: "Leé la etiqueta: mlShipmentId + (si podés) mlQr crudo, mlSenderId del vendedor y el destino. El vendedor se mapea por mlSenderId (cuenta vinculada) o `cliente` por nombre. Una llamada por etiqueta. Si la planilla es de días anteriores, pasá `fecha` (solo staff, dd/mm/aaaa): fecha el envío Y toda la cadena de `avanzarA` (cada estado con su hora de ESE día: Colectado 9, En centro 12, En camino 15, Entregado 18). Sin eso todo queda con la hora del control y se liquida en la semana equivocada. Si el envío YA estaba cargado también le mueve la fecha, salvo que ese período ya esté liquidado (ahí rechaza nombrando la liquidación).", ej: "envio_desde_etiqueta_ml mlSenderId:'123456' mlShipmentId:'44000...' destinatario:'Juan' direccion:'...' localidad:'Avellaneda'.", no: "No mueve dinero; si no reconoce el vendedor por mlSenderId tenés que pasar `cliente`." },
|
|
230
|
+
envio_reasignar_cliente: { que: "Mueve UN envío (por tracking) a otro cliente/vendedor de tu nodo cuando se cargó mal.", uso: "tracking + cliente (nombre o id destino, de tu nodo). Queda en el historial.", ej: "envio_reasignar_cliente tracking:'44000...' cliente:'Comercial Norte'.", no: "No cruza nodos; no cambia el estado del envío." },
|
|
231
|
+
envio_mensajero_externo: { que: "Registra envíos hechos por un mensajero EXTERNO (alguien de afuera, sin usuario en la plataforma).", uso: "envioIds[] + nombre + valorPorEnvio (opcional). Quedan En camino a su nombre y entran en la liquidación de mensajeros. Sin valor, figuran FALTA VALOR.", ej: "envio_mensajero_externo envioIds:[123,124] nombre:\"Juan (moto)\" valorPorEnvio:3000.", no: "No reasigna envíos ya entregados; no paga (registra el valor)." },
|
|
232
|
+
envio_editar_zona: { que: "Corrige la localidad/zona/partido/CP de UN envío (por tracking) cargado con el destino mal, para que vuelva a ser cobrable/ruteable. Recalcula la zona y deja registro.", uso: "tracking + al menos uno de localidad/zona/partido/cp. Si no pasás zona, se deriva de la localidad. El cp se guarda solo con dígitos ('1.832,00' → '1832'). Staff: solo envíos de tu nodo; admin global: cualquiera.", ej: "La etiqueta traía 'san martin - Lanús' pisadas → envio_editar_zona tracking:'NFD5L122' localidad:'Lanús'. CP con la altura de la calle → envio_editar_zona tracking:'25001' cp:'1712'.", no: "No reasigna de cliente (para eso envio_reasignar_cliente); no crea un alias global (arregla SOLO ese envío); no mueve dinero." },
|
|
233
|
+
envio_editar_fecha: { que: "Corrige la FECHA de UN envío ya cargado (por tracking): el que se subió atrasado sin `fecha` quedó con el día del alta. Es la fecha que define en qué SEMANA se le liquida al vendedor.", uso: "tracking + fecha (dd/mm/aaaa o aaaa-mm-dd). No se admite futura. Si ese período ya está liquidado, lo rechaza nombrando la liquidación (rehacerla la decidís vos). El primer estado del historial se mueve con el envío; los demás quedan como se registraron. Staff: solo envíos de tu nodo; admin global: cualquiera.", ej: "Subiste el martes recién el viernes y todo quedó con fecha del viernes → envio_editar_fecha tracking:'47904719712' fecha:'01/09/2026'.", no: "No corrige el destino (para eso envio_editar_zona) ni reasigna de cliente (envio_reasignar_cliente); no rehace la liquidación; no mueve dinero." },
|
|
234
|
+
envio_asignar_grupo: { que: "Asigna/rutea UN envío (por tracking) a un GRUPO logístico tuyo (por nombre o id) y lo DESPACHA — igual que 'Asignar grupo' del escáner.", uso: "tracking + grupo (nombre o id de TUS grupos, ej. 'Portela'). Define con qué grupo salió (tarifa del clearing) y lo pasa a 'En camino' (sale del centro). Solo tus grupos y envíos de tu nodo (admin global, cualquiera).", ej: "envio_asignar_grupo tracking:'NFABC123' grupo:'Bonorino' → lo despacha por ese grupo.", no: "No mueve dinero (el clearing es config); no lo entrega ni lo asigna a un cadete puntual." },
|
|
235
|
+
cobro_corregir: { que: "Ajusta el monto a cobrar contra entrega de un envío (se cobró de más/de menos), guardando el original en el historial.", uso: "envioId + monto nuevo; motivo opcional.", ej: "cobro_corregir envioId:987 monto:12000 motivo:'lista vieja'.", no: "No mueve dinero en cuentas: corrige el DATO del envío." },
|
|
236
|
+
|
|
237
|
+
colecta_ver: { que: "Resumen de valores de colecta del nodo: pago default, cobros por cliente y pagos pactados por cliente+mensajero.", uso: "Sin parámetros.", ej: "colecta_ver → cuánto se paga/cobra por colecta.", no: "No cambia tarifas (colecta_configurar)." },
|
|
238
|
+
colecta_historial: { que: "Paquetes colectados por día y por cliente en una fecha o rango (histórico, hasta 62 días), con quién los colectó.", uso: "desde (aaaa-mm-dd), hasta opcional, cliente opcional (nombre o id). Solo tu nodo.", ej: "colecta_historial desde:2026-09-08 cliente:Libero → cuántos paquetes se le levantaron ese día.", no: "No es el panel de hoy (colecta_pendientes) ni cobra/paga colectas." },
|
|
239
|
+
colecta_pendientes: { que: "Panel de colectas del nodo: `items` = a retirar HOY (por cliente, con corte y mensajero asignado); `itemsManana` = clientes cuyos envíos entraron después del corte → van a mañana; `clientesSinEnvios` = vendedores sin envíos cargados a los que igual se los puede mandar a colectar.", uso: "Sin parámetros. Trae el colectaId para desasignar.", ej: "'¿Quién levanta a Distri Sur?' → colecta_pendientes y mirás items.", no: "No asigna (colecta_asignar) ni configura tarifas." },
|
|
240
|
+
colecta_configurar: { que: "Setea un valor de colecta según alcance.", uso: "alcance: 'nodo' (pago default por colecta), 'mensajero' (default de ese cadete), 'cliente' (cuánto se le COBRA a ese vendedor; idCliente + valor, minEnvios opcional), 'par' (pago pactado a un mensajero por un cliente puntual; idCliente + mensajero + valor). Scopeado a tu nodo. Admin global: `nodo` para elegir/acotar el nodo.", ej: "colecta_configurar alcance:cliente idCliente:'130' valor:2500 minEnvios:5 → gratis desde 5 envíos, sino $2500.", no: "No mueve dinero: es config de tarifa (el cobro se aplica al liquidar)." },
|
|
241
|
+
colecta_fija: { que: "Días en que se va a buscar SIEMPRE a un vendedor, tenga o no envíos cargados (colecta fija).", uso: "idCliente + dias (números 0-6 o nombres). dias:[] la quita. Requiere permiso gestion.", ej: "colecta_fija idCliente:\"130\" dias:[1,3,5] → lunes, miércoles y viernes.", no: "No mueve dinero (el cobro/pago de la colecta es colecta_configurar); no crea la colecta del día, hace que aparezca en el panel." },
|
|
242
|
+
colecta_asignar: { que: "Asigna la colecta de un cliente a un mensajero (ambos por nombre de tu nodo) y avisa al cadete.", uso: "cliente + mensajero (nombres). Sirve TAMBIÉN para clientes SIN envíos cargados: la colecta se crea vacía y el cadete escanea los paquetes en la puerta (se crean solos a nombre del vendedor, sin duplicar el mismo QR). Si el corte venció, la programa para el próximo día hábil. Admin global: `nodo` para elegir/acotar el nodo.", ej: "colecta_asignar cliente:'Distri Sur' mensajero:'Maxi'.", no: "No mueve dinero; no cruza nodos." },
|
|
243
|
+
retiros_cargar: { que: "Carga de una vez la LISTA DE RETIROS del día (la ronda de paradas donde el cadete va a BUSCAR). Reemplaza la carga por Excel Maestro.", uso: "cliente + direcciones[] (una por parada, pueden venir numeradas '1. Helguera 936, CABA': el número se toma como orden de ruta) + zona ('Retiro en CABA' o 'Retiro en GBA', la que cobra y paga) + mensajero para toda la ronda + fecha si no es hoy.", ej: "retiros_cargar cliente:'Fast Correo' mensajero:'Maxi' direcciones:['1. Helguera 936, CABA','2. Nepper 1273, CABA'].", no: "NO es colecta_asignar (eso es levantarle los paquetes a un vendedor). No mueve dinero. Repetir la misma dirección el mismo día no duplica." },
|
|
244
|
+
colecta_desasignar: { que: "Quita la asignación de una colecta (los envíos vuelven a 'sin colecta').", uso: "colectaId (lo devuelve colecta_pendientes). Admin global: `nodo` para elegir/acotar el nodo. Solo colectas de tu nodo.", ej: "colecta_desasignar colectaId:44.", no: "No borra los envíos; no mueve dinero." },
|
|
245
|
+
zonas_reparto: { que: "Zonas de reparto del nodo con sus metazonas y qué mensajeros tiene cada una.", uso: "Sin parámetros.", ej: "zonas_reparto → ver quién cubre Palermo.", no: "No asigna (asignar_mensajero_zona / asignar_nodo_zona)." },
|
|
246
|
+
zonas_simetria: { que: "Avisa cuando la distancia entre zonas NO es reciproca (desde A, B es cercana; pero desde B, A es lejana).", uso: "Sin parametros. Cada grupo debe declarar su 'perfil de origen' para entrar al chequeo. Devuelve asimetricos, incompletos y perfiles sin lugar.", ej: "zonas_simetria -> revisar los pares que no cierran antes de liquidar.", no: "NO corrige ni auto-completa: puede haber asimetrias legitimas (autopista, rio); la decision es humana." },
|
|
247
|
+
zona_barrios: { que: "Qué localidades y BARRIOS componen una zona con nombre (ej. 'Matanza Norte', 'CABA'), con su tramo por perfil. Consulta rápida por nombre.", uso: "zona (nombre). Devuelve las zonas visibles (propias, globales o de tus grupos logísticos) que matchean.", ej: "zona_barrios zona:'Matanza Norte' → los barrios de esa zona.", no: "No edita la zona (eso es la config de metazonas/grupos); solo lectura." },
|
|
248
|
+
asignar_mensajero_zona: { que: "Asigna un mensajero de tu nodo a una metazona; si la zona existe le suma el cadete, si no la crea.", uso: "mensajero + metazona; nombre opcional. Admin global: `nodo` para elegir/acotar el nodo.", ej: "asignar_mensajero_zona mensajero:'Maxi' metazona:'Palermo'.", no: "No cruza nodos." },
|
|
249
|
+
asignar_nodo_zona: { que: "Asigna un NODO COMPLETO (de tu grupo logístico) a una metazona, cuando ese nodo cubre toda la localidad.", uso: "nodo (nombre, debe compartir grupo logístico) + metazona; nombre opcional. Admin global: `logisticaId` para elegir el nodo dueño de la zona.", ej: "asignar_nodo_zona nodo:'RL' metazona:'Portela'.", no: "No suma nodos fuera de tu grupo logístico." },
|
|
250
|
+
envios_por_zona: { que: "Envíos que OTROS nodos te rutearon por zona de reparto, agrupados por zona (para aceptar/rechazar).", uso: "Sin parámetros. Cada envío trae su id (para procesar_zona/rechazar_zona).", ej: "envios_por_zona → ver qué te mandaron por la zona Sur.", no: "No los acepta solo: usá procesar_zona o rechazar_zona." },
|
|
251
|
+
procesar_zona: { que: "ACEPTA (recibe en tu nodo) envíos ruteados por zona.", uso: "envioIds (de envios_por_zona); mensajeroId opcional para asignarlos. Admin global: `nodo` para elegir/acotar el nodo.", ej: "procesar_zona envioIds:[101,102] mensajeroId:7.", no: "No mueve dinero (el clearing es config)." },
|
|
252
|
+
rechazar_zona: { que: "RECHAZA envíos ruteados por zona: dejan de aparecerte y quedan para origen u otros nodos de la zona.", uso: "envioIds (de envios_por_zona); motivo opcional. Admin global: `nodo` para elegir/acotar el nodo.", ej: "rechazar_zona envioIds:[103] motivo:'fuera de mi cobertura'.", no: "No cambia el estado del envío." },
|
|
253
|
+
|
|
254
|
+
rendiciones: { que: "Estado de rendiciones: cobros/cambios/devoluciones a recuperar o rendir, con totales y quién tiene cada uno.", uso: "Sin parámetros. Scopeado a tu alcance (vendedor: lo tuyo; nodo: tu nodo; mensajero: lo suyo).", ej: "rendiciones → cuánto falta que rinda cada cadete.", no: "No confirma ni revierte (rendicion_revertir); NO incluye rutas de marketplace adjudicadas (ver ayuda tema:facturacion_marketplace)." },
|
|
255
|
+
rendicion_revertir: { que: "Revierte un cobro/cambio marcado como 'rendido' por error → vuelve a 'a rendir'.", uso: "envioId + tipo (cobro|cambio); motivo opcional. Deja registro.", ej: "rendicion_revertir envioId:987 tipo:cobro motivo:'se marcó sin recibir'.", no: "No mueve dinero en cuentas; corrige el estado de la rendición." },
|
|
256
|
+
reclamos_listar: { que: "Reclamos abiertos de clientes ligados a liquidaciones/rendiciones (tipo, estado, trackings en disputa).", uso: "estado opcional (abierto|resuelto). Vendedor: lo suyo; operador: su nodo.", ej: "reclamos_listar estado:abierto → qué cobros están en disputa.", no: "No resuelve reclamos desde acá." },
|
|
257
|
+
|
|
258
|
+
rutas_publicas: { que: "Publicaciones ABIERTAS de toda la red que tu nodo puede tomar (con cuántas ofertas tiene cada una). Marca las tuyas con esMia.", uso: "Sin parámetros.", ej: "rutas_publicas → ver qué rutas hay para ofertar.", no: "No oferta (ruta_ofertar)." },
|
|
259
|
+
mis_rutas: { que: "Tus publicaciones (con estado/adjudicación: nodoToma y precioAcordado) y las ofertas que hiciste a otros.", uso: "Sin parámetros.", ej: "mis_rutas → ver si tu ruta se adjudicó y a quién.", no: "El precioAcordado es la obligación registrada, NO un asiento de facturación (ver ayuda tema:facturacion_marketplace)." },
|
|
260
|
+
ruta_ofertas: { que: "Ofertas recibidas en una publicación TUYA, de la más barata a la más cara. Solo el que publicó.", uso: "publicacionId. Los ids de oferta sirven para adjudicar.", ej: "ruta_ofertas publicacionId:12 → elegís la mejor y su ofertaId.", no: "No adjudica (ruta_adjudicar)." },
|
|
261
|
+
ruta_publicar: { que: "Publicás una ruta/colecta/viaje para que cualquier nodo la tome (subasta abierta). VOS le pagás al que la toma.", uso: "titulo (obligatorio); tipo (ruta|colecta|viaje), descripcion, zona, precioMax (tope que ofrecés pagar) opcionales.", ej: "ruta_publicar titulo:'Reparto zona Oeste 40 paquetes' tipo:ruta precioMax:20000.", no: "No mueve dinero: la obligación recién se registra al adjudicar; el pago es manual." },
|
|
262
|
+
ruta_ofertar: { que: "Ofertás por una publicación de OTRO nodo. precio = lo que cobrás por hacerla (más barato = mejor para el que publica).", uso: "publicacionId + precio; nota opcional. Si ya ofertaste, la actualiza.", ej: "ruta_ofertar publicacionId:12 precio:18000 nota:'salgo 8am'.", no: "No podés ofertar en tu propia publicación." },
|
|
263
|
+
ruta_adjudicar: { que: "Elegís la oferta ganadora de una publicación TUYA y cerrás la subasta: el nodo ganador la toma a su precio y queda registrada la obligación.", uso: "publicacionId + ofertaId (de ruta_ofertas).", ej: "ruta_adjudicar publicacionId:12 ofertaId:34 → gana el nodo de esa oferta a $18000.", no: "NO crea asiento de facturación/rendición/comisión ni clearing automático: el pago entre nodos se salda MANUALMENTE (ver ayuda tema:facturacion_marketplace)." },
|
|
264
|
+
|
|
265
|
+
afiliacion_listar: { que: "Afiliaciones (afiliado↔entidad referida) con su comisión por envío, vigencia y estado.", uso: "afiliado y/o entidad opcionales para filtrar. Scopeado a tu nodo.", ej: "afiliacion_listar afiliado:'Nodo RL'.", no: "No muestra comisiones devengadas (comisiones_afiliado_ver)." },
|
|
266
|
+
comisiones_afiliado_ver: { que: "Comisiones devengadas por envío (pendiente/liquidada) con totales.", uso: "afiliado, desde, hasta (YYYY-MM-DD) opcionales. Scopeado a tu nodo.", ej: "comisiones_afiliado_ver afiliado:'Nodo RL' desde:2026-08-01.", no: "No las paga (comision_liquidar); NO incluye rutas de marketplace." },
|
|
267
|
+
afiliado_crear: { que: "Alta de un afiliado (quien trae volumen nuevo a la red).", uso: "nombre + tipo (nodo|mensajero|externo); refId = id del nodo/mensajero (null si externo).", ej: "afiliado_crear nombre:'Juan Ref' tipo:externo.", no: "No crea la afiliación/comisión (afiliacion_crear); no toca dinero." },
|
|
268
|
+
afiliacion_crear: { que: "Vincula un afiliado con una entidad referida (nodo o cliente) y su comisión RECURRENTE por envío.", uso: "afiliado + entidadTipo (nodo|cliente) + entidad + tipoComision (porcentaje|montoFijo) + valorComision; fechaExpiracion opcional. Una entidad = un afiliado activo.", ej: "afiliacion_crear afiliado:'Juan Ref' entidadTipo:cliente entidad:'Distri Sur' tipoComision:montoFijo valorComision:50.", no: "No paga (comision_liquidar); es config, no mueve dinero." },
|
|
269
|
+
afiliacion_editar: { que: "Edita una afiliación: valorComision, fechaExpiracion (renovar/extender) y/o activa.", uso: "id + los campos a cambiar.", ej: "afiliacion_editar id:5 activa:false → la das de baja.", no: "No toca dinero." },
|
|
270
|
+
comision_liquidar: { que: "Marca comisiones devengadas como LIQUIDADAS (registra el pago al afiliado).", uso: "ids = lista de comisiones (de comisiones_afiliado_ver). Requiere permiso de finanzas. Scopeado a tu nodo.", ej: "comision_liquidar ids:[10,11,12].", no: "No calcula comisiones (se devengan solas por envío); no factura." },
|
|
271
|
+
|
|
272
|
+
kpi_nodo: { que: "Tablero del nodo: P&L, top clientes, caídas.", uso: "desde/hasta opcionales; nodo solo para admin.", ej: "kpi_nodo desde:2026-08-01 → resultado del mes.", no: "No baja a un vendedor puntual; solo tu nodo (salvo admin)." },
|
|
273
|
+
kpi_red: { que: "KPIs globales de la red.", uso: "Sin parámetros. Solo admin global.", ej: "kpi_red → panorama SaaS de toda la red.", no: "No desglosa por nodo (usá kpi_nodo con nodo)." },
|
|
274
|
+
metricas_por_tipo: { que: "Métricas por TIPO de envío (flex/tienda/manual): total, entregados, tasa y % entregado antes de las 21hs. Para admin desglosa por logística.", uso: "desde/hasta YYYY-MM-DD (default 30 días); nodo solo admin.", ej: "metricas_por_tipo → ver si los Flex se entregan a tiempo.", no: "No es por cliente." },
|
|
275
|
+
top_productos: { que: "Ranking de productos más despachados del nodo (WMS).", uso: "desde/hasta y cliente opcionales.", ej: "top_productos cliente:'Distri Sur'.", no: "Requiere WMS activo en el nodo." },
|
|
276
|
+
|
|
277
|
+
stock_nodo: { que: "Stock del depósito del nodo (WMS).", uso: "cliente opcional para filtrar.", ej: "stock_nodo cliente:'Distri Sur'.", no: "Requiere WMS activo; no repone." },
|
|
278
|
+
productos_nodo: { que: "Catálogo del depósito del nodo (WMS).", uso: "cliente opcional.", ej: "productos_nodo.", no: "Requiere WMS activo." },
|
|
279
|
+
producto_crear: { que: "Alta de producto en el depósito (WMS).", uso: "nombre + sku/codigoBarra/peso/volumen opcionales; staff pasa idCliente dueño. Admin global: `nodo` para elegir/acotar el nodo.", ej: "producto_crear nombre:'Remera M' sku:'REM-M' idCliente:'130'.", no: "No mueve dinero; requiere WMS activo." },
|
|
280
|
+
|
|
281
|
+
clientes_del_nodo: { que: "Clientes/vendedores del nodo con su lista de precio.", uso: "Sin parámetros. Scopeado a tu nodo.", ej: "clientes_del_nodo → ver a quién facturás y con qué lista.", no: "No los crea/edita (cliente_crear/editar)." },
|
|
282
|
+
cliente_crear: { que: "Alta de cliente/vendedor en tu nodo.", uso: "nombre obligatorio; telefono/dni/direccion/idLista opcionales. Admin global: `nodo` para elegir/acotar el nodo.", ej: "cliente_crear nombre:'Nueva Distri' idLista:'ID12'.", no: "No toca dinero; no cruza nodos." },
|
|
283
|
+
cliente_editar: { que: "Edita un cliente de tu nodo cambiando SOLO los campos que pasás (lista de precio, teléfono, email con el que entra, DNI, dirección, nombre de pago, activo).", uso: "id + los campos a cambiar. Lo que no pasás queda igual; \"\" vacía el campo. `email` = el del usuario del cliente (requiere permiso de usuarios). Admin global: `nodo` mueve el cliente a ese nodo.", ej: "cliente_editar id:130 idLista:'ID12' · cliente_editar id:50 email:'nuevo@mail.com'.", no: "No toca dinero ni la clave (cliente_resetear_clave)." },
|
|
284
|
+
sucursales_cliente: { que: "Lista las sucursales (puntos de retiro) de un cliente de tu nodo con su id, nombre, dirección y perfil de zona.", uso: "cliente (id, de clientes_del_nodo). Requiere permiso 'gestion'.", ej: "sucursales_cliente cliente:130 → ver las sucursales y sus perfiles.", no: "No las crea/edita (eso lo hace el vendedor en su portal); solo el PERFIL lo setea el staff con sucursal_perfil." },
|
|
285
|
+
sucursal_perfil: { que: "Setea (o limpia) el perfil de zona de UNA sucursal: la liquidación cotiza los envíos que salen de ahí según SU distancia, no la del cliente.", uso: "sucursalId (de sucursales_cliente) + perfilZona (vacío = hereda el del cliente). Requiere permiso 'gestion'. Config de staff (el vendedor NO lo toca).", ej: "sucursal_perfil sucursalId:12 perfilZona:'MORENO'.", no: "No mueve dinero, pero afecta el tramo/precio; no crea la sucursal." },
|
|
286
|
+
precios_ver: { que: "Listas de precios por zona de tu nodo.", uso: "Sin parámetros. Requiere permiso 'precios'.", ej: "precios_ver → ver tarifas por zona.", no: "No las edita (precio_actualizar)." },
|
|
287
|
+
precio_actualizar: { que: "Cambia precios de una lista creando una VERSIÓN nueva (histórico intacto).", uso: "idLista + los tramos (cercana/media/lejana/muyLejana); referencia/vigenciaDesde opcionales. Requiere permiso 'precios' (y que el conector lo permita). Admin global: `nodo` para elegir/acotar el nodo.", ej: "precio_actualizar idLista:'ID12' cercana:1500 media:2000 lejana:2800.", no: "No mueve dinero; puede estar deshabilitado por el conector (ALLOW_PRECIOS)." },
|
|
288
|
+
cuentas_vinculadas: { que: "Cuántas cuentas de tienda hay vinculadas, por proveedor (ML / TiendaNube / TiendaNegocio).", uso: "nodo opcional (solo admin, baja al desglose por cliente).", ej: "cuentas_vinculadas → cuántos vendedores tienen ML conectado.", no: "No vincula (generar_enlace_vinculacion)." },
|
|
289
|
+
generar_enlace_vinculacion: { que: "Genera el enlace para vincular una tienda; se lo mandás al cliente para que autorice.", uso: "proveedor (ml|tiendanube|tiendanegocio); staff pasa `cliente` (de su nodo).", ej: "generar_enlace_vinculacion proveedor:ml cliente:'Distri Sur'.", no: "No completa la vinculación (la autoriza el dueño de la tienda); no toca dinero." },
|
|
290
|
+
|
|
291
|
+
marketing_config_ver: { que: "Muestra los píxeles/config de marketing (Meta Pixel, GA4, GTM y si hay tokens de la Conversions API / API secret cargados) de tu alcance.", uso: "nodo opcional (solo admin global). Vendedor: la suya; operador: la de su nodo (el backend resuelve el alcance).", ej: "marketing_config_ver → ver qué Meta Pixel / GA4 tenés configurado.", no: "No devuelve los tokens/secrets en claro (solo si están cargados); no los edita (marketing_config_guardar)." },
|
|
292
|
+
marketing_config_guardar: { que: "Guarda los píxeles/config de marketing de tu alcance (vendedor: la suya; operador: la de su nodo).", uso: "Campos opcionales: metaPixelId, ga4MeasurementId, gtmId, metaConvApiToken, metaTestEventCode, ga4ApiSecret, activo, nodo (solo admin). String vacío BORRA ese campo; omitido = queda igual.", ej: "marketing_config_guardar metaPixelId:'123456789' ga4MeasurementId:'G-ABC123'.", no: "No mueve dinero; no verifica que el píxel exista en Meta/GA4." },
|
|
293
|
+
|
|
294
|
+
liquidacion_buscar: { que: "Busca liquidaciones por cliente (nombre o id base) y/o rango de monto. Trae el saldo de cta cte del cliente y la marca sinRecibir.", uso: "Filtros opcionales: cliente, montoMin, montoMax, sinRecibir. `nodo` solo lo usa el admin global (acota a un nodo); el admin de nodo queda en el suyo. Requiere permiso finanzas.", ej: "liquidacion_buscar cliente:'Roger' montoMin:50000 → sus liquidaciones de $50.000+.", no: "No edita ni paga; el detalle de envíos es liquidacion_detalle." },
|
|
295
|
+
liquidacion_detalle: { que: "Snapshot completo de una liquidación: detalle de los envíos, resumen y comisiones.", uso: "id de la liquidación (de liquidacion_buscar). Scopeada: no abre la de otro nodo.", ej: "liquidacion_detalle id:1234 → ver los envíos que la componen.", no: "No la modifica; no factura." },
|
|
296
|
+
liquidaciones_sin_recibir: { que: "Liquidaciones de clientes con saldo deudor (aprox. por cliente: el pago todavía no entró).", uso: "Opcional cliente y nodo (nodo solo admin global). Requiere permiso finanzas.", ej: "liquidaciones_sin_recibir → ver quién debe pagar.", no: "Es aproximado a nivel cliente (los pagos son FIFO por cuenta, no por liquidación); no reclama ni cobra." },
|
|
297
|
+
|
|
298
|
+
chofer_listar: { que: "Usuarios de tu nodo, incluidos los choferes (rol mensajero): id, nombre, teléfono, activo.", uso: "Sin parámetros. Requiere permiso 'usuarios'.", ej: "chofer_listar → ver tus cadetes.", no: "No crea/edita (chofer_crear/editar)." },
|
|
299
|
+
chofer_crear: { que: "Da de alta un chofer (mensajero) en tu nodo con una CLAVE TEMPORAL que devuelve el server.", uso: "nombre + apellido (obligatorios, por separado) + telefono + email; mensajeroNombre opcional (macheo con su cta cte). Admin global: `nodo` para elegir/acotar el nodo. Pasale la clave; la cambia al primer ingreso.", ej: "chofer_crear nombre:'Maxi' apellido:'Sagarzazu' telefono:'11...' email:'maxi@...'.", no: "Claude nunca inventa la clave; el server la genera." },
|
|
300
|
+
chofer_editar: { que: "Edita un chofer de tu nodo: nombre, apellido, teléfono, mensajeroNombre y/o activo.", uso: "id + los campos a cambiar.", ej: "chofer_editar id:7 activo:false → lo desactivás.", no: "No toca credenciales/clave." },
|
|
301
|
+
usuario_editar: { que: "Corrige nombre, apellido, teléfono y/o email de un usuario de tu nodo.", uso: "id + los campos a cambiar. Si el usuario todavía no tiene nombre y apellido separados, pasá los dos. Requiere permiso 'usuarios'.", ej: "usuario_editar id:7 nombre:'Sol' apellido:'Martínez'.", no: "No toca rol, permisos ni clave. No cambia el nombre con el que se le paga al cadete." },
|
|
302
|
+
operador_crear: { que: "Da de alta un OPERADOR (staff que administra el nodo: recibe/escanea/despacha, liquida, etc.) en tu nodo, con una CLAVE TEMPORAL que devuelve el server.", uso: "nombre + apellido + email + telefono; permisos opcional (default 'logistica'): gestion, finanzas, mensajeros, reportes, logistica, wms, precios, usuarios. El admin global elige el nodo con `nodo`. Requiere permiso 'usuarios'. Pasale la clave; la cambia al primer ingreso.", ej: "operador_crear nombre:'Ana' apellido:'Gómez' email:'ana@nodo.com' telefono:'11...' permisos:['logistica','gestion'].", no: "No es un vendedor (cliente_generar_usuario) ni un chofer (chofer_crear); no otorga admin de nodo (usuario_habilitar_rol); Claude nunca inventa la clave; no toca dinero." },
|
|
303
|
+
cliente_generar_usuario: { que: "Le crea las credenciales de login a un cliente/vendedor YA EXISTENTE de tu nodo (para que entre a su portal) y devuelve una CLAVE TEMPORAL.", uso: "cliente (nombre o id, de clientes_del_nodo) + email para loguear + nombre y apellido de la PERSONA que entra (obligatorios; no el de la tienda); telefono opcional. Requiere permiso 'usuarios'. Admin global: `nodo` para elegir/acotar el nodo. Si el cliente ya tiene usuario, avisa (no lo pisa).", ej: "cliente_generar_usuario cliente:'Distri Sur' email:'ventas@distrisur.com' nombre:'Juan' apellido:'Pérez'.", no: "No da de alta el PERFIL del cliente (eso es cliente_crear); Claude nunca inventa la clave; no toca dinero." },
|
|
304
|
+
cliente_resetear_clave: { que: "Genera una CLAVE TEMPORAL NUEVA para un cliente/vendedor de tu nodo que YA tiene usuario pero perdió el acceso.", uso: "cliente (nombre o id, de clientes_del_nodo). Requiere permiso 'usuarios'. Admin global: `nodo` para elegir/acotar el nodo.", ej: "cliente_resetear_clave cliente:'Distri Sur' → nueva clave temporal.", no: "Si el cliente todavía no tiene usuario, avisa y sugiere cliente_generar_usuario; no crea uno nuevo; no toca dinero." },
|
|
305
|
+
usuario_habilitar_reparto: { que: "Marca a un operador/admin de tu nodo como TAMBIÉN mensajero (puede escanear, autoasignarse y entregar).", uso: "id + activo (default true).", ej: "usuario_habilitar_reparto id:4 → ese comisionista ya puede repartir.", no: "No toca credenciales." },
|
|
306
|
+
|
|
307
|
+
nodos_listar: { que: "Todas las logísticas (nodos) de la red.", uso: "Sin parámetros. Solo admin global.", ej: "nodos_listar → id/nombre de cada nodo.", no: "No los crea (nodo_crear)." },
|
|
308
|
+
nodo_crear: { que: "Alta de una logística/nodo.", uso: "nombre + telefono opcional. Solo admin global.", ej: "nodo_crear nombre:'Nodo Oeste' telefono:'11...'.", no: "No toca dinero." },
|
|
309
|
+
nodo_provisorio_crear: { que: "Crea al vuelo un nodo PROVISORIO (placeholder para el clearing) dentro de un grupo, cuando te bajan paquetes de un nodo que todavía no está dado de alta.", uso: "nombre + grupoId (de tus grupos). Staff: solo en grupos de TU nodo; admin global: cualquiera.", ej: "nodo_provisorio_crear nombre:'Nodo Avellaneda prov.' grupoId:3.", no: "No tiene login hasta conciliarlo con el nodo real; no mueve dinero." },
|
|
310
|
+
provisorio_conciliar: { que: "Fusiona un nodo PROVISORIO con el nodo REAL cuando este ya está dado de alta: reatribuye sus envíos y su membresía de grupo al nodo real y marca el provisorio como resuelto.", uso: "provisorioId (de nodos_listar) + nodoReal (nombre o id). Como operador solo conciliás los provisorios que creó tu nodo y contra un nodo que YA opera en el grupo; el admin global puede además pasar `tarifa` (bandas) si el nodo real todavía no está en el grupo.", ej: "provisorio_conciliar provisorioId:42 nodoReal:'Nodo Avellaneda'.", no: "No mueve dinero (reordena el clearing: esos envíos pasan a la tarifa del nodo real)." },
|
|
311
|
+
usuario_habilitar_rol: { que: "Habilita o deshabilita a un usuario como superoperador (global) y/o admin de nodo.", uso: "id + al menos uno de esSuperoperador/esAdminNodo (true = habilitar, false = quitar). Solo admin global; otorgar superoperador exige un superadmin real.", ej: "usuario_habilitar_rol id:12 esAdminNodo:true → lo hacés admin de su nodo.", no: "No crea el usuario; no toca credenciales ni dinero." },
|
|
312
|
+
grupo_crear: { que: "Crea un grupo logístico nuevo (para el clearing entre nodos).", uso: "nombre. Solo admin global. Los nodos se agregan/tarifan después.", ej: "grupo_crear nombre:'Grupo Sur'.", no: "No agrega ni tarifa nodos; no toca dinero." },
|
|
313
|
+
grupo_miembros: { que: "Lista los nodos de un grupo, marcando quién es admin de grupo y si vos lo sos.", uso: "grupo (id). El id lo ves en mis_datos o al vincular por QR. Solo miembros del grupo.", ej: "grupo_miembros grupo:21.", no: "No modifica; solo lectura." },
|
|
314
|
+
grupo_sumar_nodo: { que: "Agrega un nodo al grupo (entra como miembro normal, sin admin).", uso: "grupo + nodo (ids). Solo un ADMIN del grupo (o admin global).", ej: "grupo_sumar_nodo grupo:21 nodo:5.", no: "No lo hace admin; no toca dinero." },
|
|
315
|
+
grupo_expulsar_nodo: { que: "Saca un nodo del grupo.", uso: "grupo + nodo (ids). Solo un ADMIN del grupo. No se puede expulsar al último admin.", ej: "grupo_expulsar_nodo grupo:21 nodo:5.", no: "No deja el grupo sin admin; no toca dinero." },
|
|
316
|
+
grupo_admin_permiso: { que: "Da o saca el permiso de admin de grupo a un nodo (para que otros hereden la administración).", uso: "grupo + nodo (ids) + admin (true=dar, false=sacar). Solo un ADMIN del grupo. No se puede sacar al último admin.", ej: "grupo_admin_permiso grupo:21 nodo:5 admin:true.", no: "No toca dinero." },
|
|
317
|
+
logo_subir: { que: "Sube/actualiza el logo y la marca (marca blanca) de un nodo.", uso: "nodo (id) + logo (data-URI base64, ej. data:image/png;base64,...); marca/slug/color opcionales. Solo admin global.", ej: "logo_subir nodo:6 logo:'data:image/png;base64,iVBOR...' marca:'FastCorreo'.", no: "No toca dinero; el logo va como data-URI base64." },
|
|
318
|
+
sugerencias_listar: { que: "Sugerencias entrantes de usuarios (funciones/mejoras/bugs) con usuario, rol, nodo, estado.", uso: "rol y/o estado opcionales. Operador: su nodo; admin: todas.", ej: "sugerencias_listar estado:nueva.", no: "No las implementa; es lectura." },
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
// Renderiza la ayuda. q = nombre de tool o clave de tema (opcional).
|
|
322
|
+
// ctx = { version, disponibles: Set<string> } — disponibles filtra el índice a lo
|
|
323
|
+
// que ese usuario realmente tiene (según su rol/permisos).
|
|
324
|
+
export function renderAyuda(q, ctx = {}) {
|
|
325
|
+
const version = ctx.version ?? "";
|
|
326
|
+
const disponibles = ctx.disponibles instanceof Set ? ctx.disponibles : null;
|
|
327
|
+
const clave = String(q ?? "").trim().toLowerCase().replace(/[^a-z_]/g, "");
|
|
328
|
+
|
|
329
|
+
if (!clave) return indice(version, disponibles);
|
|
330
|
+
|
|
331
|
+
if (TOPICS[clave]) return TOPICS[clave] + `\n\n(Consultá una herramienta con: ayuda tool:<nombre>)`;
|
|
332
|
+
|
|
333
|
+
const d = TOOL_DOCS[clave];
|
|
334
|
+
if (d) {
|
|
335
|
+
const tiene = !disponibles || disponibles.has(clave);
|
|
336
|
+
const nota = tiene ? "" : "\n\n(⚠️ Con tu rol/permiso actual esta herramienta no está disponible.)";
|
|
337
|
+
return [
|
|
338
|
+
`🔧 ${clave}`,
|
|
339
|
+
"",
|
|
340
|
+
`Qué hace: ${d.que}`,
|
|
341
|
+
`Cómo usar: ${d.uso}`,
|
|
342
|
+
`Ejemplo: ${d.ej}`,
|
|
343
|
+
`Qué NO hace: ${d.no}`,
|
|
344
|
+
].join("\n") + nota;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// No matcheó: sugerir por prefijo/substring.
|
|
348
|
+
const cerca = Object.keys(TOOL_DOCS).filter((n) => n.includes(clave)).slice(0, 8);
|
|
349
|
+
const temas = Object.keys(TOPICS);
|
|
350
|
+
return [
|
|
351
|
+
`No encontré una herramienta o tema llamado "${clave}".`,
|
|
352
|
+
cerca.length ? `¿Quisiste decir?: ${cerca.join(", ")}` : "",
|
|
353
|
+
`Temas disponibles: ${temas.join(", ")}`,
|
|
354
|
+
`Sin argumentos, ayuda te da el índice completo.`,
|
|
355
|
+
].filter(Boolean).join("\n");
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function indice(version, disponibles) {
|
|
359
|
+
const out = [
|
|
360
|
+
`AYUDA DEL MCP NEXUS FLEX${version ? ` (v${version})` : ""}`,
|
|
361
|
+
"",
|
|
362
|
+
"Pedí el detalle de una herramienta con: ayuda tool:<nombre>",
|
|
363
|
+
"Pedí un tema transversal con: ayuda tema:<clave>",
|
|
364
|
+
`Temas: ${Object.keys(TOPICS).join(", ")}`,
|
|
365
|
+
"",
|
|
366
|
+
"Herramientas" + (disponibles ? " disponibles para vos" : "") + ":",
|
|
367
|
+
];
|
|
368
|
+
for (const g of GROUPS) {
|
|
369
|
+
const items = g.tools.filter((t) => TOOL_DOCS[t] && (!disponibles || disponibles.has(t)));
|
|
370
|
+
if (!items.length) continue;
|
|
371
|
+
out.push("", `▸ ${g.titulo}`);
|
|
372
|
+
for (const t of items) out.push(` • ${t} — ${TOOL_DOCS[t].que}`);
|
|
373
|
+
}
|
|
374
|
+
// Tools registradas que no estén en ningún grupo (por si se agrega una y se olvida).
|
|
375
|
+
if (disponibles) {
|
|
376
|
+
const enGrupos = new Set(GROUPS.flatMap((g) => g.tools));
|
|
377
|
+
const sueltas = [...disponibles].filter((t) => TOOL_DOCS[t] && !enGrupos.has(t));
|
|
378
|
+
if (sueltas.length) {
|
|
379
|
+
out.push("", "▸ Otras");
|
|
380
|
+
for (const t of sueltas) out.push(` • ${t} — ${TOOL_DOCS[t].que}`);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return out.join("\n");
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// ============================================================================
|
|
387
|
+
// FLUJOS GUIADOS (Fase A, ver docs/Plan-MCP-Flujo-Guiado.md). ⚠️ ESPEJO en
|
|
388
|
+
// backend/src/services/mcp-docs.ts. Paso a paso por objetivo para no dejar
|
|
389
|
+
// nada incompleto; el tool `flujo` los devuelve y el LLM guía con esto.
|
|
390
|
+
// ============================================================================
|
|
391
|
+
const CONTROL_POR_FOTO = [
|
|
392
|
+
"FLUJO — CONTROL POR FOTO Y CARGA DE PLANILLA VÍA MCP",
|
|
393
|
+
"Qué es: cargar paquetes que YA vienen con etiqueta de Mercado Libre pero NO están vinculados",
|
|
394
|
+
"a una cuenta ML (un cliente/nodo te los bajó etiquetados y te dejó las fotos en una CARPETA).",
|
|
395
|
+
"Sirve para registrar rápido el movimiento y el CLEARING entre nodos, sin escanear.",
|
|
396
|
+
"",
|
|
397
|
+
"PREPARACIÓN (una vez): definí quién reparte cada zona con `asignar_nodo_zona` (nodo",
|
|
398
|
+
" responsable) o `asignar_mensajero_zona`. Así el auto-ruteo sabe a quién mandar cada paquete.",
|
|
399
|
+
"",
|
|
400
|
+
"POR CADA FOTO de la carpeta → `envio_desde_etiqueta_ml`:",
|
|
401
|
+
" • cliente (o mlSenderId): de quién es el paquete (detecta al vendedor de ML);",
|
|
402
|
+
" • mlShipmentId + mlQr crudos + `reusarEtiqueta: true` → REUSA el tracking/QR/etiqueta ya",
|
|
403
|
+
" impreso (no genera uno nuevo), 'ml_manual', IDEMPOTENTE (recargar no duplica);",
|
|
404
|
+
" • destino (destinatario, dirección, localidad, CP);",
|
|
405
|
+
" • DÍGITOS/LETRAS ILEGIBLES: cargá la parte legible + un '*' por cada carácter que NO se lee",
|
|
406
|
+
" (ej. 'Aguirre 31**') — NO inventes — y pasá `fotoRuta` = la RUTA LOCAL de la foto en la",
|
|
407
|
+
" compu (ej. 'C:\\\\Users\\\\...\\\\Desktop\\\\etiquetas\\\\ml_123.jpg') para reencontrarla;",
|
|
408
|
+
" • CIERRE DEL CICLO en un paso con `avanzarA`: 'colectado' / 'procesado' / 'entregado'.",
|
|
409
|
+
" 'entregado' recorre TODOS los estados de una: A retirar → Colectado → Procesado (En centro)",
|
|
410
|
+
" → [si pasás `grupo`, ej. 'Bonorino': En camino, con su integrante] → Entregado. Cada estado",
|
|
411
|
+
" queda registrado con QUIÉN lo hizo (vos, el usuario MCP) — es la vía rápida del clearing;",
|
|
412
|
+
" • `autoRutear: true`: además, manda el paquete al nodo/mensajero responsable de su zona.",
|
|
413
|
+
"",
|
|
414
|
+
"AL TERMINAR: devolvé el CONTEO total + desglose por cliente/zona/estado + qué quedó sin",
|
|
415
|
+
" responsable de zona (avisá para configurarla). Cuando el nodo real se dé de alta puede",
|
|
416
|
+
" ABSORBER este historial (conciliar el provisorio) o descartarlo — no bloquea nada.",
|
|
417
|
+
].join("\n");
|
|
418
|
+
|
|
419
|
+
export const FLUJOS = {
|
|
420
|
+
alta_cliente: [
|
|
421
|
+
"FLUJO — ALTA DE CLIENTE (vendedor) COMPLETA",
|
|
422
|
+
"Guiá paso a paso; NO lo des por terminado hasta el paso 4.",
|
|
423
|
+
"",
|
|
424
|
+
"1) Datos básicos: nombre (obligatorio), teléfono/WhatsApp, dirección, DNI/CUIT si tiene.",
|
|
425
|
+
" → `cliente_crear`.",
|
|
426
|
+
"2) LISTA DE PRECIOS (preguntá SIEMPRE «¿qué le cobrás a este cliente?»):",
|
|
427
|
+
" • Oficial de Flex (la que usan todos) o un PRECIO PROPIO (mirá `precios_ver`, el nombre",
|
|
428
|
+
" está en 'referencia'; si es nueva, se crea con nombre para reusarla).",
|
|
429
|
+
" → asignás con `cliente_editar idLista:<id>`.",
|
|
430
|
+
"3) PERFIL DE ZONA: preguntá «¿usa la zonificación COMÚN o necesita un perfil aparte?».",
|
|
431
|
+
" Ej.: cliente de CABA que usa las zonas de todos → perfil GENERAL; si opera distinto",
|
|
432
|
+
" (otras metazonas / precios por zona) asignale un perfil de zona propio.",
|
|
433
|
+
"4) COLECTA (¿le retirás la mercadería?): si sí, pedí la DIRECCIÓN de retiro con",
|
|
434
|
+
" timbre/piso/referencia y que quede GEOPOSICIONADA; definí si la colecta tiene cargo.",
|
|
435
|
+
"5) ¿Vende por Mercado Libre / Tienda? Ofrecé vincular con `generar_enlace_vinculacion`.",
|
|
436
|
+
"6) CONFIRMÁ (`clientes_del_nodo`): debe estar con su LISTA. Si le falta lista, zona o",
|
|
437
|
+
" colecta según lo que dijo, el alta está INCOMPLETA → volvé al paso que falte.",
|
|
438
|
+
].join("\n"),
|
|
439
|
+
|
|
440
|
+
control_por_foto: CONTROL_POR_FOTO,
|
|
441
|
+
alta_planilla_ml: CONTROL_POR_FOTO, // alias histórico
|
|
442
|
+
|
|
443
|
+
facturacion: [
|
|
444
|
+
"FLUJO — PONER A FACTURAR (ARCA) DE CERO",
|
|
445
|
+
"",
|
|
446
|
+
"IDEA CENTRAL: quien COBRA es quien FACTURA. Cada cuenta de dinero puede tener su",
|
|
447
|
+
"propio emisor; lo que un cliente transfiere a esa cuenta se factura con el CUIT del",
|
|
448
|
+
"dueño de esa cuenta. Las cuentas sin emisor propio facturan con el CUIT de la empresa.",
|
|
449
|
+
"",
|
|
450
|
+
"⚠️ LO QUE YO NO PUEDO HACER: el trámite en ARCA (certificado, relación con el web",
|
|
451
|
+
" service, punto de venta) necesita la Clave Fiscal de esa persona y un navegador.",
|
|
452
|
+
" Yo no entro a ARCA ni manejo claves fiscales. Tampoco emito comprobantes: eso se",
|
|
453
|
+
" hace desde la PWA o desde el portal del cliente. Sí dejo TODO lo de Nexus Flex",
|
|
454
|
+
" configurado y te digo exactamente qué falta.",
|
|
455
|
+
"",
|
|
456
|
+
"PASO 0 — ¿Dónde estás parado? → `facturacion_estado`.",
|
|
457
|
+
" Te dice qué emisores hay, qué cuentas no tienen emisor y qué le falta a cada",
|
|
458
|
+
" cliente. Arrancá SIEMPRE por acá y volvé al final para confirmar.",
|
|
459
|
+
"",
|
|
460
|
+
"PASO 1 — EL EMISOR (una vez por persona/CUIT que cobre). Lo hace ESA persona:",
|
|
461
|
+
" a) Registro Único Tributario → Puntos de venta → dar de alta uno de tipo",
|
|
462
|
+
" «Factura Electrónica - Monotributo - Web Services» (monotributo) o",
|
|
463
|
+
" «RECE para aplicativo y web services» (responsable inscripto).",
|
|
464
|
+
" ⚠️ El de «Factura en Línea» NO sirve. Anotá el número.",
|
|
465
|
+
" b) Administración de Certificados Digitales → agregar el servicio → subir un CSR",
|
|
466
|
+
" y descargar el certificado.",
|
|
467
|
+
" c) Administrador de Relaciones → Nueva Relación → ARCA → WebServices →",
|
|
468
|
+
" «Facturación Electrónica» → representante = ese certificado.",
|
|
469
|
+
" d) En la PWA, Configuración → 🧾 Facturación ARCA → «+ Emisor para una cuenta»:",
|
|
470
|
+
" elegís la cuenta de dinero, cargás CUIT, razón social (el TITULAR del CUIT),",
|
|
471
|
+
" condición IVA, punto de venta, certificado y clave, y «Probar conexión».",
|
|
472
|
+
" Empezá en HOMOLOGACIÓN; pasá a producción cuando la prueba dé OK.",
|
|
473
|
+
"",
|
|
474
|
+
"PASO 2 — LOS CLIENTES a los que le va a facturar → `facturacion_preparar_cliente`.",
|
|
475
|
+
" Necesitás de cada uno: CUIT, razón social (como figura en ARCA, NO el nombre de",
|
|
476
|
+
" fantasía), condición frente al IVA, en qué cuenta cobra, y si factura semanal o",
|
|
477
|
+
" mensual. Preguntá TODO eso antes de ejecutar; no inventes ninguno.",
|
|
478
|
+
" El corte queda en HOY: lo anterior no se factura desde el sistema (ya se facturó",
|
|
479
|
+
" por fuera y emitirlo de nuevo sería un duplicado).",
|
|
480
|
+
"",
|
|
481
|
+
"PASO 3 — CONFIRMAR → `facturacion_estado` de nuevo. Un cliente está listo cuando",
|
|
482
|
+
" tiene CUIT, razón social, condición IVA, cuenta de cobro con emisor y está",
|
|
483
|
+
" habilitado. Si falta algo, el estado te dice cuál.",
|
|
484
|
+
"",
|
|
485
|
+
"CÓMO SE FACTURA DESPUÉS (no por acá):",
|
|
486
|
+
" • El cliente entra a su portal (Mi Cuenta → 🧾 Tus facturas), revisa el período",
|
|
487
|
+
" cerrado y toca «Emitir factura». Solo aparece si: el período cerró, no tiene",
|
|
488
|
+
" reclamos abiertos, no está facturado y NO tiene saldo pendiente.",
|
|
489
|
+
" • Si el importe no coincide con lo acordado, el cliente RECLAMA la liquidación y",
|
|
490
|
+
" el staff la corrige; el botón usa siempre lo liquidado, nunca un número a mano.",
|
|
491
|
+
" • El staff puede emitir desde la PWA (Liquidaciones Anteriores → Facturar).",
|
|
492
|
+
].join("\n"),
|
|
493
|
+
|
|
494
|
+
alta_operador: [
|
|
495
|
+
"FLUJO — ALTA DE UN OPERADOR DE NODO (staff que administra)",
|
|
496
|
+
"1) Datos: email (con el que loguea), nombre y apellido (obligatorios), teléfono.",
|
|
497
|
+
"2) Permisos: qué secciones va a manejar (gestión, finanzas, mensajeros, precios, wms,",
|
|
498
|
+
" reportes, datos). Si es el dueño del nodo, después habilitalo como admin de nodo.",
|
|
499
|
+
" → `operador_crear` (te devuelve una CLAVE TEMPORAL para pasarle; la cambia al entrar).",
|
|
500
|
+
"3) ¿Es dueño/mano derecha? `usuario_habilitar_rol` para admin de nodo (solo admin global).",
|
|
501
|
+
"4) Pasale la clave temporal por un canal seguro. Nunca la inventes vos.",
|
|
502
|
+
].join("\n"),
|
|
503
|
+
|
|
504
|
+
alta_chofer: [
|
|
505
|
+
"FLUJO — ALTA DE UN CHOFER (mensajero/cadete)",
|
|
506
|
+
"1) Datos: nombre y apellido (obligatorios), teléfono, email. → `chofer_crear` (devuelve CLAVE TEMPORAL).",
|
|
507
|
+
"2) Si además cobra su ganancia contra su cuenta, pasá `mensajeroNombre` (el nombre con",
|
|
508
|
+
" el que figura en el Maestro) para el macheo.",
|
|
509
|
+
"3) Zona: asignale su metazona con `asignar_mensajero_zona` para que le lleguen envíos.",
|
|
510
|
+
"4) Pasale la clave temporal; la cambia al primer ingreso.",
|
|
511
|
+
].join("\n"),
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
/** Devuelve el paso a paso de un objetivo, o el índice de flujos disponibles. */
|
|
515
|
+
export function renderFlujo(q) {
|
|
516
|
+
const key = (q ?? "").toLowerCase().trim().replace(/[\s-]+/g, "_");
|
|
517
|
+
if (key && FLUJOS[key]) return FLUJOS[key];
|
|
518
|
+
const found = key ? Object.keys(FLUJOS).find((k) => k.includes(key) || key.includes(k)) : null;
|
|
519
|
+
if (found) return FLUJOS[found];
|
|
520
|
+
return [
|
|
521
|
+
"FLUJOS GUIADOS disponibles (paso a paso para no dejar nada a medias):",
|
|
522
|
+
...Object.keys(FLUJOS).map((k) => ` • ${k}`),
|
|
523
|
+
"",
|
|
524
|
+
"Pedí uno, ej.: flujo alta_cliente",
|
|
525
|
+
].join("\n");
|
|
526
|
+
}
|