nexusflex-mcp 3.69.0 → 3.69.1

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.
Files changed (3) hide show
  1. package/docs.mjs +510 -542
  2. package/package.json +1 -1
  3. package/server.mjs +4 -1
package/docs.mjs CHANGED
@@ -1,542 +1,510 @@
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
- " • `nodoEntrega`: el nodo que RECIBIÓ y entregó el paquete (ej. 'FastCorreo'). Es lo que hace",
414
- " que el envío entre al CIERRE SEMANAL entre nodos (origen = nodo del cliente → entrega =",
415
- " este nodo). Sin `grupo` se deduce: cliente del mismo nodo = paquete propio (sin traspaso);",
416
- " de otro nodo = el grupo que comparten. Decilo siempre que sepas quién lo entregó;",
417
- "",
418
- "TANDA DE UN CADETE (lo más común: una carpeta por día y por cadete, con paquetes propios y",
419
- " de otros nodos mezclados): por cada foto → mlQr crudo (+ destino leído de la etiqueta),",
420
- " reusarEtiqueta:true, fecha:<el día>, avanzarA:'entregado', mensajero:<el cadete>,",
421
- " nodoEntrega:<nuestro nodo>. El vendedor y su nodo salen del QR; si ya estaba cargado no se",
422
- " duplica (se completa y se avanza). Si contesta que no conoce la cuenta de ML, PREGUNTÁ de",
423
- " qué nodo/cliente es y repetí con `cliente` — no adivines. Al final: conteo propios vs. de",
424
- " cada nodo, los que no se reconocieron y las advertencias.",
425
- " • PLANILLA ATRASADA: `fecha` = el día real (dd/mm/aaaa). Ej.: 5 envíos de un cliente de",
426
- " Envíos Frank que entregó FastCorreo el lunes 14 → cliente, fecha:'14/09/2026',",
427
- " avanzarA:'entregado', grupo:'Portela', nodoEntrega:'FastCorreo'. Si esa semana ya",
428
- " cerró, entra en el cierre siguiente marcado como tarde, con su fecha real.",
429
- "",
430
- "AL TERMINAR: devolvé el CONTEO total + desglose por cliente/zona/estado + qué quedó sin",
431
- " responsable de zona (avisá para configurarla). Cuando el nodo real se dé de alta puede",
432
- " ABSORBER este historial (conciliar el provisorio) o descartarlo — no bloquea nada.",
433
- ].join("\n");
434
-
435
- export const FLUJOS = {
436
- alta_cliente: [
437
- "FLUJO — ALTA DE CLIENTE (vendedor) COMPLETA",
438
- "Guiá paso a paso; NO lo des por terminado hasta el paso 4.",
439
- "",
440
- "1) Datos básicos: nombre (obligatorio), teléfono/WhatsApp, dirección, DNI/CUIT si tiene.",
441
- " → `cliente_crear`.",
442
- "2) LISTA DE PRECIOS (preguntá SIEMPRE «¿qué le cobrás a este cliente?»):",
443
- " • Oficial de Flex (la que usan todos) o un PRECIO PROPIO (mirá `precios_ver`, el nombre",
444
- " está en 'referencia'; si es nueva, se crea con nombre para reusarla).",
445
- " → asignás con `cliente_editar idLista:<id>`.",
446
- "3) PERFIL DE ZONA: preguntá «¿usa la zonificación COMÚN o necesita un perfil aparte?».",
447
- " Ej.: cliente de CABA que usa las zonas de todos → perfil GENERAL; si opera distinto",
448
- " (otras metazonas / precios por zona) asignale un perfil de zona propio.",
449
- "4) COLECTA (¿le retirás la mercadería?): si sí, pedí la DIRECCIÓN de retiro con",
450
- " timbre/piso/referencia y que quede GEOPOSICIONADA; definí si la colecta tiene cargo.",
451
- "5) ¿Vende por Mercado Libre / Tienda? Ofrecé vincular con `generar_enlace_vinculacion`.",
452
- "6) CONFIRMÁ (`clientes_del_nodo`): debe estar con su LISTA. Si le falta lista, zona o",
453
- " colecta según lo que dijo, el alta está INCOMPLETA → volvé al paso que falte.",
454
- ].join("\n"),
455
-
456
- control_por_foto: CONTROL_POR_FOTO,
457
- alta_planilla_ml: CONTROL_POR_FOTO, // alias histórico
458
-
459
- facturacion: [
460
- "FLUJO — PONER A FACTURAR (ARCA) DE CERO",
461
- "",
462
- "IDEA CENTRAL: quien COBRA es quien FACTURA. Cada cuenta de dinero puede tener su",
463
- "propio emisor; lo que un cliente transfiere a esa cuenta se factura con el CUIT del",
464
- "dueño de esa cuenta. Las cuentas sin emisor propio facturan con el CUIT de la empresa.",
465
- "",
466
- "⚠️ LO QUE YO NO PUEDO HACER: el trámite en ARCA (certificado, relación con el web",
467
- " service, punto de venta) necesita la Clave Fiscal de esa persona y un navegador.",
468
- " Yo no entro a ARCA ni manejo claves fiscales. Tampoco emito comprobantes: eso se",
469
- " hace desde la PWA o desde el portal del cliente. Sí dejo TODO lo de Nexus Flex",
470
- " configurado y te digo exactamente qué falta.",
471
- "",
472
- "PASO 0 — ¿Dónde estás parado? → `facturacion_estado`.",
473
- " Te dice qué emisores hay, qué cuentas no tienen emisor y qué le falta a cada",
474
- " cliente. Arrancá SIEMPRE por acá y volvé al final para confirmar.",
475
- "",
476
- "PASO 1 — EL EMISOR (una vez por persona/CUIT que cobre). Lo hace ESA persona:",
477
- " a) Registro Único Tributario → Puntos de venta → dar de alta uno de tipo",
478
- " «Factura Electrónica - Monotributo - Web Services» (monotributo) o",
479
- " «RECE para aplicativo y web services» (responsable inscripto).",
480
- " ⚠️ El de «Factura en Línea» NO sirve. Anotá el número.",
481
- " b) Administración de Certificados Digitales → agregar el servicio → subir un CSR",
482
- " y descargar el certificado.",
483
- " c) Administrador de Relaciones → Nueva Relación → ARCA → WebServices →",
484
- " «Facturación Electrónica» → representante = ese certificado.",
485
- " d) En la PWA, Configuración → 🧾 Facturación ARCA → «+ Emisor para una cuenta»:",
486
- " elegís la cuenta de dinero, cargás CUIT, razón social (el TITULAR del CUIT),",
487
- " condición IVA, punto de venta, certificado y clave, y «Probar conexión».",
488
- " Empezá en HOMOLOGACIÓN; pasá a producción cuando la prueba dé OK.",
489
- "",
490
- "PASO 2 — LOS CLIENTES a los que le va a facturar → `facturacion_preparar_cliente`.",
491
- " Necesitás de cada uno: CUIT, razón social (como figura en ARCA, NO el nombre de",
492
- " fantasía), condición frente al IVA, en qué cuenta cobra, y si factura semanal o",
493
- " mensual. Preguntá TODO eso antes de ejecutar; no inventes ninguno.",
494
- " El corte queda en HOY: lo anterior no se factura desde el sistema (ya se facturó",
495
- " por fuera y emitirlo de nuevo sería un duplicado).",
496
- "",
497
- "PASO 3 — CONFIRMAR → `facturacion_estado` de nuevo. Un cliente está listo cuando",
498
- " tiene CUIT, razón social, condición IVA, cuenta de cobro con emisor y está",
499
- " habilitado. Si falta algo, el estado te dice cuál.",
500
- "",
501
- "CÓMO SE FACTURA DESPUÉS (no por acá):",
502
- " • El cliente entra a su portal (Mi Cuenta → 🧾 Tus facturas), revisa el período",
503
- " cerrado y toca «Emitir factura». Solo aparece si: el período cerró, no tiene",
504
- " reclamos abiertos, no está facturado y NO tiene saldo pendiente.",
505
- " • Si el importe no coincide con lo acordado, el cliente RECLAMA la liquidación y",
506
- " el staff la corrige; el botón usa siempre lo liquidado, nunca un número a mano.",
507
- " • El staff puede emitir desde la PWA (Liquidaciones Anteriores → Facturar).",
508
- ].join("\n"),
509
-
510
- alta_operador: [
511
- "FLUJO — ALTA DE UN OPERADOR DE NODO (staff que administra)",
512
- "1) Datos: email (con el que loguea), nombre y apellido (obligatorios), teléfono.",
513
- "2) Permisos: qué secciones va a manejar (gestión, finanzas, mensajeros, precios, wms,",
514
- " reportes, datos). Si es el dueño del nodo, después habilitalo como admin de nodo.",
515
- " → `operador_crear` (te devuelve una CLAVE TEMPORAL para pasarle; la cambia al entrar).",
516
- "3) ¿Es dueño/mano derecha? `usuario_habilitar_rol` para admin de nodo (solo admin global).",
517
- "4) Pasale la clave temporal por un canal seguro. Nunca la inventes vos.",
518
- ].join("\n"),
519
-
520
- alta_chofer: [
521
- "FLUJO — ALTA DE UN CHOFER (mensajero/cadete)",
522
- "1) Datos: nombre y apellido (obligatorios), teléfono, email. → `chofer_crear` (devuelve CLAVE TEMPORAL).",
523
- "2) Si además cobra su ganancia contra su cuenta, pasá `mensajeroNombre` (el nombre con",
524
- " el que figura en el Maestro) para el macheo.",
525
- "3) Zona: asignale su metazona con `asignar_mensajero_zona` para que le lleguen envíos.",
526
- "4) Pasale la clave temporal; la cambia al primer ingreso.",
527
- ].join("\n"),
528
- };
529
-
530
- /** Devuelve el paso a paso de un objetivo, o el índice de flujos disponibles. */
531
- export function renderFlujo(q) {
532
- const key = (q ?? "").toLowerCase().trim().replace(/[\s-]+/g, "_");
533
- if (key && FLUJOS[key]) return FLUJOS[key];
534
- const found = key ? Object.keys(FLUJOS).find((k) => k.includes(key) || key.includes(k)) : null;
535
- if (found) return FLUJOS[found];
536
- return [
537
- "FLUJOS GUIADOS disponibles (paso a paso para no dejar nada a medias):",
538
- ...Object.keys(FLUJOS).map((k) => ` • ${k}`),
539
- "",
540
- "Pedí uno, ej.: flujo alta_cliente",
541
- ].join("\n");
542
- }
1
+ // ============================================================================
2
+ // ⚠️ ARCHIVO GENERADO — NO EDITAR A MANO.
3
+ // Fuente: backend/src/services/mcp-docs.ts → regenerar con `cd backend && npm run mcp:docs`.
4
+ // El test backend/src/coherencia.test.ts falla si este archivo no coincide con la fuente.
5
+ // ============================================================================
6
+ const TOPICS = {
7
+ facturacion_marketplace: [
8
+ "FACTURACI\xD3N DEL MARKETPLACE DE RUTAS P\xDABLICAS",
9
+ "",
10
+ "Adjudicar una ruta (ruta_adjudicar) es OPERATIVO: cierra la subasta, marca el",
11
+ "nodo ganador (nodoToma) y deja registrada la OBLIGACI\xD3N al precio de la oferta",
12
+ "(precioAcordado). NO crea ning\xFAn asiento autom\xE1tico de facturaci\xF3n semanal, ni",
13
+ "en rendiciones, ni en comisiones de afiliado, ni en el clearing entre nodos.",
14
+ "El marketplace NO mueve dinero.",
15
+ "",
16
+ "Es decir: adjudicar \u2260 colecta. Una colecta entre nodos s\xED genera un \xEDtem de",
17
+ "clearing; una ruta adjudicada, NO. El pago entre el nodo que publica y el que",
18
+ "toma se acuerda y se salda MANUALMENTE (efectivo con el cadete, o descont\xE1ndolo",
19
+ "de lo que un nodo ya le debe al otro por otros conceptos).",
20
+ "",
21
+ "C\xF3mo verificarlo desde el MCP:",
22
+ " \u2022 mis_rutas \u2192 estado 'adjudicada', nodoToma y precioAcordado de cada ruta tuya.",
23
+ " \u2022 rendiciones / comisiones_afiliado_ver \u2192 NO listar\xE1n la ruta adjudicada.",
24
+ "",
25
+ "Qu\xE9 falta (roadmap, todav\xEDa NO implementado): un settlement autom\xE1tico que,",
26
+ "al adjudicar, cargue el precioAcordado en la cuenta corriente entre los dos",
27
+ "nodos. Mientras no exista, trat\xE1 el pago como manual y avis\xE1selo al usuario."
28
+ ].join("\n"),
29
+ dinero: [
30
+ "DINERO \u2014 QU\xC9 NO HACE NUNCA EL MCP",
31
+ "",
32
+ "El MCP nunca mueve plata: no registra pagos de vendedores, no factura, no",
33
+ "concilia caja, no imputa cobros/haberes en cuentas corrientes. Hay una denylist",
34
+ "de dinero (mcpMoneyGuard) heredada del backend. Las \xFAnicas escrituras 'con",
35
+ "olor a plata' son configuraciones de TARIFA (colecta_configurar, precio_",
36
+ "actualizar, afiliacion_crear) o el REGISTRO de una obligaci\xF3n (comision_liquidar",
37
+ "marca comisiones ya devengadas como pagadas; ruta_adjudicar deja el precio",
38
+ "acordado). Ver tema facturacion_marketplace."
39
+ ].join("\n"),
40
+ aislamiento: [
41
+ "AISLAMIENTO ENTRE NODOS",
42
+ "",
43
+ "Cada token de MCP pertenece a un usuario. Todos los tools llaman a la API REST",
44
+ "del backend con ese token, as\xED que heredan el MISMO gateo que la web:",
45
+ "requireAuth + requirePermiso + scope por nodo/idCliente. Un vendedor solo ve",
46
+ "SUS env\xEDos/stock; un operador solo su nodo; el admin global ve la red. Ning\xFAn",
47
+ "tool puede leer ni escribir datos de otro nodo (salvo lo que es p\xFAblico por",
48
+ "dise\xF1o, como el marketplace de rutas abiertas). Si un tool devuelve 403 es el",
49
+ "aislamiento del backend, no un bug."
50
+ ].join("\n")
51
+ };
52
+ const GUIAS = {
53
+ mensajero: [
54
+ "GU\xCDA DEL MENSAJERO \u2014 registrar lo que colect\xE1s, en el momento",
55
+ "",
56
+ "La idea: cuando pas\xE1s a colectar a un cliente, en vez de anotar a mano, me",
57
+ "mand\xE1s las FOTOS y yo cargo los env\xEDos por vos. Paso a paso:",
58
+ "",
59
+ "1) Sac\xE1 una FOTO de cada paquete que agarr\xE1s: la etiqueta (Flex/Mercado Libre",
60
+ " o la que sea) y, si tiene QR, que se vea. Si el paquete NO tiene etiqueta,",
61
+ " sac\xE1 foto igual (datos del paquete: a qui\xE9n va, direcci\xF3n, tel\xE9fono).",
62
+ "2) Mand\xE1melas todas juntas (un grupo de fotos). Yo leo cada una y registro el",
63
+ " env\xEDo del cliente que est\xE1s colectando:",
64
+ " \u2022 Con etiqueta ML / QR \u2192 lo registro con `envio_desde_etiqueta_ml`",
65
+ " (guardo el QR real, la etiqueta queda reimprimible).",
66
+ " \u2022 Sin etiqueta, con datos \u2192 lo registro con `envio_cargar` como pedido",
67
+ " manual y se le puede generar una etiqueta NUEVA para pegar.",
68
+ "3) IMPORTANTE (aislamiento): SOLO pod\xE9s registrar env\xEDos de clientes de TU",
69
+ " nodo, o de un cliente que tengas COLECTADO (incluye colectas tomadas del",
70
+ " marketplace de colectas). De otros clientes, no.",
71
+ "4) Cada env\xEDo queda 'A retirar', atribuido a vos, con un LINK de etiqueta",
72
+ " imprimible. El vendedor lo ve al toque en su cuenta.",
73
+ "5) Al llegar al nodo, el centro procesa los env\xEDos; a los que cargaste sin",
74
+ " etiqueta se les pega una y salen normal. Despu\xE9s se escanean, aparecen en",
75
+ " el mapa y se te suma la tarifa como cualquier reparto.",
76
+ "",
77
+ "Tips: si una etiqueta se rompi\xF3 o no se lee, carg\xE1 el env\xEDo con `envio_cargar`",
78
+ "y gener\xE1s una nueva. Consult\xE1 el estado de un env\xEDo con `envio_consultar`, tu",
79
+ "ruta con `mi_ruta` y tus colectas con `mis_colectas`.",
80
+ "(Pr\xF3ximamente: aviso en la app para imprimir de una todas las etiquetas que",
81
+ "cargaste por ac\xE1.)"
82
+ ].join("\n"),
83
+ cliente: [
84
+ "GU\xCDA DEL VENDEDOR \u2014 carg\xE1 tus ventas y segu\xED tu operaci\xF3n desde ac\xE1",
85
+ "",
86
+ "1) Cargar una venta/env\xEDo: `envio_cargar` con destinatario, tel\xE9fono,",
87
+ " direcci\xF3n y localidad (montoCobro si cobr\xE1s contra entrega). Te devuelvo",
88
+ " el tracking + un link de etiqueta para imprimir + un link para subir una",
89
+ " foto del paquete.",
90
+ "2) \xBFVend\xE9s por Mercado Libre? Mandame la foto de la etiqueta y la registro",
91
+ " con `envio_desde_etiqueta_ml` (queda con el QR real).",
92
+ "3) Colecta (que te retiren): `mi_colecta` para ver c\xF3mo est\xE1s, `colecta_auto`",
93
+ " para prender/apagar la autom\xE1tica, `colecta_solicitar` para una por \xFAnica vez.",
94
+ "4) Tu negocio: `mi_stock` / `mi_disponible` (para no sobrevender), ",
95
+ " `mi_rentabilidad`, `mis_top_productos`, `mis_kpis`. Sucursales de retiro con",
96
+ " `mis_sucursales` / `sucursal_guardar`.",
97
+ "",
98
+ "Todo lo tuyo es solo tuyo (aislamiento). Nunca toco dinero por ac\xE1."
99
+ ].join("\n"),
100
+ nodo: [
101
+ "GU\xCDA DEL NODO (operador/admin) \u2014 implementar y operar Nexus Flex r\xE1pido",
102
+ "",
103
+ "Puesta a punto:",
104
+ " \u2022 Vendedores: `cliente_crear` / `cliente_editar` (asign\xE1 su lista de precio).",
105
+ " \u2022 Choferes: `chofer_crear` (te devuelvo una clave temporal para pasarle).",
106
+ " \u2022 Colecta: `colecta_configurar` (pago del nodo/mensajero, cobro por vendedor).",
107
+ " \u2022 Zonas: `asignar_mensajero_zona` (mensajero\u2194metazona), `asignar_nodo_zona`",
108
+ " (un nodo de tu grupo cubre una localidad).",
109
+ "",
110
+ "El d\xEDa a d\xEDa:",
111
+ " \u2022 `colecta_pendientes` (hoy + ma\xF1ana) y `colecta_asignar` (que Fulano levante",
112
+ " a tal cliente).",
113
+ " \u2022 Env\xEDos: los cadetes pueden cargar lo que colectan por MCP (foto\u2192env\xEDo); vos",
114
+ " los ves 'A retirar'. Consult\xE1 con `envio_consultar`, correg\xED con",
115
+ " `cobro_corregir` / `envio_reasignar_cliente`.",
116
+ " \u2022 Rendiciones: `rendiciones` (qu\xE9 falta recuperar/rendir).",
117
+ "",
118
+ "Clearing entre nodos (eficiente):",
119
+ " \u2022 Metazonas + grupos log\xEDsticos rutean los env\xEDos al nodo que cubre la zona.",
120
+ " Recib\xEDs lo que te rutean con `envios_por_zona` \u2192 `procesar_zona` (aceptar) o",
121
+ " `rechazar_zona`. El clearing sale de la config de tarifas por grupo.",
122
+ " \u2022 Marketplace de rutas (`rutas_publicas`, `ruta_publicar`/`ofertar`/`adjudicar`):",
123
+ " subasta abierta entre nodos. OJO: adjudicar NO factura autom\xE1tico \u2014 el pago",
124
+ " entre nodos se salda a mano (ver `ayuda tema:facturacion_marketplace`).",
125
+ " \u2022 N\xFAmeros: `kpi_nodo` (P&L, top clientes), `metricas_por_tipo`.",
126
+ "",
127
+ "Aislamiento: todo lo que ves/hac\xE9s es de TU nodo (o tu grupo cuando corresponde)."
128
+ ].join("\n")
129
+ };
130
+ const INTRO = [
131
+ "\u{1F680} Bienvenido a Nexus Flex \u2014 tu operaci\xF3n entera desde el chat.",
132
+ "",
133
+ "Esto no es un men\xFA m\xE1s: le habl\xE1s y se hace. En 5 minutos pod\xE9s dar de alta",
134
+ "clientes y choferes, cargar env\xEDos sacando una FOTO (yo leo la etiqueta), ver tu",
135
+ "plata al d\xEDa, armar zonas, y hasta tomar rutas de otros nodos de la red. Sin",
136
+ "planillas, sin clicks eternos. Cada cosa que hac\xE9s ac\xE1 queda LIVE al instante y",
137
+ "aislada a lo tuyo (nadie ve lo de otro nodo).",
138
+ "",
139
+ "Hac\xE9 la prueba: segu\xED los pasos de abajo, son 3 minutos y te va a quedar clar\xEDsimo",
140
+ "todo lo que pod\xE9s automatizar. \xBFListo? Arrancamos \u{1F447}",
141
+ "",
142
+ "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550",
143
+ ""
144
+ ].join("\n");
145
+ function guiaOnboarding(rol) {
146
+ const cuerpo = rol === "mensajero" ? GUIAS.mensajero : rol === "cliente" ? GUIAS.cliente : rol === "operador" || rol === "admin" ? GUIAS.nodo : [GUIAS.cliente, "", "\u2014 \u2014 \u2014", "", GUIAS.nodo].join("\n");
147
+ return INTRO + cuerpo;
148
+ }
149
+ const GROUPS = [
150
+ { titulo: "General", tools: ["mis_datos", "guia", "flujo", "flujo_registrar", "ayuda", "mcp_version", "sugerencia_crear", "mis_sugerencias"] },
151
+ { titulo: "Vendedor \u2014 mi operaci\xF3n", tools: ["mis_envios", "envio_consultar", "mis_sucursales", "sucursal_guardar", "mi_colecta", "colecta_solicitar", "colecta_auto"] },
152
+ { titulo: "Vendedor \u2014 mi stock y n\xFAmeros", tools: ["mi_stock", "mi_disponible", "mi_rentabilidad", "mis_productos", "mis_top_productos", "mis_kpis"] },
153
+ { titulo: "Mensajero", tools: ["mi_ruta", "mis_colectas"] },
154
+ { titulo: "Env\xEDos (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", "sin_vendedor", "asignar_sin_vendedor", "envio_completar_ciclo", "envio_pago_mensajero", "envio_corregir_estado", "envio_asignar_mensajero"] },
155
+ { titulo: "Colecta y zonas (staff)", tools: ["colecta_ver", "colecta_pendientes", "colecta_historial", "colecta_configurar", "colecta_fija", "colecta_asignar", "colecta_desasignar", "zonas_reparto", "zona_barrios", "zonas_simetria", "asignar_mensajero_zona", "asignar_nodo_zona", "envios_por_zona", "procesar_zona", "rechazar_zona", "grupos_tarifas", "nodo_alias_poner", "zona_dejar", "nodo_link_confirmacion", "zonas_grupo", "zona_mover_metazona", "zona_componer_grupo", "grupo_tarifa_set", "retiros_cargar"] },
156
+ { titulo: "Rendiciones y reclamos", tools: ["rendiciones", "rendicion_revertir", "reclamos_listar"] },
157
+ { titulo: "Marketplace de rutas", tools: ["rutas_publicas", "mis_rutas", "ruta_ofertas", "ruta_publicar", "ruta_ofertar", "ruta_adjudicar"] },
158
+ { titulo: "Afiliados", tools: ["afiliacion_listar", "comisiones_afiliado_ver", "afiliado_crear", "afiliacion_crear", "afiliacion_editar", "comision_liquidar"] },
159
+ { titulo: "M\xE9tricas / KPIs", tools: ["kpi_nodo", "kpi_red", "metricas_por_tipo", "top_productos"] },
160
+ { titulo: "WMS del nodo", tools: ["stock_nodo", "productos_nodo", "producto_crear"] },
161
+ { titulo: "Gesti\xF3n del nodo", tools: ["clientes_del_nodo", "cliente_crear", "cliente_editar", "sucursales_cliente", "sucursal_perfil", "precios_ver", "precio_actualizar", "cuentas_vinculadas", "generar_enlace_vinculacion", "facturacion_estado", "facturacion_preparar_cliente"] },
162
+ { titulo: "Marketing / atribuci\xF3n", tools: ["marketing_config_ver", "marketing_config_guardar"] },
163
+ { titulo: "Liquidaciones (consulta)", tools: ["liquidacion_buscar", "liquidacion_detalle", "liquidaciones_sin_recibir", "liquidacion_excluir_envio", "liquidacion_reincluir_envio", "cuenta_semanal_nodos"] },
164
+ { titulo: "Usuarios / choferes", tools: ["chofer_listar", "chofer_crear", "chofer_editar", "usuario_editar", "operador_crear", "cliente_generar_usuario", "cliente_resetear_clave", "usuario_habilitar_reparto"] },
165
+ { titulo: "Administraci\xF3n (red)", tools: ["nodos_listar", "nodo_crear", "usuario_habilitar_rol", "grupo_crear", "logo_subir", "sugerencias_listar", "cierre_semanal_links", "nodo_renombrar", "grupo_miembros", "grupo_sumar_nodo", "grupo_expulsar_nodo", "grupo_admin_permiso"] }
166
+ ];
167
+ const TOOL_DOCS = {
168
+ // Fichas agregadas 23/09 (esos tools no tenían ayuda; ahora lo exige src/coherencia.test.ts).
169
+ sin_vendedor: { "que": "Muestra las cuentas de Mercado Libre y env\xEDos cargados con foto que tu nodo tiene sin vendedor.", "uso": "Sin par\xE1metros obligatorios. Devuelve cuentas (cu\xE1ntos env\xEDos, ejemplos de destinatarios, si ya est\xE1 vinculada), env\xEDos (los cargados con foto), y clientes del nodo (con si tienen usuario). Admin global: pas\xE1 `nodo` para elegir/acotar el nodo.", "ej": "sin_vendedor nodo:6 \u2192 ves las cuentas y env\xEDos hu\xE9rfanos del nodo 6 para asignarlos con asignar_sin_vendedor.", "no": "No asigna (para eso us\xE1 asignar_sin_vendedor); no mueve dinero." },
170
+ asignar_sin_vendedor: { "que": "Asigna a UN vendedor las cuentas de Mercado Libre y env\xEDos sin vendedor de tu nodo, en un paso, y devuelve enlaces de vinculaci\xF3n.", "uso": "Pas\xE1 idCliente (vendedor existente) O nuevoVendedor (nombre, se crea); cuentasML (array de n\xFAmeros de usuario ML) y/o envioIds (ids de sin_vendedor); opcionales usuarioEmail/usuarioNombre/usuarioApellido para crearle usuario si no tiene. Admin global: `nodo` para elegir/acotar el nodo.", "ej": "asignar_sin_vendedor idCliente:130 cuentasML:['123456','789012'] envioIds:[1,2,3] \u2192 le pasa las cuentas y env\xEDos, devuelve los enlaces de vinculaci\xF3n.", "no": "No vincula las cuentas por s\xED solo: devuelve el enlace para que el vendedor lo autorice en 48 hs; no mueve dinero." },
171
+ liquidacion_excluir_envio: { "que": "Saca un env\xEDo del cobro de una liquidaci\xF3n YA emitida y re-suma el total.", "uso": "id (liquidaci\xF3n), tracking (del env\xEDo a excluir), motivo (obligatorio, m\xEDnimo 10 caracteres, lo ve el vendedor). Requiere permiso finanzas. Si esa liquidaci\xF3n ya est\xE1 en una factura ARCA emitida, la rechaza (har\xEDa falta nota de cr\xE9dito).", "ej": "liquidacion_excluir_envio id:1234 tracking:'NFABC123' motivo:'el nodo no lo entreg\xF3, qued\xF3 en el centro'.", "no": "No emite nota de cr\xE9dito: si la liquidaci\xF3n ya est\xE1 en una factura de ARCA emitida, lo rechaza. Re-suma la liquidaci\xF3n y ajusta la cuenta corriente del vendedor (no es un pago ni un cobro)." },
172
+ liquidacion_reincluir_envio: { "que": "Lo inverso de liquidacion_excluir_envio: devuelve al cobro un env\xEDo que se hab\xEDa excluido.", "uso": "id (liquidaci\xF3n), tracking (del env\xEDo a reincluir), motivo (obligatorio, m\xEDnimo 10 caracteres, ej. 'el nodo s\xED lo entreg\xF3, hay foto'). Requiere permiso finanzas. Mismo guard de factura emitida.", "ej": "liquidacion_reincluir_envio id:1234 tracking:'NFABC123' motivo:'verif con el nodo, s\xED lo entreg\xF3'.", "no": "Mismo cuidado que excluir: si la liquidaci\xF3n ya est\xE1 facturada en ARCA, lo rechaza. Re-suma y ajusta la cuenta corriente; no es un pago ni un cobro." },
173
+ cuenta_semanal_nodos: { "que": "La cuenta de TU nodo con cada nodo con el que se pasaron paquetes por un grupo (de lunes a s\xE1bado que se congela el mi\xE9rcoles siguiente).", "uso": "Sin par\xE1metros lista lo que est\xE1 EN VIVO, a\xFAn no cerrado. Con `cierre` (id): ese cierre congelado. Con `lista:true`: tus cierres semanales con su neto. Admin global: `nodo` para elegir/acotar el nodo.", "ej": "cuenta_semanal_nodos lista:true \u2192 ves tus cierres; cuenta_semanal_nodos cierre:100 \u2192 ese cierre congelado con el detalle por nodo.", "no": "No cierra semanas (se cierran solas); no mueve dinero." },
174
+ cierre_semanal_links: { "que": "Para cada nodo de un cierre semanal, el link SIN LOGIN donde ve su cuenta y la baja en PDF.", "uso": "Opcional cierre (id del cierre; default: el \xFAltimo). Devuelve primero los nodos SIN usuario: a esos pas\xE1selo por WhatsApp. Solo admin global.", "ej": "cierre_semanal_links cierre:100 \u2192 recib\xEDs los links de todos los nodos para mandarles por WhatsApp.", "no": "No env\xEDa por WhatsApp (solo genera el link); solo lectura." },
175
+ nodo_renombrar: { "que": "Cambia la raz\xF3n social (nombre real) de un nodo \u2014 lo ve TODO el mundo.", "uso": "nodo (nombre actual o tu apodo), nombreNuevo (nombre nuevo). Solo admin global. No toca dinero.", "ej": "nodo_renombrar nodo:'FastCorreo' nombreNuevo:'FastCorreo Log\xEDstica'.", "no": "No es un apodo personal (para eso us\xE1 nodo_alias_poner); la cambio es global." },
176
+ grupos_tarifas: { "que": "Los grupos log\xEDsticos de tu nodo (admin global: todos) con los nodos que los integran y la TARIFA de clearing por zona de cada uno.", "uso": "Sin par\xE1metros. Admin global: cualquier nodo; operador: sus grupos. Es lo que mir\xE1s para auditar por qu\xE9 un traspaso entre nodos se cobr\xF3 lo que se cobr\xF3.", "ej": "grupos_tarifas \u2192 ves Bonorino, Portela, etc. con sus nodos y cu\xE1nto cobra cada zona.", "no": "No modifica tarifas (para eso us\xE1 grupo_tarifa_set); solo lectura." },
177
+ nodo_alias_poner: { "que": "Le pon\xE9s TU propio apodo a un nodo para reconocerlo m\xE1s f\xE1cil \u2014 es personal.", "uso": "nodo (nombre actual o tu apodo), alias (el apodo nuevo; vac\xEDo para sacarlo). Pod\xE9s usar ese apodo en cualquier herramienta que pida un nodo.", "ej": `nodo_alias_poner nodo:'Log\xEDstica Portela' alias:'RL Zona Sur' \u2192 de ah\xED en m\xE1s pod\xE9s decir 'asignar_nodo_zona nodo:"RL Zona Sur"'.`, "no": "No es un cambio global (nodo_renombrar s\xED); solo te lo ve a vos." },
178
+ zona_dejar: { "que": "Te sac\xE1s (a tu nodo) de una zona de un grupo log\xEDstico que ten\xEDas asignada \u2014 self-service.", "uso": "grupo (nombre o id), metazona (nombre/localidad que dej\xE1s). Si eras el \xFAnico responsable, la zona pasa a LIBERADA y se avisa al grupo.", "ej": "zona_dejar grupo:'Portela' metazona:'Ensenada' \u2192 te sac\xE1s de esa zona.", "no": "No te deja sacarte al \xFAltimo admin de un grupo; no mueve dinero." },
179
+ nodo_link_confirmacion: { "que": "Devuelve el link FIJO (sin login) donde un nodo sin usuarios propios ve sus responsabilidades de zona y las confirma.", "uso": "nodo (nombre o tu apodo del nodo). El link es el mismo siempre para ese nodo.", "ej": "nodo_link_confirmacion nodo:'Feeder Sur' \u2192 recib\xEDs el link para mandarle por WhatsApp a ese nodo que no tiene tel\xE9fono cargado.", "no": "No entra en el link por vos; solo devuelve la URL." },
180
+ zonas_grupo: { "que": "Lista las zonas de un grupo log\xEDstico con el nodo responsable de cada una, marcando las LIBERADAS y si pod\xE9s reasignarlas.", "uso": "grupo (nombre o id). Visible a cualquier miembro del grupo. Solo lectura.", "ej": "zonas_grupo grupo:'Bonorino' \u2192 ves qu\xE9 nodo cubre cada zona y cu\xE1les est\xE1n libres (sin responsable).", "no": "No asigna (us\xE1 asignar_nodo_zona + `grupo`)." },
181
+ zona_mover_metazona: { "que": "Reclasifica una metazona/localidad: la saca de la zona de grupo que la contiene y la mete en OTRA zona del MISMO grupo.", "uso": "grupo, metazona (a mover), zonaDestino (nombre, id, o una metazona que ya tenga). Solo comisi\xF3n (admin) del grupo o admin global.", "ej": "zona_mover_metazona grupo:'Portela' metazona:'El Palomar' zonaDestino:'Tres de Febrero' \u2192 paso El Palomar de Mor\xF3n a Tres de Febrero.", "no": "No cambia el nodo responsable (solo reclasifica de zona); no mueve dinero." },
182
+ zona_componer_grupo: { "que": "Crea o edita una zona de un grupo log\xEDstico: la ubica por nombre/id, le puede sumar un nodo responsable y agrega/quita metazonas de su composici\xF3n.", "uso": "grupo, zona (nombre o id; se crea si no existe), nodo (responsable, opcional), agregar (array de metazonas a sumar), quitar (array a quitar). Solo comisi\xF3n del grupo o admin global.", "ej": "zona_componer_grupo grupo:'Portela' zona:'Flores' nodo:'FastCorreo' agregar:['CABA \xB7 Flores','Flores'] \u2192 crea/edita la zona y la suma las metazonas.", "no": "No mueve dinero; importante: el ruteo matchea EXACTAMENTE por metazona, as\xED que una zona CABA necesita 'CABA \xB7 Barrio' Y 'Barrio' separadas." },
183
+ grupo_tarifa_set: { "que": "Fija la tarifa de clearing por zona (cercana/media/lejana/muyLejana) de TODO un grupo en una sola llamada.", "uso": "grupo, y opcionalmente los montos (cercana, media, lejana, muyLejana). Con `soloDef:true`: solo el default del grupo, sin tocar los nodos miembro. Solo comisi\xF3n (admin) del grupo o admin global.", "ej": "grupo_tarifa_set grupo:'Portela' cercana:2700 media:3200 lejana:4500 muyLejana:6000 \u2192 todos los nodos del grupo quedan en esa tarifa.", "no": "No mueve dinero; es config del clearing." },
184
+ envio_completar_ciclo: { "que": "Rellena los pasos que le FALTAN a un env\xEDo ya cerrado: Colectado, En centro y En camino, cada uno a nombre de quien lo hizo.", "uso": "trackings (array; ej. ['NFABC123','TN-2057600163']), fecha (d\xEDa real en que se hicieron los pasos, dd/mm/aaaa), y por cada paso: colectado, procesado, enCamino (nombre o id). Si no pas\xE1s una persona, ese paso no se agrega.", "ej": "envio_completar_ciclo trackings:['NFABC123','TN-456'] fecha:'14/09/2026' colectado:'Maxi' procesado:'Ana' enCamino:'Juan' \u2192 les agrega los pasos faltantes con las personas que los hicieron.", "no": "No duplica pasos ya existentes; no inventa autores; no cambia el estado actual del env\xEDo ni mueve dinero." },
185
+ envio_pago_mensajero: { "que": "Carga lo que se le paga a quien reparti\xF3 cada env\xEDo y, si falta el nombre del mensajero, lo completa.", "uso": "trackings (array), valor (pesos por env\xEDo), mensajero (opcional: nombre o id, para asignar y pagar en un paso), incluirNoEntregados (opcional: true para pagar tambi\xE9n Cancelados/Devueltos).", "ej": "envio_pago_mensajero trackings:['NFABC','TN-456'] valor:2500 mensajero:'Maxi' \u2192 los paga a Maxi a $2500 cada uno.", "no": "No entra en la cuenta corriente del cadete (el clearing se sella por nombre); por defecto NO toca env\xEDos no entregados; no mueve dinero, registra el valor a pagar." },
186
+ envio_corregir_estado: { "que": "Corrige el ESTADO de un env\xEDo ya cargado para deshacer algo que qued\xF3 mal.", "uso": "tracking, estado (destino: ej. 'Cancelado', 'En camino', 'Entregado'), motivo (obligatorio, m\xEDnimo 10 caracteres, queda en el historial). Acepta estados terminales ('Cancelado', 'Rechazado por el comprador').", "ej": "envio_corregir_estado tracking:'NFABC123' estado:'Cancelado' motivo:'error del sistema, se marc\xF3 entregado sin serlo'.", "no": "No toca banderas de devoluci\xF3n ni cobro; no mueve dinero; el motivo es obligatorio." },
187
+ envio_asignar_mensajero: { "que": "Asigna \u2014o QUITA con `quitar:true`\u2014 el MENSAJERO de env\xEDos ya cargados.", "uso": "trackings (array), mensajero (nombre o id; busca entre TODOS los que pueden repartir, no solo 'mensajero'). Con `quitar:true` lo deja SIN mensajero. Si el nombre matchea a varios, devuelve los candidatos con su id.", "ej": "envio_asignar_mensajero trackings:['NFABC','TN-456'] mensajero:7 \u2192 les asigna al mensajero con id 7. / envio_asignar_mensajero trackings:['NFABC'] quitar:true \u2192 lo deja SIN mensajero.", "no": "No mueve dinero (el pago al cadete se liquida por NOMBRE); no entra en el clearing." },
188
+ retiros_cargar: { "que": "Carga de una vez la LISTA DE RETIROS del d\xEDa (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\xFAmero 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\xF3n el mismo d\xEDa no duplica." },
189
+ facturacion_estado: { que: "Diagn\xF3stico de facturaci\xF3n ARCA: emisores del nodo y si est\xE1n completos, cuentas de dinero sin emisor propio, y por cada cliente qu\xE9 dato le falta para poder facturarle.", uso: "Sin argumentos = los clientes YA habilitados. cliente:<nombre|id> = ese cliente, aunque todav\xEDa no est\xE9 habilitado.", ej: "facturacion_estado cliente:Segucentro \u2192 te dice si le falta CUIT, raz\xF3n social, condici\xF3n IVA, emisor o la habilitaci\xF3n.", no: "No devuelve importes ni factura nada. Es estado de configuraci\xF3n." },
190
+ facturacion_preparar_cliente: { que: "Deja un cliente listo para facturar: CUIT, raz\xF3n social (el titular del CUIT), condici\xF3n IVA, cuenta donde cobra (define QUI\xC9N 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\xE1mite en ARCA: eso necesita la Clave Fiscal de esa persona y se hace en la web de ARCA." },
191
+ ayuda: { que: "Devuelve esta documentaci\xF3n: \xEDndice de herramientas o la ficha completa de una (qu\xE9 hace, params, ejemplo, qu\xE9 NO hace) y t\xF3picos transversales.", uso: "Sin argumentos = \xEDndice. `tool:<nombre>` = ficha de esa herramienta. `tema:<clave>` = t\xF3pico (facturacion_marketplace, dinero, aislamiento).", ej: "ayuda tool:ruta_adjudicar \u2192 te explica c\xF3mo adjudicar y aclara que no mueve dinero.", no: "No ejecuta nada ni consulta datos; es solo lectura de documentaci\xF3n est\xE1tica." },
192
+ guia: { que: "Gu\xEDa de ARRANQUE para tu rol: la metodolog\xEDa para empezar a usar Nexus Flex r\xE1pido, con las herramientas de cada paso (mensajero: foto\u2192env\xEDo al colectar; vendedor: cargar ventas; nodo: implementar + operar + clearing).", uso: "Sin argumentos = tu gu\xEDa seg\xFAn tu rol. `rol:<mensajero|cliente|nodo>` para ver otra.", ej: "Reci\xE9n conect\xE1s como mensajero \u2192 guia \u2192 te explico c\xF3mo registrar lo que colect\xE1s sacando fotos.", no: "No ejecuta acciones; es la metodolog\xEDa. Para el detalle de una herramienta us\xE1 `ayuda tool:<x>`." },
193
+ 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\xE9 datos faltan.", uso: "Sin argumento = lista los flujos. `objetivo:<alta_cliente|alta_operador|alta_chofer>` = el paso a paso.", ej: "flujo alta_cliente \u2192 te digo que despu\xE9s de crearlo hay que preguntar qu\xE9 lista de precios le cobr\xE1s.", no: "No ejecuta las acciones; te da la secuencia. Cada paso usa su propia herramienta." },
194
+ mis_datos: { que: "Tu usuario, rol y nodo/cliente: define tu ALCANCE (qu\xE9 pod\xE9s ver/hacer).", uso: "Sin par\xE1metros.", ej: "Antes de operar, mis_datos \u2192 confirm\xE1s que sos operador del nodo 6 y qu\xE9 permisos ten\xE9s.", no: "No lista otros usuarios ni cambia nada." },
195
+ mcp_version: { que: "Versi\xF3n del MCP corriendo, por qu\xE9 v\xEDa (remoto/npx) y las novedades recientes.", uso: "Sin par\xE1metros.", ej: "El usuario pregunta 'tengo lo \xFAltimo' \u2192 mcp_version.", no: "No actualiza el MCP." },
196
+ 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." },
197
+ mis_sugerencias: { que: "Tus PROPIAS sugerencias/pedidos de mejora y en qu\xE9 estado est\xE1n (Recibida / En evaluaci\xF3n / \u2705 Implementada / Descartada). Cada usuario ve solo las suyas.", uso: "Sin argumentos.", ej: "mis_sugerencias \u2192 ves cu\xE1les de tus pedidos ya se aplicaron.", no: "No muestra las de otros usuarios (eso es `sugerencias_listar`, solo staff); no cambia su estado." },
198
+ flujo_registrar: { que: "Registra un 'flujo aprendido': cuando el asistente tuvo que hacer VARIAS preguntas para descubrir qu\xE9 quer\xEDas (no lo pediste directo), guarda el objetivo + los pasos que lo aclararon, para armar un flujo directo.", uso: "objetivo (lo que terminaste queriendo) + pasos (las preguntas/decisiones + herramientas usadas). Lo llama el asistente solo cuando corresponde.", ej: "flujo_registrar objetivo:'cargar 5 paquetes ML de un nodo nuevo' pasos:'pregunt\xF3 de qui\xE9n eran \u2192 nodo no estaba \u2192 cre\xF3 provisorio \u2192 carg\xF3 reusando QR'.", no: "No crea el flujo autom\xE1ticamente (lo revisa el equipo); no mueve dinero." },
199
+ mis_envios: { que: "Tus env\xEDos/paquetes (solo los tuyos).", uso: "estado opcional (filtra por estado).", ej: "mis_envios estado:'En camino' \u2192 los que est\xE1n en reparto.", no: "No muestra env\xEDos de otros vendedores; no los edita." },
200
+ envio_consultar: { que: "Busca UN env\xEDo por tracking/c\xF3digo y devuelve estado, historial, destino y datos de cobro/cambio.", uso: "codigo (tracking o c\xF3digo). Vendedor: entre SUS env\xEDos; staff: dentro de su nodo. Si hay varios matches, pas\xE1 el tracking completo.", ej: "El vendedor pregunta '\xBFd\xF3nde est\xE1 44000...?' \u2192 envio_consultar codigo:'44000123'.", no: "No cambia el estado del env\xEDo; no busca fuera de tu alcance." },
201
+ envio_estado: { que: "Narrativa de estado + ETA de un env\xEDo para responder '\xBFcu\xE1ndo llega?': mensajero asignado, cu\xE1ntos env\xEDos lleva en la ruta, en qu\xE9 posici\xF3n va este y un estimado (posici\xF3n \xD7 min/parada). Marca problemas (nunca despachado, sin mensajero, mensajero detenido).", uso: "tracking. Staff y mensajero, scopeado a tu nodo. Es una estimaci\xF3n explicable, no exacta.", ej: "El vendedor pregunta '\xBFcu\xE1ndo entregan NFABC?' \u2192 envio_estado tracking:'NFABC' \u2192 'En camino con Maxi, 8 env\xEDos en ruta, este va 3\xBA, ~24 min'.", no: "No cambia nada; la ETA es orientativa." },
202
+ envios_trabados: { que: "Lista los env\xEDos 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\xF3n) o en_camino_sin_cerrar (en camino +24h sin cerrarse \u2192 marcalo entregado).", uso: "Sin par\xE1metros. Staff. Ordenados por severidad. Misma detecci\xF3n que el aviso proactivo a operativos y admins del nodo.", ej: "El jefe pregunta '\xBFqu\xE9 qued\xF3 trabado / por cerrar hoy?' \u2192 envios_trabados.", no: "Solo lo detecta/lista; no lo destraba ni lo marca entregado (eso lo hac\xE9s en la app / esc\xE1ner)." },
203
+ planilla_reporte: { que: "Resumen de los env\xEDos cargados por el m\xE9todo 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' \u2192 cu\xE1ntos 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." },
204
+ mis_sucursales: { que: "Tus sucursales / puntos de retiro (la principal = tu direcci\xF3n de retiro), con horario de corte y ventanas.", uso: "Sin par\xE1metros.", ej: "mis_sucursales \u2192 confirm\xE1s desde d\xF3nde te colectan.", no: "No las edita (para eso, sucursal_guardar)." },
205
+ sucursal_guardar: { que: "Crea o edita una sucursal tuya (punto de retiro). Geocodifica la direcci\xF3n sola.", uso: "id vac\xEDo = nueva; nombre obligatorio; principal:true la vuelve tu direcci\xF3n 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." },
206
+ mi_colecta: { que: "Estado de tu colecta (retiro): si la autom\xE1tica est\xE1 prendida, si pediste una por \xFAnica vez y el aviso de costo.", uso: "Sin par\xE1metros.", ej: "mi_colecta \u2192 ver si ma\xF1ana te pasan a buscar.", no: "No la prende/apaga (colecta_auto) ni la solicita (colecta_solicitar)." },
207
+ colecta_solicitar: { que: "Ped\xEDs que te retiren los env\xEDos POR \xDANICA VEZ, aunque tengas la autom\xE1tica apagada.", uso: "Sin par\xE1metros. Devuelve el aviso de costo si aplica.", ej: "colecta_solicitar \u2192 aparec\xE9s en el panel de colecta del nodo.", no: "No prende la colecta autom\xE1tica permanente; no mueve dinero." },
208
+ colecta_auto: { que: "Prende (true) o apaga (false) tu colecta AUTOM\xC1TICA.", uso: "activa (bool).", ej: "colecta_auto activa:false \u2192 dej\xE1s de que te retiren; llev\xE1s vos al dep\xF3sito.", no: "No agenda una colecta puntual (para eso colecta_solicitar)." },
209
+ mi_stock: { que: "Tu stock f\xEDsico (solo tus productos).", uso: "Sin par\xE1metros.", ej: "mi_stock \u2192 ver unidades por SKU.", no: "No muestra disponible-para-vender (us\xE1 mi_disponible)." },
210
+ mi_disponible: { que: "Disponible por SKU = f\xEDsico \u2212 comprometido; marca \u26A0\uFE0F si est\xE1 bajo el m\xEDnimo.", uso: "Sin par\xE1metros.", ej: "mi_disponible \u2192 cu\xE1ntas unidades pod\xE9s seguir vendiendo sin sobrevender.", no: "No repone stock." },
211
+ mi_rentabilidad: { que: "Margen por SKU (precio \u2212 flete real \u2212 COGS).", uso: "desde/hasta YYYY-MM-DD (default: mes en curso).", ej: "mi_rentabilidad desde:2026-08-01 \u2192 margen del mes.", no: "No incluye gastos fijos del negocio; es por SKU." },
212
+ mis_productos: { que: "Tu cat\xE1logo de productos.", uso: "Sin par\xE1metros.", ej: "mis_productos \u2192 ver SKUs cargados.", no: "No da de alta (producto_crear, si el nodo tiene WMS)." },
213
+ mis_top_productos: { que: "Ranking de tus productos m\xE1s despachados.", uso: "desde/hasta YYYY-MM-DD (default: mes).", ej: "mis_top_productos \u2192 qu\xE9 se vende m\xE1s.", no: "No muestra rentabilidad (us\xE1 mi_rentabilidad)." },
214
+ mis_kpis: { que: "Tus m\xE9tricas + benchmark an\xF3nimo de tu nodo.", uso: "desde/hasta opcionales.", ej: "mis_kpis \u2192 tu tasa de entrega vs. el promedio del nodo.", no: "No revela datos de otros vendedores (el benchmark es an\xF3nimo)." },
215
+ mi_ruta: { que: "Tus entregas asignadas: las paradas de tu ruta del d\xEDa.", uso: "Sin par\xE1metros.", ej: "mi_ruta \u2192 orden de reparto de hoy.", no: "No marca entregas (eso se hace escaneando en la app)." },
216
+ mis_colectas: { que: "Las colectas/retiros que ten\xE9s asignados.", uso: "Sin par\xE1metros.", ej: "mis_colectas \u2192 a qu\xE9 vendedores ten\xE9s que ir a buscar.", no: "No las completa/confirma desde ac\xE1." },
217
+ envio_cargar: { que: "Registra un env\xEDo 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\xEDo NO es de hoy: es la que define en qu\xE9 semana se liquida; no se admite futura.", ej: "Staff: envio_cargar cliente:'Distri Sur' destinatario:'Ana' telefono:'11...' direccion:'Belgrano 100' localidad:'Lan\xFAs' 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\xEDa sus paquetes de per\xEDodo de liquidaci\xF3n)." },
218
+ envio_entregar: { que: "Marca como ENTREGADO uno o varios env\xEDos que YA est\xE1n cargados, por tracking \u2014 y con `fecha`, el d\xEDa REAL en que se entreg\xF3.", 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\xF3 por otro lado (lo carg\xF3 el vendedor o una integraci\xF3n ML/TiendaNube) ese par\xE1metro se ignora y el env\xEDo queda 'A retirar'. Sin `fecha` el historial dice que se entreg\xF3 ahora, que es mentira cuando la tanda se controla por foto d\xEDas despu\xE9s; sobre un env\xEDo 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' \u2192 lo cierra con el d\xEDa en que sali\xF3 y sella la log\xEDstica 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\xE1 la entrega del cadete en la PWA); no revierte una entrega." },
219
+ envio_procesar: { que: "Marca un env\xEDo como PROCESADO / recibido en el centro de distribuci\xF3n (queda 'En centro de distribuci\xF3n') por su tracking, sin escanear.", uso: "tracking del env\xEDo. Scopeado a tu nodo/grupo (solo proces\xE1s lo que te corresponde). Es la misma acci\xF3n 'procesar' del esc\xE1ner.", ej: "envio_procesar tracking:'NFABC123' \u2192 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." },
220
+ envio_desde_etiqueta_ml: { que: "Registra un env\xEDo a partir de lo que VOS (Claude) le\xEDste de la foto de una etiqueta de Mercado Libre.", uso: "Le\xE9 la etiqueta: mlShipmentId + (si pod\xE9s) 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\xEDas anteriores, pas\xE1 `fecha` (solo staff, dd/mm/aaaa): fecha el env\xEDo Y toda la cadena de `avanzarA` (cada estado con su hora de ESE d\xEDa: 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\xEDo YA estaba cargado tambi\xE9n le mueve la fecha, salvo que ese per\xEDodo ya est\xE9 liquidado (ah\xED rechaza nombrando la liquidaci\xF3n).", 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\xE9s que pasar `cliente`." },
221
+ envio_reasignar_cliente: { que: "Mueve UN env\xEDo (por tracking) a otro cliente/vendedor de tu nodo cuando se carg\xF3 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\xEDo." },
222
+ envio_mensajero_externo: { que: "Registra env\xEDos 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\xF3n de mensajeros. Sin valor, figuran FALTA VALOR.", ej: 'envio_mensajero_externo envioIds:[123,124] nombre:"Juan (moto)" valorPorEnvio:3000.', no: "No reasigna env\xEDos ya entregados; no paga (registra el valor)." },
223
+ envio_editar_zona: { que: "Corrige la localidad/zona/partido/CP de UN env\xEDo (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\xE1s zona, se deriva de la localidad. El cp se guarda solo con d\xEDgitos ('1.832,00' \u2192 '1832'). Staff: solo env\xEDos de tu nodo; admin global: cualquiera.", ej: "La etiqueta tra\xEDa 'san martin - Lan\xFAs' pisadas \u2192 envio_editar_zona tracking:'NFD5L122' localidad:'Lan\xFAs'. CP con la altura de la calle \u2192 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\xEDo); no mueve dinero." },
224
+ envio_editar_fecha: { que: "Corrige la FECHA de UN env\xEDo ya cargado (por tracking): el que se subi\xF3 atrasado sin `fecha` qued\xF3 con el d\xEDa del alta. Es la fecha que define en qu\xE9 SEMANA se le liquida al vendedor.", uso: "tracking + fecha (dd/mm/aaaa o aaaa-mm-dd). No se admite futura. Si ese per\xEDodo ya est\xE1 liquidado, lo rechaza nombrando la liquidaci\xF3n (rehacerla la decid\xEDs vos). El primer estado del historial se mueve con el env\xEDo; los dem\xE1s quedan como se registraron. Staff: solo env\xEDos de tu nodo; admin global: cualquiera.", ej: "Subiste el martes reci\xE9n el viernes y todo qued\xF3 con fecha del viernes \u2192 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\xF3n; no mueve dinero." },
225
+ envio_asignar_grupo: { que: "Asigna/rutea UN env\xEDo (por tracking) a un GRUPO log\xEDstico tuyo (por nombre o id) y, si todav\xEDa no sali\xF3, lo DESPACHA \u2014 igual que 'Asignar grupo' del esc\xE1ner.", uso: "tracking + grupo (nombre o id de TUS grupos, ej. 'Portela'). Define con qu\xE9 grupo sali\xF3 (tarifa del clearing). Si el env\xEDo est\xE1 'A retirar'/'En centro' lo pasa a 'En camino'; si YA est\xE1 'Entregado'/'En camino' NO le toca el estado (solo le queda el grupo \u2192 sirve para corregir el clearing de una planilla vieja). Solo tus grupos y env\xEDos de tu nodo (admin global, cualquiera).", ej: "envio_asignar_grupo tracking:'NFABC123' grupo:'Bonorino' \u2192 lo despacha por ese grupo (o solo le setea el grupo si ya estaba entregado).", no: "No mueve dinero (el clearing es config); no lo entrega ni lo asigna a un cadete puntual." },
226
+ cobro_corregir: { que: "Ajusta el monto a cobrar contra entrega de un env\xEDo (se cobr\xF3 de m\xE1s/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\xEDo." },
227
+ colecta_ver: { que: "Resumen de valores de colecta del nodo: pago default, cobros por cliente y pagos pactados por cliente+mensajero.", uso: "Sin par\xE1metros.", ej: "colecta_ver \u2192 cu\xE1nto se paga/cobra por colecta.", no: "No cambia tarifas (colecta_configurar)." },
228
+ colecta_historial: { que: "Paquetes colectados por d\xEDa y por cliente en una fecha o rango (hist\xF3rico, hasta 62 d\xEDas), con qui\xE9n los colect\xF3.", uso: "desde (aaaa-mm-dd), hasta opcional, cliente opcional (nombre o id). Solo tu nodo.", ej: "colecta_historial desde:2026-09-08 cliente:Libero \u2192 cu\xE1ntos paquetes se le levantaron ese d\xEDa.", no: "No es el panel de hoy (colecta_pendientes) ni cobra/paga colectas." },
229
+ colecta_pendientes: { que: "Panel de colectas del nodo: `items` = a retirar HOY (por cliente, con corte y mensajero asignado); `itemsManana` = clientes cuyos env\xEDos entraron despu\xE9s del corte \u2192 van a ma\xF1ana; `clientesSinEnvios` = vendedores sin env\xEDos cargados a los que igual se los puede mandar a colectar.", uso: "Sin par\xE1metros. Trae el colectaId para desasignar.", ej: "'\xBFQui\xE9n levanta a Distri Sur?' \u2192 colecta_pendientes y mir\xE1s items.", no: "No asigna (colecta_asignar) ni configura tarifas." },
230
+ colecta_configurar: { que: "Setea un valor de colecta seg\xFAn alcance.", uso: "alcance: 'nodo' (pago default por colecta), 'mensajero' (default de ese cadete), 'cliente' (cu\xE1nto 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 \u2192 gratis desde 5 env\xEDos, sino $2500.", no: "No mueve dinero: es config de tarifa (el cobro se aplica al liquidar)." },
231
+ colecta_fija: { que: "D\xEDas en que se va a buscar SIEMPRE a un vendedor, tenga o no env\xEDos cargados (colecta fija).", uso: "idCliente + dias (n\xFAmeros 0-6 o nombres). dias:[] la quita. Requiere permiso gestion.", ej: 'colecta_fija idCliente:"130" dias:[1,3,5] \u2192 lunes, mi\xE9rcoles y viernes.', no: "No mueve dinero (el cobro/pago de la colecta es colecta_configurar); no crea la colecta del d\xEDa, hace que aparezca en el panel." },
232
+ 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\xC9N para clientes SIN env\xEDos cargados: la colecta se crea vac\xEDa 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\xF3, la programa para el pr\xF3ximo d\xEDa h\xE1bil. Admin global: `nodo` para elegir/acotar el nodo.", ej: "colecta_asignar cliente:'Distri Sur' mensajero:'Maxi'.", no: "No mueve dinero; no cruza nodos." },
233
+ colecta_desasignar: { que: "Quita la asignaci\xF3n de una colecta (los env\xEDos 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\xEDos; no mueve dinero." },
234
+ zonas_reparto: { que: "Zonas de reparto del nodo con sus metazonas y qu\xE9 mensajeros tiene cada una.", uso: "Sin par\xE1metros.", ej: "zonas_reparto \u2192 ver qui\xE9n cubre Palermo.", no: "No asigna (asignar_mensajero_zona / asignar_nodo_zona)." },
235
+ 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." },
236
+ zona_barrios: { que: "Qu\xE9 localidades y BARRIOS componen una zona con nombre (ej. 'Matanza Norte', 'CABA'), con su tramo por perfil. Consulta r\xE1pida por nombre.", uso: "zona (nombre). Devuelve las zonas visibles (propias, globales o de tus grupos log\xEDsticos) que matchean.", ej: "zona_barrios zona:'Matanza Norte' \u2192 los barrios de esa zona.", no: "No edita la zona (eso es la config de metazonas/grupos); solo lectura." },
237
+ 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." },
238
+ asignar_nodo_zona: { que: "Asigna un NODO COMPLETO (de tu grupo log\xEDstico) a una metazona, cuando ese nodo cubre toda la localidad.", uso: "nodo (nombre, debe compartir grupo log\xEDstico) + metazona; nombre opcional. Admin global: `logisticaId` para elegir el nodo due\xF1o de la zona.", ej: "asignar_nodo_zona nodo:'RL' metazona:'Portela'.", no: "No suma nodos fuera de tu grupo log\xEDstico." },
239
+ envios_por_zona: { que: "Env\xEDos que OTROS nodos te rutearon por zona de reparto, agrupados por zona (para aceptar/rechazar).", uso: "Sin par\xE1metros. Cada env\xEDo trae su id (para procesar_zona/rechazar_zona).", ej: "envios_por_zona \u2192 ver qu\xE9 te mandaron por la zona Sur.", no: "No los acepta solo: us\xE1 procesar_zona o rechazar_zona." },
240
+ procesar_zona: { que: "ACEPTA (recibe en tu nodo) env\xEDos 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)." },
241
+ rechazar_zona: { que: "RECHAZA env\xEDos 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\xEDo." },
242
+ rendiciones: { que: "Estado de rendiciones: cobros/cambios/devoluciones a recuperar o rendir, con totales y qui\xE9n tiene cada uno.", uso: "Sin par\xE1metros. Scopeado a tu alcance (vendedor: lo tuyo; nodo: tu nodo; mensajero: lo suyo).", ej: "rendiciones \u2192 cu\xE1nto falta que rinda cada cadete.", no: "No confirma ni revierte (rendicion_revertir); NO incluye rutas de marketplace adjudicadas (ver ayuda tema:facturacion_marketplace)." },
243
+ rendicion_revertir: { que: "Revierte un cobro/cambio marcado como 'rendido' por error \u2192 vuelve a 'a rendir'.", uso: "envioId + tipo (cobro|cambio); motivo opcional. Deja registro.", ej: "rendicion_revertir envioId:987 tipo:cobro motivo:'se marc\xF3 sin recibir'.", no: "No mueve dinero en cuentas; corrige el estado de la rendici\xF3n." },
244
+ 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 \u2192 qu\xE9 cobros est\xE1n en disputa.", no: "No resuelve reclamos desde ac\xE1." },
245
+ rutas_publicas: { que: "Publicaciones ABIERTAS de toda la red que tu nodo puede tomar (con cu\xE1ntas ofertas tiene cada una). Marca las tuyas con esMia.", uso: "Sin par\xE1metros.", ej: "rutas_publicas \u2192 ver qu\xE9 rutas hay para ofertar.", no: "No oferta (ruta_ofertar)." },
246
+ mis_rutas: { que: "Tus publicaciones (con estado/adjudicaci\xF3n: nodoToma y precioAcordado) y las ofertas que hiciste a otros.", uso: "Sin par\xE1metros.", ej: "mis_rutas \u2192 ver si tu ruta se adjudic\xF3 y a qui\xE9n.", no: "El precioAcordado es la obligaci\xF3n registrada, NO un asiento de facturaci\xF3n (ver ayuda tema:facturacion_marketplace)." },
247
+ ruta_ofertas: { que: "Ofertas recibidas en una publicaci\xF3n TUYA, de la m\xE1s barata a la m\xE1s cara. Solo el que public\xF3.", uso: "publicacionId. Los ids de oferta sirven para adjudicar.", ej: "ruta_ofertas publicacionId:12 \u2192 eleg\xEDs la mejor y su ofertaId.", no: "No adjudica (ruta_adjudicar)." },
248
+ ruta_publicar: { que: "Public\xE1s una ruta/colecta/viaje para que cualquier nodo la tome (subasta abierta). VOS le pag\xE1s al que la toma.", uso: "titulo (obligatorio); tipo (ruta|colecta|viaje), descripcion, zona, precioMax (tope que ofrec\xE9s pagar) opcionales.", ej: "ruta_publicar titulo:'Reparto zona Oeste 40 paquetes' tipo:ruta precioMax:20000.", no: "No mueve dinero: la obligaci\xF3n reci\xE9n se registra al adjudicar; el pago es manual." },
249
+ ruta_ofertar: { que: "Ofert\xE1s por una publicaci\xF3n de OTRO nodo. precio = lo que cobr\xE1s por hacerla (m\xE1s 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\xE9s ofertar en tu propia publicaci\xF3n." },
250
+ ruta_adjudicar: { que: "Eleg\xEDs la oferta ganadora de una publicaci\xF3n TUYA y cerr\xE1s la subasta: el nodo ganador la toma a su precio y queda registrada la obligaci\xF3n.", uso: "publicacionId + ofertaId (de ruta_ofertas).", ej: "ruta_adjudicar publicacionId:12 ofertaId:34 \u2192 gana el nodo de esa oferta a $18000.", no: "NO crea asiento de facturaci\xF3n/rendici\xF3n/comisi\xF3n ni clearing autom\xE1tico: el pago entre nodos se salda MANUALMENTE (ver ayuda tema:facturacion_marketplace)." },
251
+ afiliacion_listar: { que: "Afiliaciones (afiliado\u2194entidad referida) con su comisi\xF3n por env\xEDo, 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)." },
252
+ comisiones_afiliado_ver: { que: "Comisiones devengadas por env\xEDo (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." },
253
+ 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\xF3n/comisi\xF3n (afiliacion_crear); no toca dinero." },
254
+ afiliacion_crear: { que: "Vincula un afiliado con una entidad referida (nodo o cliente) y su comisi\xF3n RECURRENTE por env\xEDo.", 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." },
255
+ afiliacion_editar: { que: "Edita una afiliaci\xF3n: valorComision, fechaExpiracion (renovar/extender) y/o activa.", uso: "id + los campos a cambiar.", ej: "afiliacion_editar id:5 activa:false \u2192 la das de baja.", no: "No toca dinero." },
256
+ 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\xEDo); no factura." },
257
+ kpi_nodo: { que: "Tablero del nodo: P&L, top clientes, ca\xEDdas.", uso: "desde/hasta opcionales; nodo solo para admin.", ej: "kpi_nodo desde:2026-08-01 \u2192 resultado del mes.", no: "No baja a un vendedor puntual; solo tu nodo (salvo admin)." },
258
+ kpi_red: { que: "KPIs globales de la red.", uso: "Sin par\xE1metros. Solo admin global.", ej: "kpi_red \u2192 panorama SaaS de toda la red.", no: "No desglosa por nodo (us\xE1 kpi_nodo con nodo)." },
259
+ metricas_por_tipo: { que: "M\xE9tricas por TIPO de env\xEDo (flex/tienda/manual): total, entregados, tasa y % entregado antes de las 21hs. Para admin desglosa por log\xEDstica.", uso: "desde/hasta YYYY-MM-DD (default 30 d\xEDas); nodo solo admin.", ej: "metricas_por_tipo \u2192 ver si los Flex se entregan a tiempo.", no: "No es por cliente." },
260
+ top_productos: { que: "Ranking de productos m\xE1s despachados del nodo (WMS).", uso: "desde/hasta y cliente opcionales.", ej: "top_productos cliente:'Distri Sur'.", no: "Requiere WMS activo en el nodo." },
261
+ stock_nodo: { que: "Stock del dep\xF3sito del nodo (WMS).", uso: "cliente opcional para filtrar.", ej: "stock_nodo cliente:'Distri Sur'.", no: "Requiere WMS activo; no repone." },
262
+ productos_nodo: { que: "Cat\xE1logo del dep\xF3sito del nodo (WMS).", uso: "cliente opcional.", ej: "productos_nodo.", no: "Requiere WMS activo." },
263
+ producto_crear: { que: "Alta de producto en el dep\xF3sito (WMS).", uso: "nombre + sku/codigoBarra/peso/volumen opcionales; staff pasa idCliente due\xF1o. 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." },
264
+ clientes_del_nodo: { que: "Clientes/vendedores del nodo con su lista de precio.", uso: "Sin par\xE1metros. Scopeado a tu nodo.", ej: "clientes_del_nodo \u2192 ver a qui\xE9n factur\xE1s y con qu\xE9 lista.", no: "No los crea/edita (cliente_crear/editar)." },
265
+ 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." },
266
+ cliente_editar: { que: "Edita un cliente de tu nodo cambiando SOLO los campos que pas\xE1s (lista de precio, tel\xE9fono, email con el que entra, DNI, direcci\xF3n, nombre de pago, activo).", uso: 'id + los campos a cambiar. Lo que no pas\xE1s queda igual; "" vac\xEDa 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' \xB7 cliente_editar id:50 email:'nuevo@mail.com'.", no: "No toca dinero ni la clave (cliente_resetear_clave)." },
267
+ sucursales_cliente: { que: "Lista las sucursales (puntos de retiro) de un cliente de tu nodo con su id, nombre, direcci\xF3n y perfil de zona.", uso: "cliente (id, de clientes_del_nodo). Requiere permiso 'gestion'.", ej: "sucursales_cliente cliente:130 \u2192 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." },
268
+ sucursal_perfil: { que: "Setea (o limpia) el perfil de zona de UNA sucursal: la liquidaci\xF3n cotiza los env\xEDos que salen de ah\xED seg\xFAn SU distancia, no la del cliente.", uso: "sucursalId (de sucursales_cliente) + perfilZona (vac\xEDo = 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." },
269
+ precios_ver: { que: "Listas de precios por zona de tu nodo.", uso: "Sin par\xE1metros. Requiere permiso 'precios'.", ej: "precios_ver \u2192 ver tarifas por zona.", no: "No las edita (precio_actualizar)." },
270
+ precio_actualizar: { que: "Cambia precios de una lista creando una VERSI\xD3N nueva (hist\xF3rico 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)." },
271
+ cuentas_vinculadas: { que: "Cu\xE1ntas cuentas de tienda hay vinculadas, por proveedor (ML / TiendaNube / TiendaNegocio).", uso: "nodo opcional (solo admin, baja al desglose por cliente).", ej: "cuentas_vinculadas \u2192 cu\xE1ntos vendedores tienen ML conectado.", no: "No vincula (generar_enlace_vinculacion)." },
272
+ generar_enlace_vinculacion: { que: "Genera el enlace para vincular una tienda; se lo mand\xE1s 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\xF3n (la autoriza el due\xF1o de la tienda); no toca dinero." },
273
+ marketing_config_ver: { que: "Muestra los p\xEDxeles/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 \u2192 ver qu\xE9 Meta Pixel / GA4 ten\xE9s configurado.", no: "No devuelve los tokens/secrets en claro (solo si est\xE1n cargados); no los edita (marketing_config_guardar)." },
274
+ marketing_config_guardar: { que: "Guarda los p\xEDxeles/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\xEDo BORRA ese campo; omitido = queda igual.", ej: "marketing_config_guardar metaPixelId:'123456789' ga4MeasurementId:'G-ABC123'.", no: "No mueve dinero; no verifica que el p\xEDxel exista en Meta/GA4." },
275
+ 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 \u2192 sus liquidaciones de $50.000+.", no: "No edita ni paga; el detalle de env\xEDos es liquidacion_detalle." },
276
+ liquidacion_detalle: { que: "Snapshot completo de una liquidaci\xF3n: detalle de los env\xEDos, resumen y comisiones.", uso: "id de la liquidaci\xF3n (de liquidacion_buscar). Scopeada: no abre la de otro nodo.", ej: "liquidacion_detalle id:1234 \u2192 ver los env\xEDos que la componen.", no: "No la modifica; no factura." },
277
+ liquidaciones_sin_recibir: { que: "Liquidaciones de clientes con saldo deudor (aprox. por cliente: el pago todav\xEDa no entr\xF3).", uso: "Opcional cliente y nodo (nodo solo admin global). Requiere permiso finanzas.", ej: "liquidaciones_sin_recibir \u2192 ver qui\xE9n debe pagar.", no: "Es aproximado a nivel cliente (los pagos son FIFO por cuenta, no por liquidaci\xF3n); no reclama ni cobra." },
278
+ chofer_listar: { que: "Usuarios de tu nodo, incluidos los choferes (rol mensajero): id, nombre, tel\xE9fono, activo.", uso: "Sin par\xE1metros. Requiere permiso 'usuarios'.", ej: "chofer_listar \u2192 ver tus cadetes.", no: "No crea/edita (chofer_crear/editar)." },
279
+ 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." },
280
+ chofer_editar: { que: "Edita un chofer de tu nodo: nombre, apellido, tel\xE9fono, mensajeroNombre y/o activo.", uso: "id + los campos a cambiar.", ej: "chofer_editar id:7 activo:false \u2192 lo desactiv\xE1s.", no: "No toca credenciales/clave." },
281
+ usuario_editar: { que: "Corrige nombre, apellido, tel\xE9fono y/o email de un usuario de tu nodo.", uso: "id + los campos a cambiar. Si el usuario todav\xEDa no tiene nombre y apellido separados, pas\xE1 los dos. Requiere permiso 'usuarios'.", ej: "usuario_editar id:7 nombre:'Sol' apellido:'Mart\xEDnez'.", no: "No toca rol, permisos ni clave. No cambia el nombre con el que se le paga al cadete." },
282
+ 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\xF3mez' 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." },
283
+ 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\xE9rez'.", no: "No da de alta el PERFIL del cliente (eso es cliente_crear); Claude nunca inventa la clave; no toca dinero." },
284
+ cliente_resetear_clave: { que: "Genera una CLAVE TEMPORAL NUEVA para un cliente/vendedor de tu nodo que YA tiene usuario pero perdi\xF3 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' \u2192 nueva clave temporal.", no: "Si el cliente todav\xEDa no tiene usuario, avisa y sugiere cliente_generar_usuario; no crea uno nuevo; no toca dinero." },
285
+ usuario_habilitar_reparto: { que: "Marca a un operador/admin de tu nodo como TAMBI\xC9N mensajero (puede escanear, autoasignarse y entregar).", uso: "id + activo (default true).", ej: "usuario_habilitar_reparto id:4 \u2192 ese comisionista ya puede repartir.", no: "No toca credenciales." },
286
+ nodos_listar: { que: "Todas las log\xEDsticas (nodos) de la red.", uso: "Sin par\xE1metros. Solo admin global.", ej: "nodos_listar \u2192 id/nombre de cada nodo.", no: "No los crea (nodo_crear)." },
287
+ nodo_crear: { que: "Alta de una log\xEDstica/nodo.", uso: "nombre + telefono opcional. Solo admin global.", ej: "nodo_crear nombre:'Nodo Oeste' telefono:'11...'.", no: "No toca dinero." },
288
+ 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\xEDa no est\xE1 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." },
289
+ provisorio_conciliar: { que: "Fusiona un nodo PROVISORIO con el nodo REAL cuando este ya est\xE1 dado de alta: reatribuye sus env\xEDos y su membres\xEDa de grupo al nodo real y marca el provisorio como resuelto.", uso: "provisorioId (de nodos_listar) + nodoReal (nombre o id). Como operador solo concili\xE1s los provisorios que cre\xF3 tu nodo y contra un nodo que YA opera en el grupo; el admin global puede adem\xE1s pasar `tarifa` (bandas) si el nodo real todav\xEDa no est\xE1 en el grupo.", ej: "provisorio_conciliar provisorioId:42 nodoReal:'Nodo Avellaneda'.", no: "No mueve dinero (reordena el clearing: esos env\xEDos pasan a la tarifa del nodo real)." },
290
+ 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 \u2192 lo hac\xE9s admin de su nodo.", no: "No crea el usuario; no toca credenciales ni dinero." },
291
+ grupo_crear: { que: "Crea un grupo log\xEDstico nuevo (para el clearing entre nodos).", uso: "nombre. Solo admin global. Los nodos se agregan/tarifan despu\xE9s.", ej: "grupo_crear nombre:'Grupo Sur'.", no: "No agrega ni tarifa nodos; no toca dinero." },
292
+ grupo_miembros: { que: "Lista los nodos de un grupo, marcando qui\xE9n 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." },
293
+ 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." },
294
+ grupo_expulsar_nodo: { que: "Saca un nodo del grupo.", uso: "grupo + nodo (ids). Solo un ADMIN del grupo. No se puede expulsar al \xFAltimo admin.", ej: "grupo_expulsar_nodo grupo:21 nodo:5.", no: "No deja el grupo sin admin; no toca dinero." },
295
+ grupo_admin_permiso: { que: "Da o saca el permiso de admin de grupo a un nodo (para que otros hereden la administraci\xF3n).", uso: "grupo + nodo (ids) + admin (true=dar, false=sacar). Solo un ADMIN del grupo. No se puede sacar al \xFAltimo admin.", ej: "grupo_admin_permiso grupo:21 nodo:5 admin:true.", no: "No toca dinero." },
296
+ 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." },
297
+ 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." }
298
+ };
299
+ function renderAyuda(q, ctx = {}) {
300
+ const version = ctx.version ?? "";
301
+ const disponibles = ctx.disponibles instanceof Set ? ctx.disponibles : null;
302
+ const clave = String(q ?? "").trim().toLowerCase().replace(/[^a-z_]/g, "");
303
+ if (!clave) return indice(version, disponibles);
304
+ if (TOPICS[clave]) return TOPICS[clave] + `
305
+
306
+ (Consult\xE1 una herramienta con: ayuda tool:<nombre>)`;
307
+ const d = TOOL_DOCS[clave];
308
+ if (d) {
309
+ const tiene = !disponibles || disponibles.has(clave);
310
+ const nota = tiene ? "" : "\n\n(\u26A0\uFE0F Con tu rol/permiso actual esta herramienta no est\xE1 disponible.)";
311
+ return [
312
+ `\u{1F527} ${clave}`,
313
+ "",
314
+ `Qu\xE9 hace: ${d.que}`,
315
+ `C\xF3mo usar: ${d.uso}`,
316
+ `Ejemplo: ${d.ej}`,
317
+ `Qu\xE9 NO hace: ${d.no}`
318
+ ].join("\n") + nota;
319
+ }
320
+ const cerca = Object.keys(TOOL_DOCS).filter((n) => n.includes(clave)).slice(0, 8);
321
+ const temas = Object.keys(TOPICS);
322
+ return [
323
+ `No encontr\xE9 una herramienta o tema llamado "${clave}".`,
324
+ cerca.length ? `\xBFQuisiste decir?: ${cerca.join(", ")}` : "",
325
+ `Temas disponibles: ${temas.join(", ")}`,
326
+ `Sin argumentos, ayuda te da el \xEDndice completo.`
327
+ ].filter(Boolean).join("\n");
328
+ }
329
+ function indice(version, disponibles) {
330
+ const out = [
331
+ `AYUDA DEL MCP NEXUS FLEX${version ? ` (v${version})` : ""}`,
332
+ "",
333
+ "Ped\xED el detalle de una herramienta con: ayuda tool:<nombre>",
334
+ "Ped\xED un tema transversal con: ayuda tema:<clave>",
335
+ `Temas: ${Object.keys(TOPICS).join(", ")}`,
336
+ "",
337
+ "Herramientas" + (disponibles ? " disponibles para vos" : "") + ":"
338
+ ];
339
+ for (const g of GROUPS) {
340
+ const items = g.tools.filter((t) => TOOL_DOCS[t] && (!disponibles || disponibles.has(t)));
341
+ if (!items.length) continue;
342
+ out.push("", `\u25B8 ${g.titulo}`);
343
+ for (const t of items) out.push(` \u2022 ${t} \u2014 ${TOOL_DOCS[t].que}`);
344
+ }
345
+ if (disponibles) {
346
+ const enGrupos = new Set(GROUPS.flatMap((g) => g.tools));
347
+ const sueltas = [...disponibles].filter((t) => TOOL_DOCS[t] && !enGrupos.has(t));
348
+ if (sueltas.length) {
349
+ out.push("", "\u25B8 Otras");
350
+ for (const t of sueltas) out.push(` \u2022 ${t} \u2014 ${TOOL_DOCS[t].que}`);
351
+ }
352
+ }
353
+ return out.join("\n");
354
+ }
355
+ const CONTROL_POR_FOTO = [
356
+ "FLUJO \u2014 CONTROL POR FOTO Y CARGA DE PLANILLA V\xCDA MCP",
357
+ "Qu\xE9 es: cargar paquetes que YA vienen con etiqueta de Mercado Libre pero NO est\xE1n vinculados",
358
+ "a una cuenta ML (un cliente/nodo te los baj\xF3 etiquetados y te dej\xF3 las fotos en una CARPETA).",
359
+ "Sirve para registrar r\xE1pido el movimiento y el CLEARING entre nodos, sin escanear.",
360
+ "",
361
+ "PREPARACI\xD3N (una vez): defin\xED qui\xE9n reparte cada zona con `asignar_nodo_zona` (nodo",
362
+ " responsable) o `asignar_mensajero_zona`. As\xED el auto-ruteo sabe a qui\xE9n mandar cada paquete.",
363
+ "",
364
+ "POR CADA FOTO de la carpeta \u2192 `envio_desde_etiqueta_ml`:",
365
+ " \u2022 cliente (o mlSenderId): de qui\xE9n es el paquete (detecta al vendedor de ML);",
366
+ " \u2022 mlShipmentId + mlQr crudos + `reusarEtiqueta: true` \u2192 REUSA el tracking/QR/etiqueta ya",
367
+ " impreso (no genera uno nuevo), 'ml_manual', IDEMPOTENTE (recargar no duplica);",
368
+ " \u2022 destino (destinatario, direcci\xF3n, localidad, CP);",
369
+ " \u2022 D\xCDGITOS/LETRAS ILEGIBLES: carg\xE1 la parte legible + un '*' por cada car\xE1cter que NO se lee",
370
+ " (ej. 'Aguirre 31**') \u2014 NO inventes \u2014 y pas\xE1 `fotoRuta` = la RUTA LOCAL de la foto en la",
371
+ " compu (ej. 'C:\\\\Users\\\\...\\\\Desktop\\\\etiquetas\\\\ml_123.jpg') para reencontrarla;",
372
+ " \u2022 CIERRE DEL CICLO en un paso con `avanzarA`: 'colectado' / 'procesado' / 'entregado'.",
373
+ " 'entregado' recorre TODOS los estados de una: A retirar \u2192 Colectado \u2192 Procesado (En centro)",
374
+ " \u2192 [si pas\xE1s `grupo`, ej. 'Bonorino': En camino, con su integrante] \u2192 Entregado. Cada estado",
375
+ " queda registrado con QUI\xC9N lo hizo (vos, el usuario MCP) \u2014 es la v\xEDa r\xE1pida del clearing;",
376
+ " \u2022 `autoRutear: true`: adem\xE1s, manda el paquete al nodo/mensajero responsable de su zona.",
377
+ " \u2022 `nodoEntrega`: el nodo que RECIBI\xD3 y entreg\xF3 el paquete (ej. 'FastCorreo'). Es lo que hace",
378
+ " que el env\xEDo entre al CIERRE SEMANAL entre nodos (origen = nodo del cliente \u2192 entrega =",
379
+ " este nodo). Sin `grupo` se deduce: cliente del mismo nodo = paquete propio (sin traspaso);",
380
+ " de otro nodo = el grupo que comparten. Decilo siempre que sepas qui\xE9n lo entreg\xF3;",
381
+ "",
382
+ "TANDA DE UN CADETE (lo m\xE1s com\xFAn: una carpeta por d\xEDa y por cadete, con paquetes propios y",
383
+ " de otros nodos mezclados): por cada foto \u2192 mlQr crudo (+ destino le\xEDdo de la etiqueta),",
384
+ " reusarEtiqueta:true, fecha:<el d\xEDa>, avanzarA:'entregado', mensajero:<el cadete>,",
385
+ " nodoEntrega:<nuestro nodo>. El vendedor y su nodo salen del QR; si ya estaba cargado no se",
386
+ " duplica (se completa y se avanza). Si contesta que no conoce la cuenta de ML, PREGUNT\xC1 de",
387
+ " qu\xE9 nodo/cliente es y repet\xED con `cliente` \u2014 no adivines. Al final: conteo propios vs. de",
388
+ " cada nodo, los que no se reconocieron y las advertencias.",
389
+ " \u2022 PLANILLA ATRASADA: `fecha` = el d\xEDa real (dd/mm/aaaa). Ej.: 5 env\xEDos de un cliente de",
390
+ " Env\xEDos Frank que entreg\xF3 FastCorreo el lunes 14 \u2192 cliente, fecha:'14/09/2026',",
391
+ " avanzarA:'entregado', grupo:'Portela', nodoEntrega:'FastCorreo'. Si esa semana ya",
392
+ " cerr\xF3, entra en el cierre siguiente marcado como tarde, con su fecha real.",
393
+ "",
394
+ "AL TERMINAR: devolv\xE9 el CONTEO total + desglose por cliente/zona/estado + qu\xE9 qued\xF3 sin",
395
+ " responsable de zona (avis\xE1 para configurarla). Cuando el nodo real se d\xE9 de alta puede",
396
+ " ABSORBER este historial (conciliar el provisorio) o descartarlo \u2014 no bloquea nada."
397
+ ].join("\n");
398
+ const FLUJOS = {
399
+ alta_cliente: [
400
+ "FLUJO \u2014 ALTA DE CLIENTE (vendedor) COMPLETA",
401
+ "Gui\xE1 paso a paso; NO lo des por terminado hasta el paso 4.",
402
+ "",
403
+ "1) Datos b\xE1sicos: nombre (obligatorio), tel\xE9fono/WhatsApp, direcci\xF3n, DNI/CUIT si tiene.",
404
+ " \u2192 `cliente_crear`.",
405
+ "2) LISTA DE PRECIOS (pregunt\xE1 SIEMPRE \xAB\xBFqu\xE9 le cobr\xE1s a este cliente?\xBB):",
406
+ " \u2022 Oficial de Flex (la que usan todos) o un PRECIO PROPIO (mir\xE1 `precios_ver`, el nombre",
407
+ " est\xE1 en 'referencia'; si es nueva, se crea con nombre para reusarla).",
408
+ " \u2192 asign\xE1s con `cliente_editar idLista:<id>`.",
409
+ "3) PERFIL DE ZONA: pregunt\xE1 \xAB\xBFusa la zonificaci\xF3n COM\xDAN o necesita un perfil aparte?\xBB.",
410
+ " Ej.: cliente de CABA que usa las zonas de todos \u2192 perfil GENERAL; si opera distinto",
411
+ " (otras metazonas / precios por zona) asignale un perfil de zona propio.",
412
+ "4) COLECTA (\xBFle retir\xE1s la mercader\xEDa?): si s\xED, ped\xED la DIRECCI\xD3N de retiro con",
413
+ " timbre/piso/referencia y que quede GEOPOSICIONADA; defin\xED si la colecta tiene cargo.",
414
+ "5) \xBFVende por Mercado Libre / Tienda? Ofrec\xE9 vincular con `generar_enlace_vinculacion`.",
415
+ "6) CONFIRM\xC1 (`clientes_del_nodo`): debe estar con su LISTA. Si le falta lista, zona o",
416
+ " colecta seg\xFAn lo que dijo, el alta est\xE1 INCOMPLETA \u2192 volv\xE9 al paso que falte."
417
+ ].join("\n"),
418
+ control_por_foto: CONTROL_POR_FOTO,
419
+ alta_planilla_ml: CONTROL_POR_FOTO,
420
+ // alias histórico
421
+ facturacion: [
422
+ "FLUJO \u2014 PONER A FACTURAR (ARCA) DE CERO",
423
+ "",
424
+ "IDEA CENTRAL: quien COBRA es quien FACTURA. Cada cuenta de dinero puede tener su",
425
+ "propio emisor; lo que un cliente transfiere a esa cuenta se factura con el CUIT del",
426
+ "due\xF1o de esa cuenta. Las cuentas sin emisor propio facturan con el CUIT de la empresa.",
427
+ "",
428
+ "\u26A0\uFE0F LO QUE YO NO PUEDO HACER: el tr\xE1mite en ARCA (certificado, relaci\xF3n con el web",
429
+ " service, punto de venta) necesita la Clave Fiscal de esa persona y un navegador.",
430
+ " Yo no entro a ARCA ni manejo claves fiscales. Tampoco emito comprobantes: eso se",
431
+ " hace desde la PWA o desde el portal del cliente. S\xED dejo TODO lo de Nexus Flex",
432
+ " configurado y te digo exactamente qu\xE9 falta.",
433
+ "",
434
+ "PASO 0 \u2014 \xBFD\xF3nde est\xE1s parado? \u2192 `facturacion_estado`.",
435
+ " Te dice qu\xE9 emisores hay, qu\xE9 cuentas no tienen emisor y qu\xE9 le falta a cada",
436
+ " cliente. Arranc\xE1 SIEMPRE por ac\xE1 y volv\xE9 al final para confirmar.",
437
+ "",
438
+ "PASO 1 \u2014 EL EMISOR (una vez por persona/CUIT que cobre). Lo hace ESA persona:",
439
+ " a) Registro \xDAnico Tributario \u2192 Puntos de venta \u2192 dar de alta uno de tipo",
440
+ " \xABFactura Electr\xF3nica - Monotributo - Web Services\xBB (monotributo) o",
441
+ " \xABRECE para aplicativo y web services\xBB (responsable inscripto).",
442
+ " \u26A0\uFE0F El de \xABFactura en L\xEDnea\xBB NO sirve. Anot\xE1 el n\xFAmero.",
443
+ " b) Administraci\xF3n de Certificados Digitales \u2192 agregar el servicio \u2192 subir un CSR",
444
+ " y descargar el certificado.",
445
+ " c) Administrador de Relaciones \u2192 Nueva Relaci\xF3n \u2192 ARCA \u2192 WebServices \u2192",
446
+ " \xABFacturaci\xF3n Electr\xF3nica\xBB \u2192 representante = ese certificado.",
447
+ " d) En la PWA, Configuraci\xF3n \u2192 \u{1F9FE} Facturaci\xF3n ARCA \u2192 \xAB+ Emisor para una cuenta\xBB:",
448
+ " eleg\xEDs la cuenta de dinero, carg\xE1s CUIT, raz\xF3n social (el TITULAR del CUIT),",
449
+ " condici\xF3n IVA, punto de venta, certificado y clave, y \xABProbar conexi\xF3n\xBB.",
450
+ " Empez\xE1 en HOMOLOGACI\xD3N; pas\xE1 a producci\xF3n cuando la prueba d\xE9 OK.",
451
+ "",
452
+ "PASO 2 \u2014 LOS CLIENTES a los que le va a facturar \u2192 `facturacion_preparar_cliente`.",
453
+ " Necesit\xE1s de cada uno: CUIT, raz\xF3n social (como figura en ARCA, NO el nombre de",
454
+ " fantas\xEDa), condici\xF3n frente al IVA, en qu\xE9 cuenta cobra, y si factura semanal o",
455
+ " mensual. Pregunt\xE1 TODO eso antes de ejecutar; no inventes ninguno.",
456
+ " El corte queda en HOY: lo anterior no se factura desde el sistema (ya se factur\xF3",
457
+ " por fuera y emitirlo de nuevo ser\xEDa un duplicado).",
458
+ "",
459
+ "PASO 3 \u2014 CONFIRMAR \u2192 `facturacion_estado` de nuevo. Un cliente est\xE1 listo cuando",
460
+ " tiene CUIT, raz\xF3n social, condici\xF3n IVA, cuenta de cobro con emisor y est\xE1",
461
+ " habilitado. Si falta algo, el estado te dice cu\xE1l.",
462
+ "",
463
+ "C\xD3MO SE FACTURA DESPU\xC9S (no por ac\xE1):",
464
+ " \u2022 El cliente entra a su portal (Mi Cuenta \u2192 \u{1F9FE} Tus facturas), revisa el per\xEDodo",
465
+ " cerrado y toca \xABEmitir factura\xBB. Solo aparece si: el per\xEDodo cerr\xF3, no tiene",
466
+ " reclamos abiertos, no est\xE1 facturado y NO tiene saldo pendiente.",
467
+ " \u2022 Si el importe no coincide con lo acordado, el cliente RECLAMA la liquidaci\xF3n y",
468
+ " el staff la corrige; el bot\xF3n usa siempre lo liquidado, nunca un n\xFAmero a mano.",
469
+ " \u2022 El staff puede emitir desde la PWA (Liquidaciones Anteriores \u2192 Facturar)."
470
+ ].join("\n"),
471
+ alta_operador: [
472
+ "FLUJO \u2014 ALTA DE UN OPERADOR DE NODO (staff que administra)",
473
+ "1) Datos: email (con el que loguea), nombre y apellido (obligatorios), tel\xE9fono.",
474
+ "2) Permisos: qu\xE9 secciones va a manejar (gesti\xF3n, finanzas, mensajeros, precios, wms,",
475
+ " reportes, datos). Si es el due\xF1o del nodo, despu\xE9s habilitalo como admin de nodo.",
476
+ " \u2192 `operador_crear` (te devuelve una CLAVE TEMPORAL para pasarle; la cambia al entrar).",
477
+ "3) \xBFEs due\xF1o/mano derecha? `usuario_habilitar_rol` para admin de nodo (solo admin global).",
478
+ "4) Pasale la clave temporal por un canal seguro. Nunca la inventes vos."
479
+ ].join("\n"),
480
+ alta_chofer: [
481
+ "FLUJO \u2014 ALTA DE UN CHOFER (mensajero/cadete)",
482
+ "1) Datos: nombre y apellido (obligatorios), tel\xE9fono, email. \u2192 `chofer_crear` (devuelve CLAVE TEMPORAL).",
483
+ "2) Si adem\xE1s cobra su ganancia contra su cuenta, pas\xE1 `mensajeroNombre` (el nombre con",
484
+ " el que figura en el Maestro) para el macheo.",
485
+ "3) Zona: asignale su metazona con `asignar_mensajero_zona` para que le lleguen env\xEDos.",
486
+ "4) Pasale la clave temporal; la cambia al primer ingreso."
487
+ ].join("\n")
488
+ };
489
+ function renderFlujo(q) {
490
+ const key = (q ?? "").toLowerCase().trim().replace(/[\s-]+/g, "_");
491
+ if (key && FLUJOS[key]) return FLUJOS[key];
492
+ const found = key ? Object.keys(FLUJOS).find((k) => k.includes(key) || key.includes(k)) : null;
493
+ if (found) return FLUJOS[found];
494
+ return [
495
+ "FLUJOS GUIADOS disponibles (paso a paso para no dejar nada a medias):",
496
+ ...Object.keys(FLUJOS).map((k) => ` \u2022 ${k}`),
497
+ "",
498
+ "Ped\xED uno, ej.: flujo alta_cliente"
499
+ ].join("\n");
500
+ }
501
+ export {
502
+ FLUJOS,
503
+ GROUPS,
504
+ GUIAS,
505
+ TOOL_DOCS,
506
+ TOPICS,
507
+ guiaOnboarding,
508
+ renderAyuda,
509
+ renderFlujo
510
+ };